149 lines
5.8 KiB
Swift
149 lines
5.8 KiB
Swift
import SwiftUI
|
|
import LocalData
|
|
|
|
@MainActor
|
|
struct MigrationDemo: View {
|
|
@State private var legacyValue = ""
|
|
@State private var modernValue = ""
|
|
@State private var statusMessage = ""
|
|
@State private var isLoading = false
|
|
|
|
var body: some View {
|
|
Form {
|
|
Section("The Scenario") {
|
|
Text("Imagine you have an old version of the app that stored a User ID in plain UserDefaults. Now, you want to move it to the secure Keychain automatically without the user noticing.")
|
|
.font(.caption)
|
|
.foregroundStyle(Color.Text.secondary)
|
|
}
|
|
|
|
Section("Step 1: Setup Legacy Data") {
|
|
Text("First, save a value to the 'legacy' key in UserDefaults.")
|
|
.font(.caption)
|
|
.foregroundStyle(Color.Text.secondary)
|
|
|
|
TextField("Legacy Value", text: $legacyValue)
|
|
.textFieldStyle(.roundedBorder)
|
|
|
|
Button(action: saveToLegacy) {
|
|
Label("Save to Legacy (UserDefaults)", systemImage: "arrow.down.doc")
|
|
}
|
|
.disabled(legacyValue.isEmpty || isLoading)
|
|
}
|
|
|
|
Section("Step 2: Trigger Migration") {
|
|
Text("Now, attempt to load from the 'modern' Keychain key. It will automatically check the legacy key, move the data, and delete the old record using the new migration protocol.")
|
|
.font(.caption)
|
|
.foregroundStyle(Color.Text.secondary)
|
|
|
|
Button(action: loadFromModern) {
|
|
Label("Load from Modern (Keychain)", systemImage: "sparkles")
|
|
}
|
|
.disabled(isLoading)
|
|
|
|
if !modernValue.isEmpty {
|
|
LabeledContent("Migrated Value", value: modernValue)
|
|
.foregroundStyle(Color.Status.success)
|
|
.bold()
|
|
}
|
|
}
|
|
|
|
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 Force Migration.")
|
|
.font(.caption)
|
|
.foregroundStyle(Color.Text.secondary)
|
|
|
|
Button(action: runManualMigration) {
|
|
Label("Force Migration", 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)
|
|
.foregroundStyle(Color.Text.secondary)
|
|
|
|
Button(action: checkLegacyExists) {
|
|
Label("Check Legacy Exists?", systemImage: "magnifyingglass")
|
|
}
|
|
.disabled(isLoading)
|
|
}
|
|
|
|
if !statusMessage.isEmpty {
|
|
Section {
|
|
Text(statusMessage)
|
|
.font(.caption)
|
|
.foregroundStyle(statusMessage.contains("Error") ? Color.Status.error : Color.Status.info)
|
|
}
|
|
}
|
|
}
|
|
.navigationTitle("Data Migration")
|
|
.navigationBarTitleDisplayMode(.inline)
|
|
}
|
|
|
|
// MARK: - Actions
|
|
|
|
private func saveToLegacy() {
|
|
isLoading = true
|
|
Task {
|
|
do {
|
|
let key = StorageKeys.LegacyMigrationSourceKey()
|
|
try await StorageRouter.shared.set(legacyValue, for: key)
|
|
statusMessage = "✓ Saved '\(legacyValue)' to legacy UserDefaults"
|
|
} catch {
|
|
statusMessage = "Error: \(error.localizedDescription)"
|
|
}
|
|
isLoading = false
|
|
}
|
|
}
|
|
|
|
private func loadFromModern() {
|
|
isLoading = true
|
|
statusMessage = "Retrieving from Modern..."
|
|
Task {
|
|
do {
|
|
let key = StorageKeys.ModernMigrationDestinationKey()
|
|
let value = try await StorageRouter.shared.get(key)
|
|
modernValue = value
|
|
statusMessage = "✓ Success! Data migrated from UserDefaults to Keychain."
|
|
} catch StorageError.notFound {
|
|
statusMessage = "Modern key is empty (and no legacy data found)."
|
|
} catch {
|
|
statusMessage = "Error: \(error.localizedDescription)"
|
|
}
|
|
isLoading = false
|
|
}
|
|
}
|
|
|
|
private func runManualMigration() {
|
|
isLoading = true
|
|
statusMessage = "Running manual migration..."
|
|
Task {
|
|
do {
|
|
let key = StorageKeys.ModernMigrationDestinationKey()
|
|
_ = try await StorageRouter.shared.forceMigration(for: key)
|
|
// Refresh modern value display
|
|
modernValue = try await StorageRouter.shared.get(key)
|
|
statusMessage = "✓ Manual migration complete. Legacy data drained into Keychain."
|
|
} catch {
|
|
statusMessage = "Error: \(error.localizedDescription)"
|
|
}
|
|
isLoading = false
|
|
}
|
|
}
|
|
|
|
private func checkLegacyExists() {
|
|
isLoading = true
|
|
Task {
|
|
do {
|
|
let key = StorageKeys.LegacyMigrationSourceKey()
|
|
let exists = try await StorageRouter.shared.exists(key)
|
|
statusMessage = exists ? "⚠️ Legacy data STILL EXISTS in UserDefaults!" : "✅ Legacy data was successfully DELETED."
|
|
} catch {
|
|
statusMessage = "Error: \(error.localizedDescription)"
|
|
}
|
|
isLoading = false
|
|
}
|
|
}
|
|
}
|