From 6db8ab5bc4800f78f2dcba59d056fe591b152b0d Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Wed, 14 Jan 2026 09:35:32 -0600 Subject: [PATCH] Add Credential, SampleLocationData, StorageKeys, PrivateNotesKey (+13 more); Remove Credential, SampleLocationData, StorageKeys, AppVersionKey (+13 more) --- README.md | 27 +- .../project.pbxproj | 4 + SecureStorgageSample/Models/Credential.swift | 8 + .../Models/SampleLocationData.swift | 8 + SecureStorgageSample/StorageKeys.swift | 271 ------------------ .../EncryptedFileSystem/PrivateNotesKey.swift | 19 ++ .../EncryptedFileSystem/SessionLogsKey.swift | 22 ++ .../FileSystem/CachedDataKey.swift | 17 ++ .../FileSystem/CustomEncodedKey.swift | 28 ++ .../FileSystem/SettingsPlistKey.swift | 17 ++ .../FileSystem/UserProfileFileKey.swift | 22 ++ .../StorageKeys/Keychain/APITokenKey.swift | 20 ++ .../StorageKeys/Keychain/CredentialsKey.swift | 22 ++ .../Keychain/LastLocationKey.swift | 20 ++ .../Platform/SyncableSettingKey.swift | 23 ++ .../Platform/WatchVibrationKey.swift | 18 ++ .../UserDefaults/AppVersionKey.swift | 20 ++ .../UserDefaults/UserPreferencesKey.swift | 20 ++ 18 files changed, 314 insertions(+), 272 deletions(-) create mode 100644 SecureStorgageSample/Models/Credential.swift create mode 100644 SecureStorgageSample/Models/SampleLocationData.swift delete mode 100644 SecureStorgageSample/StorageKeys.swift create mode 100644 SecureStorgageSample/StorageKeys/EncryptedFileSystem/PrivateNotesKey.swift create mode 100644 SecureStorgageSample/StorageKeys/EncryptedFileSystem/SessionLogsKey.swift create mode 100644 SecureStorgageSample/StorageKeys/FileSystem/CachedDataKey.swift create mode 100644 SecureStorgageSample/StorageKeys/FileSystem/CustomEncodedKey.swift create mode 100644 SecureStorgageSample/StorageKeys/FileSystem/SettingsPlistKey.swift create mode 100644 SecureStorgageSample/StorageKeys/FileSystem/UserProfileFileKey.swift create mode 100644 SecureStorgageSample/StorageKeys/Keychain/APITokenKey.swift create mode 100644 SecureStorgageSample/StorageKeys/Keychain/CredentialsKey.swift create mode 100644 SecureStorgageSample/StorageKeys/Keychain/LastLocationKey.swift create mode 100644 SecureStorgageSample/StorageKeys/Platform/SyncableSettingKey.swift create mode 100644 SecureStorgageSample/StorageKeys/Platform/WatchVibrationKey.swift create mode 100644 SecureStorgageSample/StorageKeys/UserDefaults/AppVersionKey.swift create mode 100644 SecureStorgageSample/StorageKeys/UserDefaults/UserPreferencesKey.swift diff --git a/README.md b/README.md index ce3d183..4185551 100644 --- a/README.md +++ b/README.md @@ -41,8 +41,19 @@ SharedPackage/ └── UserProfile.swift SecureStorgageSample/ ├── ContentView.swift # Tabbed navigation +├── Models/ +│ ├── Credential.swift +│ └── SampleLocationData.swift ├── StorageKeys.swift # 12 example key definitions +├── StorageKeys/ +│ ├── UserDefaults/ +│ ├── Keychain/ +│ ├── FileSystem/ +│ ├── EncryptedFileSystem/ +│ └── Platform/ ├── WatchOptimized.swift # Watch data models +├── Services/ +│ └── WatchConnectivityService.swift └── Views/ ├── UserDefaultsDemo.swift ├── KeychainDemo.swift @@ -52,8 +63,16 @@ SecureStorgageSample/ SecureStorageSample Watch App/ ├── SecureStorageSampleApp.swift ├── ContentView.swift +├── Models/ +│ └── UserProfile.swift +├── Protocols/ +│ └── WatchDataHandling.swift +├── State/ +│ └── WatchProfileStore.swift └── Services/ - └── WatchConnectivityService.swift + ├── WatchConnectivityService.swift + └── Handlers/ + └── UserProfileWatchHandler.swift ``` ## Storage Key Examples @@ -87,6 +106,12 @@ The app demonstrates various storage configurations: - [LocalData](../localPackages/LocalData) - Local package for typed secure storage - SharedKit - Local package for shared iOS/watch models and constants +## Notes + +- Storage keys are now split into one file per key and grouped by domain; platform-focused keys live in `StorageKeys/Platform` with comments calling out availability/sync focus. +- The shared model/constants live in `SharedPackage` (`SharedKit`) to keep the watch/iOS data contract centralized. +- The watch app uses a handler-based WatchConnectivity layer so new payload types can be added in `Services/Handlers` without bloating the main service. + ## License This sample is provided for demonstration purposes. diff --git a/SecureStorgageSample.xcodeproj/project.pbxproj b/SecureStorgageSample.xcodeproj/project.pbxproj index cccddb4..31925a6 100644 --- a/SecureStorgageSample.xcodeproj/project.pbxproj +++ b/SecureStorgageSample.xcodeproj/project.pbxproj @@ -9,6 +9,8 @@ /* Begin PBXBuildFile section */ EA179D562F17379800B1D54A /* LocalData in Frameworks */ = {isa = PBXBuildFile; productRef = EA179D552F17379800B1D54A /* LocalData */; }; EA65D70D2F17DDEB00C48466 /* SecureStorageSample Watch App.app in Embed Watch Content */ = {isa = PBXBuildFile; fileRef = EA65D6E52F17DD6700C48466 /* SecureStorageSample Watch App.app */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; + EA65D9442F17EAD800C48466 /* SharedKit in Frameworks */ = {isa = PBXBuildFile; productRef = EA65D7312F17DDEB00C48466 /* SharedKit */; }; + EA65D9452F17EAD800C48466 /* SharedKit in Frameworks */ = {isa = PBXBuildFile; productRef = EA65D7312F17DDEB00C48466 /* SharedKit */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -110,6 +112,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + EA65D9442F17EAD800C48466 /* SharedKit in Frameworks */, EA179D562F17379800B1D54A /* LocalData in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -118,6 +121,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + EA65D9452F17EAD800C48466 /* SharedKit in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/SecureStorgageSample/Models/Credential.swift b/SecureStorgageSample/Models/Credential.swift new file mode 100644 index 0000000..d9bcaec --- /dev/null +++ b/SecureStorgageSample/Models/Credential.swift @@ -0,0 +1,8 @@ +import Foundation + +/// Simple credential model for keychain storage demo. +nonisolated +struct Credential: Codable, Sendable { + let username: String + let password: String +} diff --git a/SecureStorgageSample/Models/SampleLocationData.swift b/SecureStorgageSample/Models/SampleLocationData.swift new file mode 100644 index 0000000..8e7f00a --- /dev/null +++ b/SecureStorgageSample/Models/SampleLocationData.swift @@ -0,0 +1,8 @@ +import Foundation + +/// Location data model. +nonisolated +struct SampleLocationData: Codable, Sendable { + let lat: Double + let lon: Double +} diff --git a/SecureStorgageSample/StorageKeys.swift b/SecureStorgageSample/StorageKeys.swift deleted file mode 100644 index 705b1d9..0000000 --- a/SecureStorgageSample/StorageKeys.swift +++ /dev/null @@ -1,271 +0,0 @@ -// -// StorageKeys.swift -// SecureStorgageSample -// -// Example StorageKey implementations demonstrating all variations -// supported by the LocalData package. -// - -import Foundation -import LocalData -import SharedKit - -// MARK: - Sample Data Models - -/// Simple credential model for keychain storage demo. -nonisolated -struct Credential: Codable, Sendable { - let username: String - let password: String -} - -/// Location data model. -nonisolated -struct SampleLocationData: Codable, Sendable { - let lat: Double - let lon: Double -} - -// MARK: - UserDefaults Keys - -extension StorageKeys { - - /// Stores the app version in standard UserDefaults. - /// - Domain: UserDefaults (standard) - /// - Security: None - /// - Sync: Automatic for small data - struct AppVersionKey: StorageKey { - typealias Value = String - - let name = "last_app_version" - let domain: StorageDomain = .userDefaults(suite: nil) - let security: SecurityPolicy = .none - let serializer: Serializer = .json - let owner = "SampleApp" - let availability: PlatformAvailability = .all - let syncPolicy: SyncPolicy = .automaticSmall - } - - /// Stores user preferences in a custom suite. - /// - Domain: UserDefaults (custom suite) - /// - Security: None - /// - Sync: Never - struct UserPreferencesKey: StorageKey { - typealias Value = [String: AnyCodable] - - let name = "user_preferences" - let domain: StorageDomain = .userDefaults(suite: "group.com.example.securestorage") - let security: SecurityPolicy = .none - let serializer: Serializer<[String: AnyCodable]> = .json - let owner = "SampleApp" - let availability: PlatformAvailability = .all - let syncPolicy: SyncPolicy = .never - } -} - -// MARK: - Keychain Keys - -extension StorageKeys { - - /// Stores user credentials securely in keychain. - /// Configurable accessibility and access control. - struct CredentialsKey: StorageKey { - typealias Value = Credential - - let name = "user_credentials" - let domain: StorageDomain = .keychain(service: "com.example.securestorage") - let security: SecurityPolicy - let serializer: Serializer = .json - let owner = "SampleApp" - let availability: PlatformAvailability = .phoneOnly - let syncPolicy: SyncPolicy = .never - - init(accessibility: KeychainAccessibility = .afterFirstUnlock, accessControl: KeychainAccessControl? = nil) { - self.security = .keychain(accessibility: accessibility, accessControl: accessControl) - } - } - - /// Stores sensitive location data in keychain with biometric protection. - struct LastLocationKey: StorageKey { - typealias Value = SampleLocationData - - let name = "last_known_location" - let domain: StorageDomain = .keychain(service: "com.example.app.security") - let security: SecurityPolicy = .keychain( - accessibility: .afterFirstUnlock, - accessControl: .userPresence - ) - let serializer: Serializer = .json - let owner = "SampleApp" - let availability: PlatformAvailability = .phoneOnly - let syncPolicy: SyncPolicy = .never - } - - /// Stores API token in keychain. - struct APITokenKey: StorageKey { - typealias Value = String - - let name = "api_token" - let domain: StorageDomain = .keychain(service: "com.example.securestorage.api") - let security: SecurityPolicy = .keychain( - accessibility: .whenUnlockedThisDeviceOnly, - accessControl: nil - ) - let serializer: Serializer = .json - let owner = "SampleApp" - let availability: PlatformAvailability = .phoneOnly - let syncPolicy: SyncPolicy = .never - } -} - -// MARK: - File System Keys - -extension StorageKeys { - - /// Stores user profile as JSON file in documents. - struct UserProfileFileKey: StorageKey { - typealias Value = UserProfile - - let name = UserProfile.storageKeyName - let domain: StorageDomain - let security: SecurityPolicy = .none - let serializer: Serializer = .json - let owner = "SampleApp" - let availability: PlatformAvailability = .phoneWithWatchSync - let syncPolicy: SyncPolicy = .automaticSmall - - init(directory: FileDirectory = .documents) { - self.domain = .fileSystem(directory: directory) - } - } - - /// Stores cached data files. - struct CachedDataKey: StorageKey { - typealias Value = Data - - let name = "cached_data.bin" - let domain: StorageDomain = .fileSystem(directory: .caches) - let security: SecurityPolicy = .none - let serializer: Serializer = .data - let owner = "SampleApp" - let availability: PlatformAvailability = .all - let syncPolicy: SyncPolicy = .never - } - - /// Stores settings as property list. - struct SettingsPlistKey: StorageKey { - typealias Value = [String: AnyCodable] - - let name = "settings.plist" - let domain: StorageDomain = .fileSystem(directory: .documents) - let security: SecurityPolicy = .none - let serializer: Serializer<[String: AnyCodable]> = .plist - let owner = "SampleApp" - let availability: PlatformAvailability = .all - let syncPolicy: SyncPolicy = .never - } -} - -// MARK: - Encrypted File System Keys - -extension StorageKeys { - - /// Stores session logs with full encryption. - /// Configurable PBKDF2 iterations. - struct SessionLogsKey: StorageKey { - typealias Value = [String] - - let name = "session_logs.json" - let domain: StorageDomain = .encryptedFileSystem(directory: .caches) - let security: SecurityPolicy - let serializer: Serializer<[String]> = .json - let owner = "SampleApp" - let availability: PlatformAvailability = .phoneOnly - let syncPolicy: SyncPolicy = .never - - init(iterations: Int = 10_000) { - self.security = .encrypted(.aes256(keyDerivation: .pbkdf2(iterations: iterations))) - } - } - - /// Stores private notes with encryption. - struct PrivateNotesKey: StorageKey { - typealias Value = String - - let name = "private_notes.enc" - let domain: StorageDomain = .encryptedFileSystem(directory: .documents) - let security: SecurityPolicy = .encrypted( - .aes256(keyDerivation: .pbkdf2(iterations: 50_000)) - ) - let serializer: Serializer = .json - let owner = "SampleApp" - let availability: PlatformAvailability = .phoneOnly - let syncPolicy: SyncPolicy = .never - } -} - -// MARK: - Platform-Specific Keys - -extension StorageKeys { - - /// Watch-only setting for vibration. - struct WatchVibrationKey: StorageKey { - typealias Value = Bool - - let name = "watch_vibration_enabled" - let domain: StorageDomain = .userDefaults(suite: nil) - let security: SecurityPolicy = .none - let serializer: Serializer = .json - let owner = "SampleApp" - let availability: PlatformAvailability = .watchOnly - let syncPolicy: SyncPolicy = .never - } - - /// Syncable setting with configurable platform and sync policy. - struct SyncableSettingKey: StorageKey { - typealias Value = String - - let name = "syncable_setting" - let domain: StorageDomain = .userDefaults(suite: nil) - let security: SecurityPolicy = .none - let serializer: Serializer = .json - let owner = "SampleApp" - let availability: PlatformAvailability - let syncPolicy: SyncPolicy - - init(availability: PlatformAvailability = .all, syncPolicy: SyncPolicy = .never) { - self.availability = availability - self.syncPolicy = syncPolicy - } - } -} - -// MARK: - Custom Serializer Example - -extension StorageKeys { - - /// Example using custom serializer for specialized encoding. - struct CustomEncodedKey: StorageKey { - typealias Value = String - - let name = "custom_encoded" - let domain: StorageDomain = .fileSystem(directory: .documents) - let security: SecurityPolicy = .none - let serializer: Serializer = .custom( - encode: { value in - // Example: Base64 encode - Data(value.utf8).base64EncodedData() - }, - decode: { data in - guard let decoded = Data(base64Encoded: data), - let string = String(data: decoded, encoding: .utf8) else { - throw StorageError.deserializationFailed - } - return string - } - ) - let owner = "SampleApp" - let availability: PlatformAvailability = .all - let syncPolicy: SyncPolicy = .never - } -} diff --git a/SecureStorgageSample/StorageKeys/EncryptedFileSystem/PrivateNotesKey.swift b/SecureStorgageSample/StorageKeys/EncryptedFileSystem/PrivateNotesKey.swift new file mode 100644 index 0000000..8ea5c8d --- /dev/null +++ b/SecureStorgageSample/StorageKeys/EncryptedFileSystem/PrivateNotesKey.swift @@ -0,0 +1,19 @@ +import Foundation +import LocalData + +extension StorageKeys { + /// Stores private notes with encryption. + struct PrivateNotesKey: StorageKey { + typealias Value = String + + let name = "private_notes.enc" + let domain: StorageDomain = .encryptedFileSystem(directory: .documents) + let security: SecurityPolicy = .encrypted( + .aes256(keyDerivation: .pbkdf2(iterations: 50_000)) + ) + let serializer: Serializer = .json + let owner = "SampleApp" + let availability: PlatformAvailability = .phoneOnly + let syncPolicy: SyncPolicy = .never + } +} diff --git a/SecureStorgageSample/StorageKeys/EncryptedFileSystem/SessionLogsKey.swift b/SecureStorgageSample/StorageKeys/EncryptedFileSystem/SessionLogsKey.swift new file mode 100644 index 0000000..39cc1db --- /dev/null +++ b/SecureStorgageSample/StorageKeys/EncryptedFileSystem/SessionLogsKey.swift @@ -0,0 +1,22 @@ +import Foundation +import LocalData + +extension StorageKeys { + /// Stores session logs with full encryption. + /// Configurable PBKDF2 iterations. + struct SessionLogsKey: StorageKey { + typealias Value = [String] + + let name = "session_logs.json" + let domain: StorageDomain = .encryptedFileSystem(directory: .caches) + let security: SecurityPolicy + let serializer: Serializer<[String]> = .json + let owner = "SampleApp" + let availability: PlatformAvailability = .phoneOnly + let syncPolicy: SyncPolicy = .never + + init(iterations: Int = 10_000) { + self.security = .encrypted(.aes256(keyDerivation: .pbkdf2(iterations: iterations))) + } + } +} diff --git a/SecureStorgageSample/StorageKeys/FileSystem/CachedDataKey.swift b/SecureStorgageSample/StorageKeys/FileSystem/CachedDataKey.swift new file mode 100644 index 0000000..459d621 --- /dev/null +++ b/SecureStorgageSample/StorageKeys/FileSystem/CachedDataKey.swift @@ -0,0 +1,17 @@ +import Foundation +import LocalData + +extension StorageKeys { + /// Stores cached data files. + struct CachedDataKey: StorageKey { + typealias Value = Data + + let name = "cached_data.bin" + let domain: StorageDomain = .fileSystem(directory: .caches) + let security: SecurityPolicy = .none + let serializer: Serializer = .data + let owner = "SampleApp" + let availability: PlatformAvailability = .all + let syncPolicy: SyncPolicy = .never + } +} diff --git a/SecureStorgageSample/StorageKeys/FileSystem/CustomEncodedKey.swift b/SecureStorgageSample/StorageKeys/FileSystem/CustomEncodedKey.swift new file mode 100644 index 0000000..dcebdb7 --- /dev/null +++ b/SecureStorgageSample/StorageKeys/FileSystem/CustomEncodedKey.swift @@ -0,0 +1,28 @@ +import Foundation +import LocalData + +extension StorageKeys { + /// Example using custom serializer for specialized encoding. + struct CustomEncodedKey: StorageKey { + typealias Value = String + + let name = "custom_encoded" + let domain: StorageDomain = .fileSystem(directory: .documents) + let security: SecurityPolicy = .none + let serializer: Serializer = .custom( + encode: { value in + Data(value.utf8).base64EncodedData() + }, + decode: { data in + guard let decoded = Data(base64Encoded: data), + let string = String(data: decoded, encoding: .utf8) else { + throw StorageError.deserializationFailed + } + return string + } + ) + let owner = "SampleApp" + let availability: PlatformAvailability = .all + let syncPolicy: SyncPolicy = .never + } +} diff --git a/SecureStorgageSample/StorageKeys/FileSystem/SettingsPlistKey.swift b/SecureStorgageSample/StorageKeys/FileSystem/SettingsPlistKey.swift new file mode 100644 index 0000000..9dae0ee --- /dev/null +++ b/SecureStorgageSample/StorageKeys/FileSystem/SettingsPlistKey.swift @@ -0,0 +1,17 @@ +import Foundation +import LocalData + +extension StorageKeys { + /// Stores settings as property list. + struct SettingsPlistKey: StorageKey { + typealias Value = [String: AnyCodable] + + let name = "settings.plist" + let domain: StorageDomain = .fileSystem(directory: .documents) + let security: SecurityPolicy = .none + let serializer: Serializer<[String: AnyCodable]> = .plist + let owner = "SampleApp" + let availability: PlatformAvailability = .all + let syncPolicy: SyncPolicy = .never + } +} diff --git a/SecureStorgageSample/StorageKeys/FileSystem/UserProfileFileKey.swift b/SecureStorgageSample/StorageKeys/FileSystem/UserProfileFileKey.swift new file mode 100644 index 0000000..a48881d --- /dev/null +++ b/SecureStorgageSample/StorageKeys/FileSystem/UserProfileFileKey.swift @@ -0,0 +1,22 @@ +import Foundation +import LocalData +import SharedKit + +extension StorageKeys { + /// Stores user profile as JSON file in documents. + struct UserProfileFileKey: StorageKey { + typealias Value = UserProfile + + let name = UserProfile.storageKeyName + let domain: StorageDomain + let security: SecurityPolicy = .none + let serializer: Serializer = .json + let owner = "SampleApp" + let availability: PlatformAvailability = .phoneWithWatchSync + let syncPolicy: SyncPolicy = .automaticSmall + + init(directory: FileDirectory = .documents) { + self.domain = .fileSystem(directory: directory) + } + } +} diff --git a/SecureStorgageSample/StorageKeys/Keychain/APITokenKey.swift b/SecureStorgageSample/StorageKeys/Keychain/APITokenKey.swift new file mode 100644 index 0000000..db192e1 --- /dev/null +++ b/SecureStorgageSample/StorageKeys/Keychain/APITokenKey.swift @@ -0,0 +1,20 @@ +import Foundation +import LocalData + +extension StorageKeys { + /// Stores API token in keychain. + struct APITokenKey: StorageKey { + typealias Value = String + + let name = "api_token" + let domain: StorageDomain = .keychain(service: "com.example.securestorage.api") + let security: SecurityPolicy = .keychain( + accessibility: .whenUnlockedThisDeviceOnly, + accessControl: nil + ) + let serializer: Serializer = .json + let owner = "SampleApp" + let availability: PlatformAvailability = .phoneOnly + let syncPolicy: SyncPolicy = .never + } +} diff --git a/SecureStorgageSample/StorageKeys/Keychain/CredentialsKey.swift b/SecureStorgageSample/StorageKeys/Keychain/CredentialsKey.swift new file mode 100644 index 0000000..a2a280d --- /dev/null +++ b/SecureStorgageSample/StorageKeys/Keychain/CredentialsKey.swift @@ -0,0 +1,22 @@ +import Foundation +import LocalData + +extension StorageKeys { + /// Stores user credentials securely in keychain. + /// Configurable accessibility and access control. + struct CredentialsKey: StorageKey { + typealias Value = Credential + + let name = "user_credentials" + let domain: StorageDomain = .keychain(service: "com.example.securestorage") + let security: SecurityPolicy + let serializer: Serializer = .json + let owner = "SampleApp" + let availability: PlatformAvailability = .phoneOnly + let syncPolicy: SyncPolicy = .never + + init(accessibility: KeychainAccessibility = .afterFirstUnlock, accessControl: KeychainAccessControl? = nil) { + self.security = .keychain(accessibility: accessibility, accessControl: accessControl) + } + } +} diff --git a/SecureStorgageSample/StorageKeys/Keychain/LastLocationKey.swift b/SecureStorgageSample/StorageKeys/Keychain/LastLocationKey.swift new file mode 100644 index 0000000..26f5360 --- /dev/null +++ b/SecureStorgageSample/StorageKeys/Keychain/LastLocationKey.swift @@ -0,0 +1,20 @@ +import Foundation +import LocalData + +extension StorageKeys { + /// Stores sensitive location data in keychain with biometric protection. + struct LastLocationKey: StorageKey { + typealias Value = SampleLocationData + + let name = "last_known_location" + let domain: StorageDomain = .keychain(service: "com.example.app.security") + let security: SecurityPolicy = .keychain( + accessibility: .afterFirstUnlock, + accessControl: .userPresence + ) + let serializer: Serializer = .json + let owner = "SampleApp" + let availability: PlatformAvailability = .phoneOnly + let syncPolicy: SyncPolicy = .never + } +} diff --git a/SecureStorgageSample/StorageKeys/Platform/SyncableSettingKey.swift b/SecureStorgageSample/StorageKeys/Platform/SyncableSettingKey.swift new file mode 100644 index 0000000..5feef01 --- /dev/null +++ b/SecureStorgageSample/StorageKeys/Platform/SyncableSettingKey.swift @@ -0,0 +1,23 @@ +import Foundation +import LocalData + +extension StorageKeys { + /// Syncable setting with configurable platform and sync policy. + /// Grouped under Platform to highlight availability/sync behavior. + struct SyncableSettingKey: StorageKey { + typealias Value = String + + let name = "syncable_setting" + let domain: StorageDomain = .userDefaults(suite: nil) + let security: SecurityPolicy = .none + let serializer: Serializer = .json + let owner = "SampleApp" + let availability: PlatformAvailability + let syncPolicy: SyncPolicy + + init(availability: PlatformAvailability = .all, syncPolicy: SyncPolicy = .never) { + self.availability = availability + self.syncPolicy = syncPolicy + } + } +} diff --git a/SecureStorgageSample/StorageKeys/Platform/WatchVibrationKey.swift b/SecureStorgageSample/StorageKeys/Platform/WatchVibrationKey.swift new file mode 100644 index 0000000..2eff968 --- /dev/null +++ b/SecureStorgageSample/StorageKeys/Platform/WatchVibrationKey.swift @@ -0,0 +1,18 @@ +import Foundation +import LocalData + +extension StorageKeys { + /// Watch-only setting for vibration. + /// Grouped under Platform to highlight watch-only availability. + struct WatchVibrationKey: StorageKey { + typealias Value = Bool + + let name = "watch_vibration_enabled" + let domain: StorageDomain = .userDefaults(suite: nil) + let security: SecurityPolicy = .none + let serializer: Serializer = .json + let owner = "SampleApp" + let availability: PlatformAvailability = .watchOnly + let syncPolicy: SyncPolicy = .never + } +} diff --git a/SecureStorgageSample/StorageKeys/UserDefaults/AppVersionKey.swift b/SecureStorgageSample/StorageKeys/UserDefaults/AppVersionKey.swift new file mode 100644 index 0000000..3e9ed82 --- /dev/null +++ b/SecureStorgageSample/StorageKeys/UserDefaults/AppVersionKey.swift @@ -0,0 +1,20 @@ +import Foundation +import LocalData + +extension StorageKeys { + /// Stores the app version in standard UserDefaults. + /// - Domain: UserDefaults (standard) + /// - Security: None + /// - Sync: Automatic for small data + struct AppVersionKey: StorageKey { + typealias Value = String + + let name = "last_app_version" + let domain: StorageDomain = .userDefaults(suite: nil) + let security: SecurityPolicy = .none + let serializer: Serializer = .json + let owner = "SampleApp" + let availability: PlatformAvailability = .all + let syncPolicy: SyncPolicy = .automaticSmall + } +} diff --git a/SecureStorgageSample/StorageKeys/UserDefaults/UserPreferencesKey.swift b/SecureStorgageSample/StorageKeys/UserDefaults/UserPreferencesKey.swift new file mode 100644 index 0000000..c530563 --- /dev/null +++ b/SecureStorgageSample/StorageKeys/UserDefaults/UserPreferencesKey.swift @@ -0,0 +1,20 @@ +import Foundation +import LocalData + +extension StorageKeys { + /// Stores user preferences in a custom suite. + /// - Domain: UserDefaults (custom suite) + /// - Security: None + /// - Sync: Never + struct UserPreferencesKey: StorageKey { + typealias Value = [String: AnyCodable] + + let name = "user_preferences" + let domain: StorageDomain = .userDefaults(suite: "group.com.example.securestorage") + let security: SecurityPolicy = .none + let serializer: Serializer<[String: AnyCodable]> = .json + let owner = "SampleApp" + let availability: PlatformAvailability = .all + let syncPolicy: SyncPolicy = .never + } +}