---
name: swift-actor-persistence
description: 在 Swift 中使用 Actors 進行線程安全的資料持久化 — 結合記憶體快取與檔案儲存，從設計上消除資料競爭 (Data Races)。
---

# 使用 Swift Actors 達成線程安全持久化

這是在 Swift 中使用 Actor 構建線程安全資料持久層的模式。它結合了記憶體快取 (In-memory cache) 與磁碟檔案存儲，利用 Actor 模型在編譯時期即消除資料競爭風險。

## 何時啟用

- 在 Swift 5.5+ 環境下構建資料持久層。
- 對共享的可變狀態 (Shared Mutable State) 需要線程安全存取。
- 希望淘汰手動同步機制（如 Locks, DispatchQueue）。
- 開發具備本地存儲能力的離線優先 (Offline-first) 應用程式。

## 核心模式：基於 Actor 的 Repository

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)
        // 在 init 期間同步載入（此時 Actor Isolation 尚未啟用）
        self.cache = Self.loadSynchronously(from: fileURL)
    }

    // 提供給外部使用的線程安全 API
    public func save(_ item: T) throws {
        cache[item.id] = item
        try persistToFile()
    }

    public func find(by id: String) -> T? {
        cache[id]
    }

    // 私有實作：原子化寫入檔案以防止損毀
    private func persistToFile() throws {
        let data = try JSONEncoder().encode(Array(cache.values))
        try data.write(to: fileURL, options: .atomic)
    }
}
```

## 使用方式

由於 Actor 隔離機制，所有外部調用皆會自動轉換為 `async` 行為：

```swift
let repository = LocalRepository<User>()

// 讀取 — 從記憶體快取中進行極速 O(1) 查找
let user = await repository.find(by: "u-001")

// 寫入 — 同步更新快取並原子化持久化至磁碟
try await repository.save(newUser)
```

## 實踐之最佳實踐

- **使用 `Sendable` 類型**：確保所有跨越 Actor 邊界的資料皆符合 Sendable 協定。
- **內部實作封裝**：Actor 的公開 API 應僅暴露業務邏輯操作，隱藏持久化細節。
- **原子化寫入 (`.atomic`)**：防止 App 在寫入中途崩潰導致資料損毀。
- **配合 `@Observable`**：在 ViewModel 中使用，為 UI 提供反應式數據更新。

## 應避免的反模式

- 在新的 Swift Concurrency 程式碼中繼續混用 `DispatchQueue` 或 `NSLock`。
- 對外暴露內部的 Cache 字典。
- 使用 `nonisolated` 關鍵字來規避 Actor 隔離（這會破壞線程安全性）。

## 適用情境

- iOS/macOS 應用的本地資料存儲（用戶資料、偏好設定、快取內容）。
- 需要更換舊有的 `DispatchQueue` 式同步邏輯，轉向現代化 Swift Concurrency 架構。
