About this skill
This skill provides a robust pattern for building a thread-safe data persistence layer in Swift (5.5+). It leverages the Actor model to ensure serialized access to shared mutable state, thereby eliminating data races at compile time without the need for manual synchronization mechanisms like locks or DispatchQueues. The persistence layer combines an in-memory cache for fast access with file-based storage (e.g., JSON) for durability, making it ideal for offline-first applications. It offers a structured approach to manage local data, ensuring data integrity and simplifying concurrent programming in Swift.
Best use case
To generate or implement a thread-safe local data persistence layer in Swift applications; to build offline-first applications requiring robust local storage; to manage shared mutable state concurrently without manual locking; to leverage modern Swift concurrency features for data management.
在 Swift 中使用 actor 实现线程安全的数据持久化——基于内存缓存与文件支持的存储,通过设计消除数据竞争。
A well-structured, thread-safe Swift persistence layer (e.g., a `LocalRepository` actor) that handles data caching and file storage, effectively eliminating data races. The generated code will be clean, utilizing Swift's Actor model for safe concurrency, and ready to be integrated into a Swift application.
Practical example
Example input
Generate a Swift actor-based persistence layer for `Product` objects (with `id`, `name`, `price`), including in-memory cache and file storage. Ensure it's thread-safe for a local-first application.
Example output
```swift
import Foundation
struct Product: Codable, Identifiable {
let id: String
var name: String
var price: Double
}
extension URL {
static var documentsDirectory: URL {
FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
}
}
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.loadFromFile(fileURL: self.fileURL) ?? [:]
}
private static func loadFromFile(fileURL: URL) -> [String: T]? {
guard let data = try? Data(contentsOf: fileURL) else { return nil }
return try? JSONDecoder().decode([String: T].self, from: data)
}
private func saveToFile() {
guard let data = try? JSONEncoder().encode(cache) else { return }
try? data.write(to: fileURL)
}
public func save(_ item: T) { // Actor isolated
cache[item.id] = item
saveToFile()
}
public func get(id: String) -> T? { // Actor isolated
cache[id]
}
public func delete(id: String) { // Actor isolated
cache.removeValue(forKey: id)
saveToFile()
}
public func getAll() -> [T] { // Actor isolated
Array(cache.values)
}
}
// Example Usage (in an async context):
// async { // Or inside a Task
// let productRepository = LocalRepository<Product>(filename: "products.json")
// let newProduct = Product(id: "prod_001", name: "Laptop", price: 1200.00)
// await productRepository.save(newProduct)
// if let fetchedProduct = await productRepository.get(id: "prod_001") {
// print("Fetched: \(fetchedProduct.name)")
// }
// }
```When to use this skill
- When building data persistence layers in Swift 5.5+ applications (iOS, macOS, watchOS, tvOS).
- When thread-safe access to shared mutable data is crucial, and you want to avoid data races.
- When designing an application that requires local data caching and file-based storage.
- When aiming to reduce complexity and potential bugs associated with manual synchronization (locks, semaphores, DispatchQueues).
When not to use this skill
- For applications not written in Swift.
- When a full-fledged database solution (e.g., Core Data, Realm, SQLite, CloudKit) is required for complex queries, schema migrations, relationships, or extremely large datasets.
- In environments where Swift Actors are not supported (pre-Swift 5.5).
- When data persistence is not a requirement for the application's functionality.
Installation
Claude Code / Cursor / Codex
Manual Installation
- Download SKILL.md from GitHub
- Place it in
.claude/skills/swift-actor-persistence/SKILL.mdinside your project - Restart your AI agent — it will auto-discover the skill
How swift-actor-persistence Compares
| Feature / Agent | swift-actor-persistence | Standard Approach |
|---|---|---|
| Platform Support | Claude | Limited / Varies |
| Context Awareness | High | Baseline |
| Installation Complexity | easy | N/A |
Frequently Asked Questions
What does this skill do?
在 Swift 中使用 actor 实现线程安全的数据持久化——基于内存缓存与文件支持的存储,通过设计消除数据竞争。
Which AI agents support this skill?
This skill is designed for Claude.
How difficult is it to install?
The installation complexity is rated as easy. You can find the installation instructions above.
Where can I find the source code?
You can find the source code on GitHub using the link provided at the top of the page.
Related Guides
AI Agents for Coding
Browse AI agent skills for coding, debugging, testing, refactoring, code review, and developer workflows across Claude, Cursor, and Codex.
Best AI Skills for Claude
Explore the best AI skills for Claude and Claude Code across coding, research, workflow automation, documentation, and agent operations.
ChatGPT vs Claude for Agent Skills
Compare ChatGPT and Claude for AI agent skills across coding, writing, research, and reusable workflow execution.
SKILL.md Source
# 用于线程安全持久化的 Swift Actor
使用 Swift actor 构建线程安全数据持久化层的模式。结合内存缓存与文件支持的存储,利用 actor 模型在编译时消除数据竞争。
## 何时激活
* 在 Swift 5.5+ 中构建数据持久化层
* 需要对共享可变状态进行线程安全访问
* 希望消除手动同步(锁、DispatchQueue)
* 构建具有本地存储的离线优先应用
## 核心模式
### 基于 Actor 的存储库
Actor 模型保证了序列化访问 —— 没有数据竞争,由编译器强制执行。
```swift
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) })
}
}
```
### 用法
由于 actor 隔离,所有调用都会自动变为异步:
```swift
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 ViewModel 结合使用
```swift
@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(而非类 + 锁) | 编译器强制执行的线程安全性,无需手动同步 |
| 内存缓存 + 文件持久化 | 从缓存中快速读取,持久化写入磁盘 |
| 同步初始化加载 | 避免异步初始化的复杂性 |
| 按 ID 键控的字典 | 按标识符进行 O(1) 查找 |
| 泛型化 `Codable & Identifiable` | 可在任何模型类型中重复使用 |
| 原子文件写入 (`.atomic`) | 防止崩溃时部分写入 |
## 最佳实践
* **对所有跨越 actor 边界的数据使用 `Sendable` 类型**
* **保持 actor 的公共 API 最小化** —— 仅暴露领域操作,而非持久化细节
* **使用 `.atomic` 写入** 以防止应用在写入过程中崩溃导致数据损坏
* **在 `init` 中同步加载** —— 异步初始化器会增加复杂性,而对本地文件的益处微乎其微
* **与 `@Observable` ViewModel 结合使用** 以实现响应式 UI 更新
## 应避免的反模式
* 在 Swift 并发新代码中使用 `DispatchQueue` 或 `NSLock` 而非 actor
* 将内部缓存字典暴露给外部调用者
* 在不进行验证的情况下使文件 URL 可配置
* 忘记所有 actor 方法调用都是 `await` —— 调用者必须处理异步上下文
* 使用 `nonisolated` 来绕过 actor 隔离(违背了初衷)
## 何时使用
* iOS/macOS 应用中的本地数据存储(用户数据、设置、缓存内容)
* 稍后同步到服务器的离线优先架构
* 应用中多个部分并发访问的任何共享可变状态
* 用现代 Swift 并发性替换基于 `DispatchQueue` 的旧式线程安全机制Related Skills
swiftui-patterns
SwiftUI 架构模式,使用 @Observable 进行状态管理,视图组合,导航,性能优化,以及现代 iOS/macOS UI 最佳实践。
swift-protocol-di-testing
基于协议的依赖注入,用于可测试的Swift代码——使用聚焦协议和Swift Testing模拟文件系统、网络和外部API。
swift-concurrency-6-2
Swift 6.2 可接近的并发性 — 默认单线程,@concurrent 用于显式后台卸载,隔离一致性用于主 actor 类型。
workspace-surface-audit
Audit the active repo, MCP servers, plugins, connectors, env surfaces, and harness setup, then recommend the highest-value ECC-native skills, hooks, agents, and operator workflows. Use when the user wants help setting up Claude Code or understanding what capabilities are actually available in their environment.
safety-guard
Use this skill to prevent destructive operations when working on production systems or running agents autonomously.
repo-scan
Cross-stack source code asset audit — classifies every file, detects embedded third-party libraries, and delivers actionable four-level verdicts per module with interactive HTML reports.
project-flow-ops
Operate execution flow across GitHub and Linear by triaging issues and pull requests, linking active work, and keeping GitHub public-facing while Linear remains the internal execution layer. Use when the user wants backlog control, PR triage, or GitHub-to-Linear coordination.
manim-video
Build reusable Manim explainers for technical concepts, graphs, system diagrams, and product walkthroughs, then hand off to the wider ECC video stack if needed. Use when the user wants a clean animated explainer rather than a generic talking-head script.
laravel-plugin-discovery
Discover and evaluate Laravel packages via LaraPlugins.io MCP. Use when the user wants to find plugins, check package health, or assess Laravel/PHP compatibility.
design-system
Use this skill to generate or audit design systems, check visual consistency, and review PRs that touch styling.
click-path-audit
Trace every user-facing button/touchpoint through its full state change sequence to find bugs where functions individually work but cancel each other out, produce wrong final state, or leave the UI in an inconsistent state. Use when: systematic debugging found no bugs but users report broken buttons, or after any major refactor touching shared state stores.
ck
Persistent per-project memory for Claude Code. Auto-loads project context on session start, tracks sessions with git activity, and writes to native memory. Commands run deterministic Node.js scripts — behavior is consistent across model versions.