LocalData/Documentation/Migration.md
Matt Bruce fff33f2a08 Update LocalData.swift + docs
Summary:
- Sources: LocalData.swift
- Docs: Design, Migration, Migration_Refactor_Plan_Clean
- Added symbols: extension StorageKey, typealias Value, struct StorageKey, struct SimpleLegacyMigration, struct AppVersionConditionalMigration, func transform (+8 more)
- Removed symbols: struct MyNewKey, typealias DestinationKey, extension MyNewKey, protocol StorageKey, extension StorageKey, struct SimpleLegacyMigration (+16 more)

Stats:
- 4 files changed, 146 insertions(+), 132 deletions(-)
2026-01-18 14:53:30 -06:00

83 lines
2.6 KiB
Markdown

# LocalData Migration Guide
## Overview
`LocalData` provides protocol-based migration support to move data from legacy storage locations to modern `StorageKey` values.
## Automatic Migration
When calling `get(_:)` on a key, the `StorageRouter` automatically:
1. Checks the primary location.
2. If not found, evaluates `migration` defined on the key.
3. If data is found in a source:
- Unsecures it using the source's old policy.
- Re-secures it using the destination key's policy.
- Stores it in the new location.
- Deletes the legacy data.
- Returns the value.
## Proactive Migration (Sweep)
You can trigger a sweep of all registered keys at app launch:
```swift
try await StorageRouter.shared.registerCatalog(MyCatalog(), migrateImmediately: true)
```
This iterates through all keys in the catalog and calls `forceMigration(for:)` on each, ensuring all legacy data is consolidated.
## Defining Migration Sources
### Simple Legacy Migration
For 1:1 migrations, attach a `SimpleLegacyMigration`:
```swift
extension StorageKey where Value == String {
static let legacyToken = StorageKey(
name: "old_key_name",
domain: .userDefaults(suite: nil),
security: .none,
serializer: .json,
owner: "MigrationDemo",
description: "Legacy token stored in UserDefaults."
)
static let modernToken = StorageKey(
name: "modern_token",
domain: .keychain(service: "com.myapp"),
owner: "MigrationDemo",
description: "Modern token stored in Keychain.",
migration: { key in
AnyStorageMigration(
SimpleLegacyMigration(
destinationKey: key,
sourceKey: .key(StorageKey.legacyToken)
)
)
}
)
}
```
### Protocol-Based Migration
For complex scenarios, attach an explicit migration:
```swift
struct MyMigration: StorageMigration {
typealias Value = String
let destinationKey = StorageKey.modernToken
func shouldMigrate(using router: StorageRouter, context: MigrationContext) async throws -> Bool {
try await router.exists(destinationKey)
}
func migrate(using router: StorageRouter, context: MigrationContext) async throws -> MigrationResult {
// Custom migration logic
MigrationResult(success: true)
}
}
extension StorageKey where Value == String {
static let modernToken = StorageKey(
name: "modern_token",
domain: .keychain(service: "com.myapp"),
owner: "MigrationDemo",
description: "Modern token stored in Keychain.",
migration: { _ in AnyStorageMigration(MyMigration()) }
)
}
```