关于 swiftui-development
面向 SwiftUI 应用开发者的实践指南,涵盖架构、状态管理、并发、布局、动画与测试。强调 Swift 6 安全并发、现代 SwiftUI API 及可维护的视图设计。
主要功能
- 01MVVM 与 Clean Architecture 指导
- 02状态管理与依赖注入建议
- 03Swift 6 并发与数据流规范
- 04布局、动画及性能实践
- 05无障碍、预览与测试指导
适用场景
- 01规划 SwiftUI 应用架构
- 02实现并发安全的视图模型
- 03优化列表与自适应布局
- 04为界面补充预览和可访问性
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ mindrally/ skills --skill swiftui-development安装后在 Claude Code 中输入/swiftui-development确认已加载。
详细介绍
SwiftUI 开发
本技能涵盖构建清晰、高性能且易于维护的 SwiftUI 应用的最佳实践,包括架构、状态管理、Swift 6 并发、布局、动画和测试。
核心原则
- 编写正确、最新、无错误、功能完整、安全且高性能的代码
- 优先考虑可读性,但不能以牺牲正确性或安全性为代价
- 完整实现所有请求的功能——不留 TODO、占位内容或缺失部分
- 以 Swift 6.0+ 为目标,并启用严格并发检查;将并发警告视为错误
- 尽可能以 iOS 17+ 为目标,以使用现代 API(
@Observable、NavigationStack、#Preview);如果最低部署目标要求使用较旧模式,请注明
架构
- 使用 MVVM(Model-View-ViewModel)或 Clean Architecture;对于复杂的多屏导航流程,可引入 Coordinator 层
- 对视图和视图模型应用 SOLID 原则:
- 单一职责——每个视图或视图模型都只有一个变更原因
- 开闭原则——通过组合和协议扩展行为,而不是修改现有类型
- 里氏替换原则——协议遵循必须可替换,且不会让调用方感到意外
- 接口隔离原则——保持协议精简且目标明确,而不是创建一个包办一切的大型接口
- 依赖倒置原则——依赖协议抽象,而不是具体类型,并通过
init注入依赖
- 采用面向协议编程;数据模型优先使用结构体而非类
- 使用扩展整理代码并分离关注点
SwiftUI 视图结构
- 保持视图小巧,并专注于单一职责
- 将 50 行视为
body属性的实用上限——超出后,将子视图提取为小型可复用结构体或私有扩展函数 - 使用
@ViewBuilder构建自定义容器视图和复杂的条件视图逻辑 - 采用恰当的视图组合模式
状态管理
@State——局部值类型视图状态(Bool、Int、String、小型结构体)@Binding——与子视图进行双向数据绑定@StateObject——仅在创建并拥有对象生命周期的视图中使用@ObservedObject——用于会响应对象变化但不拥有该对象的子视图@EnvironmentObject/@Environment——谨慎用于广泛共享的依赖项或系统值;如果依赖项属于特定视图层级,而非整个应用,则优先通过init显式注入依赖@Published——用于ObservableObject类上的可观察属性(iOS 17 之前,或其他必须使用ObservableObject的情况)@Observable宏(iOS 17+)——新视图模型优先使用它,而非ObservableObject/@Published;它无需为每个属性都添加@Published,并且只会在视图实际读取的属性发生变化时触发视图更新,从而减少不必要的重绘
命名约定
- 变量、函数和方法使用 camelCase;类型(类、结构体、枚举、协议)使用 PascalCase
- 使用描述性强的完整名称——优先使用
fetchUserData,而非getData - 布尔变量使用
is、has、should等前缀 - 函数名称使用动词短语
SwiftUI 最佳实践
- 系统图标使用 SF Symbols;使用资源目录中的语义颜色,以自动支持深色模式
- 支持动态字体,并妥善处理键盘避让
- 使用
NavigationStack(iOS 16+),而非已弃用的NavigationView - 在适用时优先使用可推断类型的简写点语法(例如
.background(.blue)) - 为背景透明的
HStack/VStack行添加.contentShape(Rectangle()),让整行都能响应点击,而不只是其中不透明的内容
布局与样式
- 使用原生 SwiftUI 布局容器(VStack、HStack、ZStack、Grid);对于动态或较长的列表,使用
LazyVStack/LazyHStack - 谨慎使用
GeometryReader——它会扩展以填满所有可用空间,并可能影响布局性能;优先使用作用域限定在单个视图上的.background(GeometryReader { ... }),或使用Layout协议实现自定义布局 - 为列表项提供稳定且有意义的
id(来自Identifiable),而不是使用\.self - 针对不同屏幕尺寸实现自适应布局
- 使用
ViewModifier实现可复用样式;创建自定义ButtonStyle、TextFieldStyle等
动画与转场
- 优先使用限定到特定状态值的
.animation(_:value:),而不是宽泛的withAnimation调用,尤其是在body中 - 仅在明确由用户交互触发动画时使用
withAnimation - 使用
AnyTransition实现自定义转场;使用matchedGeometryEffect实现 Hero 动画 - 对于高频、由时间驱动的视觉更新,使用
TimelineView,而不是Timer+ 已发布属性
Swift 6 并发与数据流
- 为视图模型标注
@MainActor;所有 UI 更新都必须在主 Actor 上执行 - 启动异步任务时,优先使用
.task(id:),而不是.onAppear——视图消失或 id 变化时,它会自动取消任务,避免遗留任务 - 使用
async/await执行异步操作,并使用Result(或类型化 throws)处理错误 - 对共享可变状态或由多个任务访问的服务,优先使用
actor类型 - 对不涉及主 Actor 的纯逻辑函数标记
nonisolated,避免不必要的切换 - 切勿阻塞主线程——将繁重计算移至分离的
Task - 明确处理加载、错误和成功状态,不要留下隐式或未定义状态
内存管理与安全性
- 对生命周期超出当前作用域的闭包,默认使用
[weak self];在异步闭包开头使用guard let self else { return } - 仅当能够确定闭包的生命周期短于
self时,才使用[unowned self] - 安全处理可选值——不要强制解包;使用
guard提前返回 - 对于远程图片,使用
AsyncImage(搭配缓存层,或在生产应用中使用 Nuke/Kingfisher 等库),并立即应用.resizable()
性能优化
- 尽量减少视图 body 的重新计算;在有助于 SwiftUI 跳过冗余差异比对时,采用
Equatable一致性 - 使用
Identifiable项和稳定的 id 实现恰当的列表差异比对 - 优化前先使用 Instruments 进行性能分析;缓存开销较大的计算,而不是在
body中反复计算
无障碍功能
- 适当添加无障碍标签、提示和特征;支持 VoiceOver
- 为交互元素分配不同的
accessibilityIdentifier字符串,以便 UI 测试可靠地定位它们 - 启用无障碍功能(Dynamic Type、VoiceOver、Reduce Motion)进行测试
测试与预览
- 始终使用
#Preview(Xcode 15+)或在较旧的工具链中使用PreviewProvider提供预览,并注入真实的模拟数据 - 在多种配色方案和设备尺寸下预览
- 按 Given-When-Then 结构编写单元测试;为外部依赖生成基于协议的模拟对象,以便在不调用真实服务的情况下测试视图模型
代码质量
- 编写自说明代码;仅为不明显的逻辑添加注释
- 遵循 Swift API 设计指南
- 使用
MARK: - Section Name组织较长文件,并将私有辅助方法放在private extension中
常见模式
使用 @Observable ViewModel 的视图(iOS 17+)
@Observable
@MainActor
final class ContentViewModel {
var items: [Item] = []
var isLoading = false
func loadItems() async {
isLoading = true
defer { isLoading = false }
// Load items
}
}
struct ContentView: View {
@State private var viewModel = ContentViewModel()
var body: some View {
List(viewModel.items) { item in
Text(item.name)
}
.task(id: viewModel.items.count) {
await viewModel.loadItems()
}
}
}
使用 ObservableObject ViewModel 的视图(iOS 17 之前 / 旧版)
struct ContentView: View {
@StateObject private var viewModel = ContentViewModel()
var body: some View {
// View implementation
}
}
@MainActor
class ContentViewModel: ObservableObject {
@Published var items: [Item] = []
@Published var isLoading = false
func loadItems() async {
isLoading = true
// Load items
isLoading = false
}
}
可复用视图修饰符
struct CardModifier: ViewModifier {
func body(content: Content) -> some View {
content
.padding()
.background(Color(.systemBackground))
.cornerRadius(12)
.shadow(radius: 4)
}
}
extension View {
func cardStyle() -> some View {
modifier(CardModifier())
}
}
评论
支持 Markdown