81 lines
3.6 KiB
Swift
81 lines
3.6 KiB
Swift
//
|
|
// SecureStorageSampleApp.swift
|
|
// SecureStorageSample
|
|
//
|
|
// Created by Matt Bruce on 1/13/26.
|
|
//
|
|
|
|
import SwiftUI
|
|
import LocalData
|
|
import SharedKit
|
|
|
|
@main
|
|
/// App entry point that centralizes LocalData configuration and storage key registration.
|
|
/// Keeping this bootstrap in one place makes the sample's storage behavior easy to audit.
|
|
struct SecureStorageSampleApp: App {
|
|
init() {
|
|
// Log derived identifiers up front so it's obvious which services the sample uses.
|
|
StorageServiceIdentifiers.logConfiguration()
|
|
// Spin up WCSession early so background sync opportunities aren't missed.
|
|
_ = WatchConnectivityService.shared
|
|
Task {
|
|
// 1. Global Encryption Configuration
|
|
// We isolate our library's master key in the Keychain by providing a specific service
|
|
// and account name. This prevents conflicts with other apps using the same library.
|
|
// We also set PBKDF2 iterations for consistent security across the app.
|
|
let config = EncryptionConfiguration(
|
|
masterKeyService: "SecureStorageSample",
|
|
masterKeyAccount: "AppMasterKey",
|
|
pbkdf2Iterations: 20_000
|
|
)
|
|
await StorageRouter.shared.updateEncryptionConfiguration(config)
|
|
|
|
// 2. Global Sync Configuration
|
|
// Overriding the default 100KB limit for automatic Watch sync to 50KB.
|
|
// This demonstrates how to tune performance for low-bandwidth scenarios.
|
|
let syncConfig = SyncConfiguration(maxAutoSyncSize: 50_000)
|
|
await StorageRouter.shared.updateSyncConfiguration(syncConfig)
|
|
|
|
// 3. Global File Storage Configuration
|
|
// We scope all our library files into a "SecureStorage" sub-directory
|
|
// underneath the standard Documents/Caches folders.
|
|
let fileConfig = FileStorageConfiguration(subDirectory: "SecureStorage")
|
|
await StorageRouter.shared.updateFileStorageConfiguration(fileConfig)
|
|
|
|
// 4. Global Storage Defaults
|
|
// Setting default identifiers for Keychain and App Groups.
|
|
// This allows keys to be defined more concisely without repeating these IDs.
|
|
let storageConfig = StorageConfiguration(
|
|
defaultKeychainService: StorageServiceIdentifiers.bundleIdentifier,
|
|
defaultAppGroupIdentifier: StorageServiceIdentifiers.appGroupIdentifier
|
|
)
|
|
await StorageRouter.shared.updateStorageConfiguration(storageConfig)
|
|
|
|
// Register keys once so LocalData can validate and migrate them consistently.
|
|
do {
|
|
try await StorageRouter.shared.registerCatalog(AppStorageCatalog(), migrateImmediately: true)
|
|
} catch {
|
|
assertionFailure("Storage catalog registration failed: \(error)")
|
|
}
|
|
// Provide external key material for the encrypted storage demo.
|
|
await StorageRouter.shared.registerKeyMaterialProvider(
|
|
ExternalKeyMaterialProvider(),
|
|
for: SampleKeyMaterialSources.external
|
|
)
|
|
// If a watch is paired, send the current syncable payloads on launch.
|
|
await StorageRouter.shared.syncRegisteredKeysIfNeeded()
|
|
}
|
|
#if DEBUG
|
|
// Disabled to keep console focused on sync logs.
|
|
// let report = StorageAuditReport.renderText(AppStorageCatalog())
|
|
// print(report)
|
|
#endif
|
|
}
|
|
|
|
var body: some Scene {
|
|
WindowGroup {
|
|
ContentView()
|
|
}
|
|
}
|
|
}
|