mcpField
S
swiftui-development

swiftui-development

github.com/mindrally开发287680 次安装3 周前更新
代码仓库

关于 swiftui-development

面向 SwiftUI 应用开发者的实践指南,涵盖架构、状态管理、并发、布局、动画与测试。强调 Swift 6 安全并发、现代 SwiftUI API 及可维护的视图设计。

主要功能

  1. 01MVVM 与 Clean Architecture 指导
  2. 02状态管理与依赖注入建议
  3. 03Swift 6 并发与数据流规范
  4. 04布局、动画及性能实践
  5. 05无障碍、预览与测试指导

适用场景

  1. 01规划 SwiftUI 应用架构
  2. 02实现并发安全的视图模型
  3. 03优化列表与自适应布局
  4. 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