关于 swift-mvvm
面向 Swift 编码 Agent 的 MVVM 技能,指导创建和重构 ViewModel。强调隔离 UI 框架依赖、拆分可测试逻辑,并通过协议注入依赖。
主要功能
- 01保持 ViewModel 不依赖 UI 框架
- 02将状态、意图与依赖协调分开
- 03拆分纯逻辑和副作用单元
- 04通过协议注入依赖
- 05管理主线程状态与任务取消
- 06提供 ViewModel 测试指导
适用场景
- 01为 SwiftUI、UIKit 或 AppKit 添加功能
- 02重构臃肿的 ViewModel
- 03排查状态管理与线程问题
- 04提升依赖模拟和状态测试能力
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ tobitech/ swift-mvvm --skill swift-mvvm安装后在 Claude Code 中输入/swift-mvvm确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
README.mdreferences/adding-new-features.mdfile-naming-conventions.mdmodule-structure.mdcommon-patterns.mdmvvm-overview.mdintegration-patterns.mdmvvm-style-guide.mdcontroller-vs-coordinator.mdanti-patterns.mdservices-vs-feature-services.mdwhere-things-go.mdstate-management.mdtesting-considerations.mdtemplates/AppKitRevealer.swiftAppKitViewController.swiftObservationViewModel.swiftServiceUseCaseControllerAndPureLogic.swiftCombineViewModel.swiftProtocolAdapters.swiftViewModelTests.swiftSwiftUICombineView.swiftSwiftUIObservationView.swift详细介绍
Swift MVVM 技能
帮助 Agent 为 Swift 项目编写更好的 MVVM 代码。
核心原则
- 让 ViewModel 不依赖 UI 框架。 ViewModel 不应导入
SwiftUI、UIKit或AppKit。 - ViewModel 应主要由以下内容组成:(1)状态、(2)意图方法和(3)依赖协调。
- 将工作拆分到更小、可测试的单元中(纯结构体/函数、用例、控制器、存储库、映射器、格式化器)。
- 使用基于协议的依赖注入,确保所有依赖都可模拟。
- 鼓励使用扩展来组织代码(尤其是协议遵循)。
适用场景
当用户要求执行以下操作时,使用此技能:
- 在 SwiftUI/UIKit/AppKit 中添加或重构功能,同时保持架构清晰。
- 创建或改进 ViewModel(状态、意图、效果),并将逻辑移出 View/ViewController。
- 修复状态管理问题(包装器错误、线程警告、不稳定的绑定)。
- 提高可测试性(基于协议的依赖、模拟对象/假对象、确定性的状态更新)。
- 通过拆分职责来精简“臃肿的 ViewModel”。
首先检查(编写代码之前)
- 确定 UI 技术:SwiftUI、UIKit 或 AppKit。
- 确定部署目标:
- 如果支持 iOS 17+/macOS 14+,优先使用 Observation(
@Observable)。 - 否则优先使用基于 Combine 的
ObservableObject和@Published。
- 如果支持 iOS 17+/macOS 14+,优先使用 Observation(
- 遵循项目现有风格(命名、文件夹、依赖注入方式、网络层)。
MVVM 职责
View 层
SwiftUI View/UIKit UIViewController/AppKit NSViewController:
- 声明布局并绑定到状态。
- 将用户意图(点击、选择、文本更改)发送给 ViewModel。
- 负责仅与 UI 有关的事项(导航、显示警告、AppKit 面板、第一响应者等)。
ViewModel
- 管理屏幕状态(加载/数据/错误、派生的 UI 值)。
- 通过注入的依赖协调效果。
- 提供意图方法(
onAppear()、refresh()、didTap…),而不是让视图随意调用内部方法。
Domain/Services
- 封装数据获取、缓存、持久化、解码和验证。
- 必须独立于 UI 框架。
硬性规则:ViewModel 不得导入 UI 框架
ViewModel 不应导入:
SwiftUIUIKitAppKit
如果 ViewModel 需要平台行为,应在非 UI 模块(仅依赖 Foundation)中定义一个小型协议,并在 View 层(或平台适配器模块)提供平台实现。
示例:
// In a Foundation-only target / file.
protocol FileRevealing {
func reveal(_ url: URL)
}
// In AppKit layer.
import AppKit
struct WorkspaceFileRevealer: FileRevealing {
func reveal(_ url: URL) {
NSWorkspace.shared.activateFileViewerSelecting([url])
}
}
Any “AppKit-y” or “UIKit-y” behavior inside the ViewModel is a **smell**.
## Preferred ViewModel shapes
Pick one based on scope.
### Pattern A: Simple screen
- `State` struct (nested)
- intent methods
- async `load()` with cancellation
### Pattern B: Complex screen
- `State` struct (nested)
- `Action` enum + `send(_:)`
- reducer-like switch for state transitions
- side effects delegated to injected units
## Keep state structured (avoid a ViewModel with 30 vars)
Prefer:
```swift
struct State: Equatable {
var view = ViewState()
var content = ContentState()
var alerts = AlertsState()
struct ViewState: Equatable {
var isLoading = false
var title = ""
}
struct ContentState: Equatable {
var rows: [Row] = []
var emptyMessage: String? = nil
}
struct AlertsState: Equatable {
var error: ErrorState? = nil
}
}
重构臃肿的 ViewModel(优先顺序)
精简大型 VM 时,按以下顺序重构:
- 首先提取纯逻辑
- 将非 IO 计算移入纯结构体或纯函数。
- 示例:筛选、排序、将领域模型映射为行模型、格式化、验证、状态归约器。
- 将副作用提取到控制器中(仍可测试)
- 创建用于 IO 和编排的小型“效果”单元。
- 通过协议封装,并注入 VM。
- 示例:
LoadExamplesUseCase、ExamplesController、AnalyticsTracking、FileRevealing。
- 让 VM 只保留状态和意图
- VM 将意图转发给纯逻辑/效果单元,并设置状态。
并发与取消规则
- UI 状态更改应在主 actor上进行。
- 保存正在运行的任务,并在新请求开始或视图消失时取消这些任务。
SwiftUI 集成规则
Observation(@Observable)
- 使用
@State持有 ViewModel 实例(所有者),并将其向下传递。
Combine(ObservableObject)
- 当视图创建/拥有 ViewModel 时,使用
@StateObject。 - 当视图接收 ViewModel 时,使用
@ObservedObject。
依赖注入规则
- ViewModel 在初始化器中接收依赖。
- 依赖应为协议;为生产环境提供实现,并为测试提供模拟/伪实现。
- 优先注入小单元:
- 纯逻辑:
RowBuilder、Validator、Reducer - 副作用:
UseCase、Controller、Repository - 平台适配器:
FileRevealing、URLOpening等
- 纯逻辑:
避免臃肿的 ViewModel
问题表现:
- 导入 UI 框架。
- 直接处理 URLSession/JSON 解码。
- 创建 NSAlert/UIAlertController。
- 了解 NSWorkspace/UIApplication。
- 在内联代码中使用复杂逻辑处理所有格式化。
- 将导航、分析、网络和缓存都放在一起处理。
重构方式:
- 提取纯逻辑
RowBuilder或Reducer。 - 为业务规则提取
UseCase。 - 为 IO 提取
Repository。 - 提取副作用
Controller来协调多个服务。 - 为 UI 操作提取平台适配器(小型协议)。
输出要求
编写或重构时:
- 按文件分组,以便于审查差异的代码块提供代码。
- 优先使用小型、可组合的函数。
- 使用扩展进行组织(例如,协议遵循、分组辅助方法)。
- 使用清晰的命名:
FooViewModel、FooState、FooUseCase、FooRepository、FooController、FooRowBuilder。 - 为 ViewModel 状态转换和提取出的纯逻辑添加测试。
测试指南
新测试优先使用 Swift Testing(import Testing、@Test、#expect、#require)。
- 直接测试纯逻辑单元(快速、确定)。
- 通过注入模拟对象,测试 ViewModel 状态转换(成功/失败/取消)。
模板
复制/粘贴自:
templates/ObservationViewModel.swifttemplates/CombineViewModel.swifttemplates/AppKitViewController.swifttemplates/ProtocolAdapters.swifttemplates/ServiceUseCaseControllerAndPureLogic.swifttemplates/ViewModelTests.swift(Swift Testing)
附加资源(仅在需要时加载)
- MVVM 概述:references/mvvm-overview.md
- 模块结构:references/module-structure.md
- 文件命名约定:references/file-naming-conventions.md
- 内容放置位置:references/where-things-go.md
- 常见模式:references/common-patterns.md
- 添加新功能:references/adding-new-features.md
- 集成模式:references/integration-patterns.md
- 反模式:references/anti-patterns.md
- 测试注意事项:references/testing-considerations.md
- 服务与功能服务:references/services-vs-feature-services.md
- Controller 与 Coordinator:references/controller-vs-coordinator.md
- 状态管理:references/state-management.md
评论
支持 Markdown