diff --git a/README.md b/README.md index 4f05038..a32cb92 100644 --- a/README.md +++ b/README.md @@ -121,6 +121,11 @@ The app demonstrates various storage configurations: - Sync policies (never, manual, automaticSmall) - Global sync configuration (max file size) in app `init` +### Data Migration +- **Fallback**: Automatically moves data from `LegacyMigrationSourceKey` to `ModernMigrationDestinationKey` on first access. +- **Manual Sweep**: Explicitly triggers a "drain" of legacy keys to the Keychain using `StorageRouter.shared.migrate(for:)`. +- **Startup Sweep**: Automatically cleanses all registered legacy keys at app launch via `registerCatalog(..., migrateImmediately: true)`. + ## Global Configuration The app demonstrates how to configure the `LocalData` library globally during startup in `SecureStorageSampleApp.swift`: diff --git a/SecureStorageSample/SecureStorageSampleApp.swift b/SecureStorageSample/SecureStorageSampleApp.swift index 92a94de..679a9dd 100644 --- a/SecureStorageSample/SecureStorageSampleApp.swift +++ b/SecureStorageSample/SecureStorageSampleApp.swift @@ -48,7 +48,7 @@ struct SecureStorageSampleApp: App { await StorageRouter.shared.updateStorageConfiguration(storageConfig) do { - try await StorageRouter.shared.registerCatalog(AppStorageCatalog.self) + try await StorageRouter.shared.registerCatalog(AppStorageCatalog.self, migrateImmediately: true) } catch { assertionFailure("Storage catalog registration failed: \(error)") } diff --git a/SecureStorageSample/Views/MigrationDemo.swift b/SecureStorageSample/Views/MigrationDemo.swift index 9b83980..d13b514 100644 --- a/SecureStorageSample/Views/MigrationDemo.swift +++ b/SecureStorageSample/Views/MigrationDemo.swift @@ -45,7 +45,17 @@ struct MigrationDemo: View { } } - Section("Step 3: Verify Cleanup") { + Section("Step 3: Proactive Sweep (Drain)") { + Text("Even if the modern key already has data, you can force a 'Sweep' of legacy sources. Try saving a NEW value to Legacy, then click Drain.") + .font(.caption) + + Button(action: runManualMigration) { + Label("Drain Migration Sources", systemImage: "arrow.up.circle.badge.clock") + } + .disabled(isLoading) + } + + Section("Step 4: Verify Cleanup") { Text("Check if the data was actually removed from UserDefaults after migration.") .font(.caption) @@ -101,6 +111,23 @@ struct MigrationDemo: View { } } + private func runManualMigration() { + isLoading = true + statusMessage = "Draining migration sources..." + Task { + do { + let key = StorageKeys.ModernMigrationDestinationKey() + try await StorageRouter.shared.migrate(for: key) + // Refresh modern value display + modernValue = try await StorageRouter.shared.get(key) + statusMessage = "✓ Proactive migration complete. Legacy data drained into Keychain." + } catch { + statusMessage = "Error: \(error.localizedDescription)" + } + isLoading = false + } + } + private func checkLegacyExists() { isLoading = true Task {