SecureStorageSample/SecureStorageSample/Views/UserDefaultsDemo.swift
Matt Bruce 9a938b68c2 updated UserDefaults demo to not use AppVersionKey
Signed-off-by: Matt Bruce <mbrucedogs@gmail.com>
2026-01-23 14:05:35 -06:00

248 lines
8.2 KiB
Swift

//
// UserDefaultsDemo.swift
// SecureStorageSample
//
// Demonstrates UserDefaults storage with LocalData package.
//
import SwiftUI
import LocalData
@MainActor
/// Demonstrates typed UserDefaults keys, including App Group sharing.
struct UserDefaultsDemo: View {
@State private var inputText = ""
@State private var storedValue = ""
@State private var appGroupStoredValue = ""
@State private var statusMessage = ""
@State private var isLoading = false
@FocusState private var isInputFocused: Bool
var body: some View {
Form {
Section {
Text("UserDefaults stores simple data persistently. Great for preferences and small values.")
.font(.caption)
.foregroundStyle(.secondary)
}
Section("Store Value") {
TextField("Enter a value", text: $inputText)
.textFieldStyle(.roundedBorder)
.focused($isInputFocused)
Button(action: saveValue) {
HStack {
Image(systemName: "square.and.arrow.down")
Text("Save to UserDefaults")
}
}
.disabled(inputText.isEmpty || isLoading)
}
Section("Retrieve Value") {
Button(action: loadValue) {
HStack {
Image(systemName: "arrow.down.circle")
Text("Load from UserDefaults")
}
}
.disabled(isLoading)
if !storedValue.isEmpty {
HStack {
Text("Stored:")
Spacer()
Text(storedValue)
.foregroundStyle(.blue)
}
}
}
Section("Remove Value") {
Button(action: removeValue) {
HStack {
Image(systemName: "trash")
Text("Remove from UserDefaults")
}
}
.foregroundStyle(.red)
.disabled(isLoading)
}
Section("App Group UserDefaults") {
Text("Requires App Group entitlement and matching identifier in AppGroupConfiguration.")
.font(.caption)
.foregroundStyle(.secondary)
Button(action: saveAppGroupValue) {
HStack {
Image(systemName: "square.and.arrow.down")
Text("Save to App Group")
}
}
.disabled(inputText.isEmpty || isLoading)
Button(action: loadAppGroupValue) {
HStack {
Image(systemName: "arrow.down.circle")
Text("Load from App Group")
}
}
.disabled(isLoading)
Button(action: removeAppGroupValue) {
HStack {
Image(systemName: "trash")
Text("Remove from App Group")
}
}
.foregroundStyle(.red)
.disabled(isLoading)
if !appGroupStoredValue.isEmpty {
HStack {
Text("App Group Stored:")
Spacer()
Text(appGroupStoredValue)
.foregroundStyle(.blue)
}
}
}
if !statusMessage.isEmpty {
Section {
Text(statusMessage)
.font(.caption)
.foregroundStyle(statusMessage.contains("Error") ? .red : .green)
}
}
Section("Key Configuration") {
LabeledContent("Domain", value: "UserDefaults (standard)")
LabeledContent("Security", value: "None")
LabeledContent("Serializer", value: "JSON")
LabeledContent("Platform", value: "All")
LabeledContent("Sync Policy", value: "Automatic Small")
}
}
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItemGroup(placement: .keyboard) {
Spacer()
Button("Done") {
isInputFocused = false
}
}
}
}
/// Persists the current input value with the standard UserDefaults key.
private func saveValue() {
isLoading = true
Task {
do {
let key = StorageKey.userDefaultsDemoValue
try await StorageRouter.shared.set(inputText, for: key)
statusMessage = "✓ Saved successfully"
} catch {
statusMessage = "Error: \(error.localizedDescription)"
}
isLoading = false
}
}
/// Loads the standard UserDefaults value and reflects it in the UI.
private func loadValue() {
isLoading = true
Task {
do {
let key = StorageKey.userDefaultsDemoValue
let value = try await StorageRouter.shared.get(key)
storedValue = value
inputText = value // Sync to field
statusMessage = "✓ Loaded successfully"
} catch StorageError.notFound {
storedValue = ""
statusMessage = "No value stored yet"
} catch {
statusMessage = "Error: \(error.localizedDescription)"
}
isLoading = false
}
}
/// Removes the standard UserDefaults value to reset the demo.
private func removeValue() {
isLoading = true
Task {
do {
let key = StorageKey.userDefaultsDemoValue
try await StorageRouter.shared.remove(key)
storedValue = ""
statusMessage = "✓ Removed successfully"
} catch {
statusMessage = "Error: \(error.localizedDescription)"
}
isLoading = false
}
}
/// Saves the input value into the App Group UserDefaults container.
private func saveAppGroupValue() {
isLoading = true
Task {
do {
let key = StorageKey.appGroupUserDefaults
try await StorageRouter.shared.set(inputText, for: key)
statusMessage = "✓ App Group saved successfully"
} catch {
statusMessage = "Error: \(error.localizedDescription)"
}
isLoading = false
}
}
/// Loads the App Group value to demonstrate cross-target sharing.
private func loadAppGroupValue() {
isLoading = true
Task {
do {
let key = StorageKey.appGroupUserDefaults
let value = try await StorageRouter.shared.get(key)
appGroupStoredValue = value
inputText = value // Sync to field
statusMessage = "✓ App Group loaded successfully"
} catch StorageError.notFound {
appGroupStoredValue = ""
statusMessage = "No App Group value stored yet"
} catch {
statusMessage = "Error: \(error.localizedDescription)"
}
isLoading = false
}
}
/// Clears the App Group value to demonstrate cleanup behavior.
private func removeAppGroupValue() {
isLoading = true
Task {
do {
let key = StorageKey.appGroupUserDefaults
try await StorageRouter.shared.remove(key)
appGroupStoredValue = ""
statusMessage = "✓ App Group value removed"
} catch {
statusMessage = "Error: \(error.localizedDescription)"
}
isLoading = false
}
}
}
#Preview {
NavigationStack {
UserDefaultsDemo()
}
}