ECC 的 Swift 模式指南:协议驱动设计、值类型与并发安全编程范式 📅 发布时间:2026/9/11 21:27:07 👁 浏览次数: ECC 的 Swift 模式指南协议驱动设计、值类型与并发安全编程范式【免费下载链接】ECCThe agent harness performance optimization system. Skills, instincts, memory, security, and research-first development for Claude Code, Codex, Opencode, Cursor and beyond.项目地址: https://gitcode.com/GitHub_Trending/ev/ECC本文基于 ECC 仓库中 rules/swift/patterns.md含日文版 docs/ja-JP/rules/swift/patterns.md这一 Swift 语言专属规则文件展开。该文件是 ECC 分层规则体系rules/common通用层 rules/swift语言专属层的重要组成部分为 Claude Code、Codex、Opencode、Cursor 等 Agent 在 Swift 项目上提供一套协议驱动、值类型优先、Actor 并发的现代 Swift 编程范式。读完本文你将掌握如何用小型协议 协议扩展组织抽象、用结构体与关联值枚举建模数据、用 Actor 替代锁与派发队列、以及用默认参数的协议注入实现可测试架构——并了解这些范式在 swift-actor-persistence 与 swift-protocol-di-testing 两个技能中的落地实现。规则文件在 ECC 体系中的定位在深入模式本身之前先明确这份文件在整个仓库中的位置。根据 rules/README.md 的说明ECC 的规则采用common 通用层 语言专属层的两层结构rules/ ├── common/ # 语言无关的通用原则始终安装 │ ├── coding-style.md │ ├── testing.md │ ├── patterns.md │ └── ... └── swift/ # Swift 专属规则 ├── coding-style.md ├── testing.md ├── patterns.md # ← 本文主题 ├── hooks.md └── security.md其中common/patterns.md提供了语言无关的设计模式基础如仓库模式 Repository Pattern、API 响应封装格式等而rules/swift/patterns.md以「 This file extends common/patterns.md with Swift specific content.」开篇明确声明自己是通用模式的Swift 语言化扩展——用 Swift 特有的协议、值类型、Actor 等语言能力把通用模式落地为可直接执行的代码范式。层级优先规则rules/README.md「Rule Priority」一节规定当语言专属规则与通用规则冲突时语言专属规则优先类似于 CSS 特指度或.gitignore优先级。因此这份patterns.md就是 Agent 处理 Swift 代码时关于怎么设计的最高优先级依据。该文件通过 front matter 声明生效范围paths: - **/*.swift - **/Package.swift即当 Agent 读写任意.swift源文件或 SwiftPM 的Package.swift清单时以下四类模式均应被激活应用。安装方式见 rules/README.md可使用./install.sh swift一键安装或手动cp -r rules/swift ~/.claude/rules/ecc/。协议导向设计Protocol-Oriented Design核心原则小而聚焦的协议 协议扩展共享默认实现patterns.md给出的第一条范式是定义小而聚焦的协议small, focused protocols用协议扩展protocol extensions提供共享默认实现。这与swift-protocol-di-testing技能中反复强调的单一职责不要创建包含大量方法的 God Protocol一脉相承。原文档给出的示例是一个异步仓库协议protocol Repository: Sendable { associatedtype Item: Identifiable Sendable func find(by id: Item.ID) async throws - Item? func save(_ item: Item) async throws }这个例子浓缩了三条现代 Swift 设计要点关联类型 协议约束associatedtype Item: Identifiable Sendable让协议泛化于任何可唯一标识且可安全跨隔离域传递的模型类型Item.ID直接使用Identifiable的 ID 类型无需额外类型参数异步错误签名async throws把 I/O 与错误处理显式写进协议契约符合 Swift 并发与类型化错误typed throws见 rules/swift/coding-style.md的演进方向Sendable约束保证协议约束的类型可以安全跨越 Actor 隔离边界这是 Swift 6 严格并发检查下的硬性要求。这一范式在 swift-actor-persistence 技能中有完整的落地public actor LocalRepositoryT: Codable Identifiable正是协议/泛型约束 并发隔离的具体实现通过字典[String: T]提供 O(1) 查找。与通用仓库模式的衔接从源码结构看这个 Swift 协议示例正是common/patterns.md中 Repository Pattern 的 Swift 化表达common/patterns.md规定将数据访问封装在统一接口之后定义标准操作 findById、create、update、delete具体实现隐藏存储细节数据库、API、文件等业务逻辑依赖抽象接口而非存储机制。patterns.md用 Swift 语法将这套通用原则落为find(by:)/save(_:)两个异步契约同时用Sendable、associatedtype等语言机制补上了并发安全与类型安全两个 Swift 特有维度。二者合读就是通用原则 → 语言落地的完整链路。值类型优先Value Types结构体承载数据关联值枚举建模状态第二条范式直指 Swift 与 Objective-C 时代最大的分水岭——值语义数据传输对象DTO与模型一律使用结构体struct。结构体具有值语义赋值即拷贝、let不可变、天然线程安全不需要像 class 那样管理引用与生命周期用带关联值associated values的枚举建模不同状态。这是 Swift 表达有限状态集合 每状态附带数据的标准手法。原文档示例enum LoadStateT: Sendable: Sendable { case idle case loading case loaded(T) case failed(Error) }这个LoadState是典型的 UI 数据加载状态机idle尚未开始、loading加载中、loaded(T)成功并携带数据、failed(Error)失败并携带错误。相比布尔量加可选值的组合枚举加关联值把不可能状态从类型层面排除——例如不存在既 loading 又 loaded的组合。而Sendable约束保证了该状态值可以安全地在 Actor、Task 之间传递。补充说明这条值类型优先原则同样出现在 rules/swift/coding-style.md 的 Immutability 一节——默认使用带值语义的 struct仅在需要身份identity或引用语义时才使用 class以及优先let而非var。两份规则文件相互印证共同构成 ECC 对 Swift 值语义的完整要求。Actor 模式Actor Pattern用 Actor 取代锁与派发队列第三条范式针对 Swift 中最容易被误用的领域——共享可变状态用 Actor 取代锁locks和派发队列dispatch queues来保护共享可变状态。原文档示例actor CacheKey: Hashable Sendable, Value: Sendable { private var storage: [Key: Value] [:] func get(_ key: Key) - Value? { storage[key] } func set(_ key: Key, value: Value) { storage[key] value } }Actor 的价值在于编译器强制的串行化访问Actor 隔离actor isolation由编译期检查保证任何对storage的读写都必须经过 Actor 的串行执行体从语言层面消灭数据竞争data race而无需手写NSLock、DispatchQueue.sync或信号量。这一范式在 swift-actor-persistence 中被放大为完整的持久化层实现public actor LocalRepositoryT: Codable Identifiable where T.ID String { private var cache: [String: T] [:] private let fileURL: URL public init(directory: URL .documentsDirectory, filename: String data.json) { self.fileURL directory.appendingPathComponent(filename) self.cache Self.loadSynchronously(from: fileURL) // init 期间 Actor 隔离尚未生效同步加载 } public func save(_ item: T) throws { cache[item.id] item try persistToFile() } public func delete(_ id: String) throws { cache[id] nil try persistToFile() } public func find(by id: String) - T? { cache[id] } public func loadAll() - [T] { Array(cache.values) } private func persistToFile() throws { let data try JSONEncoder().encode(Array(cache.values)) try data.write(to: fileURL, options: .atomic) // 原子写入防崩溃中断写坏文件 } private static func loadSynchronously(from url: URL) - [String: T] { guard let data try? Data(contentsOf: url), let items try? JSONDecoder().decode([T].self, from: data) else { return [:] } return Dictionary(uniqueKeysWithValues: items.map { ($0.id, $0) }) } }该技能文档skills/swift-actor-persistence/SKILL.md 的 Key Design Decisions 表明确列出了选择 Actor 而非class 锁的理由编译期强制线程安全、无需手工同步同时给出内存缓存 文件持久化、按 ID 建字典 O(1) 查找、.atomic原子写入防止崩溃时部分写入等设计决策。所有调用因 Actor 隔离自动变为异步let repository LocalRepositoryQuestion() let question await repository.find(by: q-001) // 读内存缓存 O(1) try await repository.save(newQuestion) // 写更新缓存并原子落盘 try await repository.delete(q-001)需要避免的反模式patterns.md虽未展开但配套技能与 rules/swift/coding-style.md 的 Concurrency 一节给出了明确的边界在 Swift 并发代码中继续使用DispatchQueue/NSLock而非 Actor技能明确列为反模式用nonisolated绕过 Actor 隔离等于放弃设计初衷忘记所有 Actor 方法调用都需要await对外暴露内部缓存字典在 Swift 6 严格并发检查下凡跨越隔离边界的数据都必须满足Sendable。依赖注入Dependency Injection默认参数的协议注入生产用默认测试注入 Mock第四条范式给出一种零框架、零依赖的 Swift 依赖注入手法——带默认参数的协议注入struct UserService { private let repository: any UserRepository init(repository: any UserRepository DefaultUserRepository()) { self.repository repository } }要点在于init的默认参数生产环境直接UserService()即使用DefaultUserRepository()真实实现测试环境注入UserService(repository: MockUserRepository())即可替换为 Mock。注意这里使用了any关键字存在性类型existential type这是 Swift 5.7 对protocol作为类型使用的显式写法。技能中的完整落地从协议到测试swift-protocol-di-testing 把这一范式扩展为一条可复制的五步流水线第 1 步定义小而聚焦的协议每个协议只处理一个外部关注点public protocol FileSystemProviding: Sendable { func containerURL(for purpose: Purpose) - URL? } public protocol FileAccessorProviding: Sendable { func read(from url: URL) throws - Data func write(_ data: Data, to url: URL) throws func fileExists(at url: URL) - Bool }第 2 步提供生产默认实现public struct DefaultFileAccessor: FileAccessorProviding { public init() {} public func read(from url: URL) throws - Data { try Data(contentsOf: url) } public func write(_ data: Data, to url: URL) throws { try data.write(to: url, options: .atomic) } public func fileExists(at url: URL) - Bool { FileManager.default.fileExists(atPath: url.path) } }第 3 步为测试创建 Mock 实现带可配置的错误属性以模拟失败路径public final class MockFileAccessor: FileAccessorProviding, unchecked Sendable { public var files: [URL: Data] [:] public var readError: Error? public var writeError: Error? public func read(from url: URL) throws - Data { if let error readError { throw error } guard let data files[url] else { throw CocoaError(.fileReadNoSuchFile) } return data } // write / fileExists 同理操作内存字典 files }第 4 步默认参数注入——生产代码不感知 Mock测试才显式指定public actor SyncManager { private let fileSystem: FileSystemProviding private let fileAccessor: FileAccessorProviding public init( fileSystem: FileSystemProviding DefaultFileSystemProvider(), fileAccessor: FileAccessorProviding DefaultFileAccessor() ) { self.fileSystem fileSystem self.fileAccessor fileAccessor } }第 5 步用 Swift Testing 写确定性测试无真实 I/Oimport Testing Test(Sync manager handles missing container) func testMissingContainer() async { let mockFileSystem MockFileSystemProvider(containerURL: nil) let manager SyncManager(fileSystem: mockFileSystem) await #expect(throws: SyncError.containerNotAvailable) { try await manager.sync() } } Test(Sync manager handles read errors gracefully) func testReadError() async { let mockFileAccessor MockFileAccessor() mockFileAccessor.readError CocoaError(.fileReadCorruptFile) let manager SyncManager(fileAccessor: mockFileAccessor) await #expect(throws: SyncError.self) { try await manager.sync() } }技能同时给出最佳实践与反模式清单只 Mock 外部依赖边界文件系统、网络、外部 API不 Mock 无外部依赖的内部类型用Sendable约束跨 Actor 边界的协议用可配置的错误属性模拟失败路径避免用#if DEBUG条件编译代替真正的依赖注入若类型没有外部依赖就不必为其引入协议。配套技能与参考资源patterns.md在文末 Reference 一节指向两个配套技能二者正是本文件四大范式协议、值类型、Actor、DI的实战放大swift-actor-persistenceskills/swift-actor-persistence/SKILL.md —— 基于 Actor 的线程安全持久化模式内存缓存 文件落盘把用 Actor 替代锁与派发队列落到真实的本地存储场景可与ObservableViewModel 组合实现响应式 UI见技能中QuestionListViewModel示例。swift-protocol-di-testingskills/swift-protocol-di-testing/SKILL.md —— 基于协议的依赖注入与 Swift Testing Mock 模式把小而聚焦协议 默认参数注入落到可测试架构设计。如需扩展阅读同一规则目录下还有配套的 rules/swift/coding-style.md值类型与不可变性、类型化 throws、严格并发检查、rules/swift/testing.mdTest/#expect与参数化测试、swift test --enable-code-coverage覆盖率、以及 rules/swift/security.md 与 rules/swift/hooks.md通用层基础见 rules/common/patterns.md。这些文件连同日文版 docs/ja-JP/rules/swift/patterns.md 构成了 ECC 面向 Swift 的完整编码规则面。小结一份可直接执行的 Swift 设计检查单将patterns.md的四大范式汇总为可供 Agent 与开发者直接对照的检查单范式核心动作判定标准协议导向设计定义小而聚焦的协议共享默认行为放协议扩展协议职责单一Sendable约束到位值类型DTO/模型用 struct状态机用带关联值的 enum无继承需求、无身份需求时不用 classActor 模式共享可变状态用 actor 封装代码中不出现手写NSLock/ 派发队列同步依赖注入协议 默认参数注入生产零参数实例化测试注入 Mock这四条范式互相咬合协议保证抽象的稳定性值类型保证数据的可传递性SendableActor 保证共享状态的安全性默认参数注入保证上述三者的可测试性。它们共同构成 ECC 推荐的、与现代 SwiftSwift 5.5 并发 / Swift 6 严格并发完全对齐的编码基线。【免费下载链接】ECCThe agent harness performance optimization system. Skills, instincts, memory, security, and research-first development for Claude Code, Codex, Opencode, Cursor and beyond.项目地址: https://gitcode.com/GitHub_Trending/ev/ECC创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考