swift-actor-persistence by affaan-m/everything-claude-code
npx skills add https://github.com/affaan-m/everything-claude-code --skill swift-actor-persistence使用 Swift actors 构建线程安全数据持久化层的模式。结合内存缓存与文件支持的存储,利用 actor 模型在编译时消除数据竞争。
Actor 模型保证了串行化访问——没有数据竞争,由编译器强制执行。
public actor LocalRepository<T: 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)
// 在初始化期间同步加载(actor 隔离尚未激活)
self.cache = Self.loadSynchronously(from: fileURL)
}
// MARK: - Public API
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)
}
// MARK: - Private
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) })
}
}
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
由于 actor 隔离,所有调用都自动变为异步:
let repository = LocalRepository<Question>()
// 读取 — 从内存缓存进行快速的 O(1) 查找
let question = await repository.find(by: "q-001")
let allQuestions = await repository.loadAll()
// 写入 — 更新缓存并原子性地持久化到文件
try await repository.save(newQuestion)
try await repository.delete("q-001")
@Observable
final class QuestionListViewModel {
private(set) var questions: [Question] = []
private let repository: LocalRepository<Question>
init(repository: LocalRepository<Question> = LocalRepository()) {
self.repository = repository
}
func load() async {
questions = await repository.loadAll()
}
func add(_ question: Question) async throws {
try await repository.save(question)
questions = await repository.loadAll()
}
}
| 决策 | 理由 |
|---|---|
| 使用 Actor(而非 class + 锁) | 编译器强制执行的线程安全,无需手动同步 |
| 内存缓存 + 文件持久化 | 从缓存快速读取,持久化写入磁盘 |
| 同步初始化加载 | 避免异步初始化的复杂性 |
| 使用 ID 作为键的字典 | 通过标识符进行 O(1) 查找 |
泛型化于 Codable & Identifiable | 可在任何模型类型中复用 |
原子文件写入(.atomic) | 防止崩溃时发生部分写入 |
Sendable 类型.atomic 写入 — 防止应用在写入过程中崩溃导致数据损坏init 中同步加载 — 异步初始化器会增加复杂性,而对于本地文件来说收益甚微@Observable ViewModels 结合使用 — 用于响应式 UI 更新DispatchQueue 或 NSLock 而非 actorsawait — 调用者必须处理异步上下文nonisolated 绕过 actor 隔离(违背了初衷)DispatchQueue 的遗留线程安全方案每周安装量
483
代码仓库
GitHub 星标数
72.1K
首次出现时间
2026年2月17日
安全审计
安装于
codex432
opencode414
gemini-cli402
github-copilot401
cursor393
kimi-cli389
Patterns for building thread-safe data persistence layers using Swift actors. Combines in-memory caching with file-backed storage, leveraging the actor model to eliminate data races at compile time.
The actor model guarantees serialized access — no data races, enforced by the compiler.
public actor LocalRepository<T: 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)
// Synchronous load during init (actor isolation not yet active)
self.cache = Self.loadSynchronously(from: fileURL)
}
// MARK: - Public API
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)
}
// MARK: - Private
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) })
}
}
All calls are automatically async due to actor isolation:
let repository = LocalRepository<Question>()
// Read — fast O(1) lookup from in-memory cache
let question = await repository.find(by: "q-001")
let allQuestions = await repository.loadAll()
// Write — updates cache and persists to file atomically
try await repository.save(newQuestion)
try await repository.delete("q-001")
@Observable
final class QuestionListViewModel {
private(set) var questions: [Question] = []
private let repository: LocalRepository<Question>
init(repository: LocalRepository<Question> = LocalRepository()) {
self.repository = repository
}
func load() async {
questions = await repository.loadAll()
}
func add(_ question: Question) async throws {
try await repository.save(question)
questions = await repository.loadAll()
}
}
| Decision | Rationale |
|---|---|
| Actor (not class + lock) | Compiler-enforced thread safety, no manual synchronization |
| In-memory cache + file persistence | Fast reads from cache, durable writes to disk |
| Synchronous init loading | Avoids async initialization complexity |
| Dictionary keyed by ID | O(1) lookups by identifier |
Generic over Codable & Identifiable | Reusable across any model type |
Atomic file writes (.atomic) | Prevents partial writes on crash |
Sendable types for all data crossing actor boundaries.atomic writes to prevent data corruption if the app crashes mid-writeinit — async initializers add complexity with minimal benefit for local files@Observable ViewModels for reactive UI updatesDispatchQueue or NSLock instead of actors for new Swift concurrency codeawait — callers must handle async contextnonisolated to bypass actor isolation (defeats the purpose)DispatchQueue-based thread safety with modern Swift concurrencyWeekly Installs
483
Repository
GitHub Stars
72.1K
First Seen
Feb 17, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
codex432
opencode414
gemini-cli402
github-copilot401
cursor393
kimi-cli389
React 组合模式指南:Vercel 组件架构最佳实践,提升代码可维护性
102,200 周安装
Grimoire CLI 使用指南:区块链法术编写、验证与执行全流程
940 周安装
Grimoire Uniswap 技能:查询 Uniswap 元数据与生成代币/资金池快照的 CLI 工具
940 周安装
Grimoire Aave 技能:查询 Aave V3 元数据和储备快照的 CLI 工具
941 周安装
Railway CLI 部署指南:使用 railway up 命令快速部署代码到 Railway 平台
942 周安装
n8n Python 代码节点使用指南:在自动化工作流中编写 Python 脚本
943 周安装
Flutter Platform Views 实现指南:Android/iOS/macOS原生视图与Web嵌入教程
943 周安装