### Retrieve Credentials from Keychain Source: https://context7.com/stanfordspezi/spezistorage/llms.txt Retrieves the first matching `Credentials` entry for a given tag, optionally filtered by username. Returns `nil` if not found. Use `retrieveAllCredentials` to get all entries for a tag. ```swift func loadCredentials(_ keychainStorage: KeychainStorage) { do { if let credentials = try keychainStorage.retrieveCredentials( withUsername: "alice@stanford.edu", for: .stanfordSUNet ) { print("Username: \(credentials.username)") print("Password: \(credentials.password)") print("Synchronized: \(credentials.synchronizable)") } else { print("No credentials found") } } catch { print("Retrieval failed: \(error)") } } ``` ```swift // Retrieve all credentials for a tag regardless of username: func loadAllCredentials(_ keychainStorage: KeychainStorage) throws -> [Credentials] { try keychainStorage.retrieveAllCredentials(withUsername: nil, for: .stanfordSUNet) } ``` -------------------------------- ### Retrieve and Use Keys with KeychainStorage Source: https://context7.com/stanfordspezi/spezistorage/llms.txt Demonstrates retrieving private and public keys from KeychainStorage and using them for encryption and decryption. Ensure keys exist before attempting to use them. ```swift func retrieveKeys(_ keychainStorage: KeychainStorage) throws { guard let privateKey = try keychainStorage.retrievePrivateKey(for: .encryptionKey) else { print("Private key not found, creating...") try keychainStorage.createKey(for: .encryptionKey) return } guard let publicKey = try keychainStorage.retrievePublicKey(for: .encryptionKey) else { print("Public key unavailable") return } // Use keys for encryption/decryption with SecKey APIs let algorithm = SecKeyAlgorithm.eciesEncryptionCofactorX963SHA256AESGCM let plaintext = Data("Hello, Spezi!".utf8) var encryptError: Unmanaged? if let encrypted = SecKeyCreateEncryptedData(publicKey, algorithm, plaintext as CFData, &encryptError) as Data? { print("Encrypted \(encrypted.count) bytes") var decryptError: Unmanaged? if let decrypted = SecKeyCreateDecryptedData(privateKey, algorithm, encrypted as CFData, &decryptError) as Data? { print("Decrypted: \(String(decoding: decrypted, as: UTF8.self))") // Output: Decrypted: Hello, Spezi! } } } ``` -------------------------------- ### KeychainStorage.createKey(for:) Source: https://context7.com/stanfordspezi/spezistorage/llms.txt Creates a new EC key pair and stores it in the keychain or Secure Enclave. Returns the private SecKey. Throws if the Secure Enclave is unavailable or key creation fails. ```APIDOC ## KeychainStorage.createKey(for:) ### Description Creates a new EC key pair and stores it in the keychain or Secure Enclave. Returns the private `SecKey`. Throws if the Secure Enclave is unavailable or key creation fails. ### Method ```swift func createKey(for tag: CryptographicKeyTag) throws -> SecKey ``` ### Parameters #### Path Parameters - **tag** (CryptographicKeyTag) - Required - The tag identifying the type and storage location of the key to create. ### Response #### Success Response (200) - **SecKey** - The newly created private key. ### Error Handling - **KeychainError.failedToCreateKeyPair(tag:storage:)**: Thrown if key pair creation fails. Specific error includes `.secureEnclaveNotAvailable` if the Secure Enclave is not accessible. ### Request Example ```swift do { let privateKey = try keychainStorage.createKey(for: .encryptionKey) print("Key created: \(privateKey)") } catch KeychainStorage.KeychainError.failedToCreateKeyPair(let tag, .secureEnclaveNotAvailable) { print("Secure Enclave not available for tag: \(tag.tagValue)") } catch { print("Key creation failed: \(error)") } ``` ``` -------------------------------- ### Configure LocalStorage in SpeziAppDelegate Source: https://github.com/stanfordspezi/spezistorage/blob/main/Sources/SpeziLocalStorage/SpeziLocalStorage.docc/SpeziLocalStorage.md Integrate the LocalStorage module into your Spezi application by adding it to the `Configuration` within your `SpeziAppDelegate`. Ensure Spezi is set up correctly before adding this module. ```swift import Spezi import SpeziLocalStorage class ExampleDelegate: SpeziAppDelegate { override var configuration: Configuration { Configuration { LocalStorage() // ... } } } ``` -------------------------------- ### Configure Spezi Storage Modules Source: https://github.com/stanfordspezi/spezistorage/blob/main/README.md Configure LocalStorage and KeychainStorage modules within the SpeziAppDelegate. Ensure Keychain Access Groups entitlement is added for macOS. ```swift import Spezi import SpeziLocalStorage import SpeziKeychainStorage class ExampleDelegate: SpeziAppDelegate { override var configuration: Configuration { Configuration { LocalStorage() KeychainStorage() // ... } } } ``` -------------------------------- ### Configure KeychainStorage in SpeziAppDelegate Source: https://github.com/stanfordspezi/spezistorage/blob/main/Sources/SpeziKeychainStorage/KeychainStorage.docc/KeychainStorage.md Integrate KeychainStorage into your application's configuration by adding it to the SpeziAppDelegate's configuration. Ensure Spezi is set up and consider macOS-specific entitlements if applicable. ```swift import Spezi import SpeziKeychainStorage class ExampleDelegate: SpeziAppDelegate { override var configuration: Configuration { Configuration { KeychainStorage() // ... } } } ``` -------------------------------- ### Standalone KeychainStorage Usage Source: https://context7.com/stanfordspezi/spezistorage/llms.txt Demonstrates using KeychainStorage outside of the Spezi framework for storing and retrieving credentials. Requires importing SpeziKeychainStorage. ```swift import SpeziKeychainStorage // Create a standalone instance let keychainStorage = KeychainStorage() // Define a tag let tag = CredentialsTag.internetPassword(forServer: "api.example.com", storage: .keychain) // Store credentials try keychainStorage.store(Credentials(username: "user@example.com", password: "token123"), for: tag) // Retrieve credentials if let creds = try keychainStorage.retrieveCredentials(withUsername: "user@example.com", for: tag) { print("Token: \(creds.password)") } // List all internet credentials for a server let allCreds = try keychainStorage.retrieveAllInternetCredentials(forServer: "api.example.com") print("Found \(allCreds.count) credential(s)") // Clean up try keychainStorage.deleteCredentials(withUsername: "user@example.com", for: tag) ``` -------------------------------- ### Standalone KeychainStorage Usage Source: https://context7.com/stanfordspezi/spezistorage/llms.txt Demonstrates the standalone usage of KeychainStorage outside the Spezi module system for storing and retrieving credentials. ```APIDOC ## Standalone KeychainStorage Usage (Outside Spezi) ### Description `KeychainStorage` can be used independently without the Spezi module system, making it suitable as a lightweight keychain wrapper in any Swift app. ### Methods - **store(_:for:)**: Stores credentials for a given tag. - **retrieveCredentials(withUsername:for:)**: Retrieves credentials matching the username and tag. - **retrieveAllInternetCredentials(forServer:)**: Retrieves all internet credentials for a specific server. - **deleteCredentials(withUsername:for:)**: Deletes credentials matching the username and tag. ### Parameters #### Path Parameters - **credentials** (`Credentials`) - Required - The credentials object to store. - **tag** (`CredentialsTag`) - Required - The tag identifying the storage location for the credentials. - **username** (`String`) - Required - The username associated with the credentials to retrieve or delete. - **server** (`String`) - Required - The server associated with the internet credentials to retrieve. ### Request Example ```swift import SpeziKeychainStorage let keychainStorage = KeychainStorage() let tag = CredentialsTag.internetPassword(forServer: "api.example.com", storage: .keychain) try keychainStorage.store(Credentials(username: "user@example.com", password: "token123"), for: tag) if let creds = try keychainStorage.retrieveCredentials(withUsername: "user@example.com", for: tag) { print("Token: \(creds.password)") } let allCreds = try keychainStorage.retrieveAllInternetCredentials(forServer: "api.example.com") print("Found \(allCreds.count) credential(s)") try keychainStorage.deleteCredentials(withUsername: "user@example.com", for: tag) ``` ``` -------------------------------- ### Register Spezi Storage Modules Source: https://context7.com/stanfordspezi/spezistorage/llms.txt Register LocalStorage and KeychainStorage as Spezi modules in your app delegate. Inject them into SwiftUI views using @Environment. ```swift import Spezi import SpeziLocalStorage import SpeziKeychainStorage // App delegate configuration class ExampleDelegate: SpeziAppDelegate { override var configuration: Configuration { Configuration { LocalStorage() KeychainStorage() } } } // Accessing modules in a SwiftUI view struct ExampleView: View { @Environment(LocalStorage.self) var localStorage @Environment(KeychainStorage.self) var keychainStorage var body: some View { Text("Storage ready") } } ``` -------------------------------- ### LocalStorage.delete(_:) and LocalStorage.deleteAll() Source: https://context7.com/stanfordspezi/spezistorage/llms.txt Removes entries from local storage. `delete(_:)` removes a single entry by key, `deleteAll()` removes all entries, and `deleteAll(where:)` removes entries based on a predicate. ```APIDOC ## LocalStorage.delete(_:) and deleteAll() ### Description `delete(_:)` removes a single entry; `deleteAll()` wipes the entire local storage directory; `deleteAll(where:)` selectively removes entries by raw key predicate. ### Parameters #### Path Parameters - **key** (`LocalStorageKey`) - The key of the entry to delete (for `delete(_:)`). - **predicate** (`(String) -> Bool`) - A closure that returns `true` for keys to be deleted (for `deleteAll(where:)`). ### Request Example ```swift func deleteEntry(_ localStorage: LocalStorage) { do { try localStorage.delete(.healthRecord) print("Entry deleted") } catch { print("Deletion failed: \(error)") // Possible error: LocalStorageError.deletionNotPossible } } // Remove all entries func wipeAll(_ localStorage: LocalStorage) { try? localStorage.deleteAll() } // Remove only entries whose key contains "temp" func wipeTempEntries(_ localStorage: LocalStorage) { try? localStorage.deleteAll { rawKey in rawKey.contains("temp") } } ``` ``` -------------------------------- ### Store and Retrieve Credentials using KeychainStorage Source: https://github.com/stanfordspezi/spezistorage/blob/main/Sources/SpeziKeychainStorage/KeychainStorage.docc/KeychainStorage.md Store credentials (username and password) in the keychain using the `store` method and retrieve them using `retrieveCredentials`. Ensure you provide the correct username and tag. ```swift try keychainStorage.store( Credentials(username: "lukas", password: "isThisSecure?123"), for: .stanfordSUNet ) // retrieval: if let credentials = try keychainStorage.retrieveCredentials(withUsername: "lukas", for: .stanfordSUNet) { // ... } ``` -------------------------------- ### Load Data from LocalStorage Source: https://github.com/stanfordspezi/spezistorage/blob/main/Sources/SpeziLocalStorage/SpeziLocalStorage.docc/SpeziLocalStorage.md Retrieve elements conforming to `Codable` from local storage using the `load` method. Ensure to handle potential read errors. ```swift do { let storedNote = try localStorage.load(.note) // Do something with `storedNote`. } catch { // Handle read errors ... } ``` -------------------------------- ### Define Custom Internet and Generic Credentials Tags Source: https://github.com/stanfordspezi/spezistorage/blob/main/Sources/SpeziKeychainStorage/KeychainStorage.docc/KeychainStorage.md Define custom tags for storing internet and generic credentials. These tags specify the server or service associated with the credentials and the storage behavior (e.g., synchronizable). ```swift extension CredentialsTag { static let stanfordSUNet = Self.internetPassword( forServer: "stanford.edu", storage: .keychainSynchronizable ) static let syncCredentials = Self.genericPassword( forService: "my-internal-sync-service", storage: .keychainSynchronizable ) } ``` -------------------------------- ### KeychainStorage.retrieveCredentials(withUsername:for:) Source: https://context7.com/stanfordspezi/spezistorage/llms.txt Retrieves the first matching Credentials entry for a given tag, optionally filtered by username. Returns nil if not found. ```APIDOC ## KeychainStorage.retrieveCredentials(withUsername:for:) ### Description Retrieves the first matching `Credentials` entry for a given tag, optionally filtered by username. Returns `nil` if not found. ### Method ```swift func retrieveCredentials(withUsername username: String? = nil, for tag: CredentialsTag) throws -> Credentials? ``` ### Parameters #### Path Parameters - **username** (String?) - Optional - The username to filter credentials by. If `nil`, all credentials for the tag are considered. - **tag** (CredentialsTag) - Required - The tag to retrieve credentials from. ### Response #### Success Response (200) - **Credentials?** - The retrieved credentials object, or `nil` if not found. ### Request Example ```swift if let credentials = try keychainStorage.retrieveCredentials(withUsername: "alice@stanford.edu", for: .stanfordSUNet) { print("Username: \(credentials.username)") print("Password: \(credentials.password)") } ``` ``` -------------------------------- ### KeychainStorage.store(_:for:) Source: https://context7.com/stanfordspezi/spezistorage/llms.txt Stores a Credentials object (username + password pair) into the keychain under the specified CredentialsTag. Replaces duplicates by default. ```APIDOC ## KeychainStorage.store(_:for:) ### Description Stores a `Credentials` object (username + password pair) into the keychain under the specified `CredentialsTag`. Replaces duplicates by default. ### Method ```swift func store(_ credentials: Credentials, for tag: CredentialsTag) throws ``` ### Parameters #### Path Parameters - **credentials** (Credentials) - Required - The credentials object to store. - **tag** (CredentialsTag) - Required - The tag under which to store the credentials. ### Request Example ```swift let credentials = Credentials(username: "alice@stanford.edu", password: "s3cur3P@ss!") try keychainStorage.store(credentials, for: .stanfordSUNet) ``` ``` -------------------------------- ### KeychainStorage.retrieveAllCredentials(withUsername:for:) Source: https://context7.com/stanfordspezi/spezistorage/llms.txt Retrieves all matching Credentials entries for a given tag, optionally filtered by username. Returns an empty array if none are found. ```APIDOC ## KeychainStorage.retrieveAllCredentials(withUsername:for:) ### Description Retrieves all matching `Credentials` entries for a given tag, optionally filtered by username. Returns an empty array if none are found. ### Method ```swift func retrieveAllCredentials(withUsername username: String? = nil, for tag: CredentialsTag) throws -> [Credentials] ``` ### Parameters #### Path Parameters - **username** (String?) - Optional - The username to filter credentials by. If `nil`, all credentials for the tag are considered. - **tag** (CredentialsTag) - Required - The tag to retrieve credentials from. ### Response #### Success Response (200) - **[Credentials]** - An array of matching credentials objects. ### Request Example ```swift let allCredentials = try keychainStorage.retrieveAllCredentials(withUsername: nil, for: .stanfordSUNet) ``` ``` -------------------------------- ### CredentialsTag: Defining Keychain Credentials Entries Source: https://context7.com/stanfordspezi/spezistorage/llms.txt Defines how and where `Credentials` entries are stored in the keychain. Supports internet passwords for servers and generic passwords for services, with options for synchronization and local-only storage. ```APIDOC ## CredentialsTag: Defining Keychain Credentials Entries ### Description `CredentialsTag` specifies how and where a `Credentials` entry is stored in the keychain. Use `.internetPassword(forServer:)` for website-associated credentials and `.genericPassword(forService:)` for app-internal credentials. ### Parameters #### Path Parameters - **server** (`String`) - The server domain for internet passwords. - **service** (`String`) - The service name for generic passwords. - **storage** (`KeychainStorage.Storage`) - Specifies whether the credential should be synchronized across iCloud devices or stored locally. - **label** (`String`, optional) - A user-readable label for the credential. ### Request Example ```swift import SpeziKeychainStorage extension CredentialsTag { // Internet password, synchronized across the user's iCloud devices static let stanfordSUNet = CredentialsTag.internetPassword( forServer: "stanford.edu", storage: .keychainSynchronizable, label: "Stanford SUNet Account" ) // Generic password, stored locally on-device only static let appPIN = CredentialsTag.genericPassword( forService: "edu.stanford.spezi.pin", storage: .keychain ) } ``` ``` -------------------------------- ### Store Data with LocalStorage Source: https://github.com/stanfordspezi/spezistorage/blob/main/Sources/SpeziLocalStorage/SpeziLocalStorage.docc/SpeziLocalStorage.md Store elements conforming to `Codable` in the local storage using the `store` method. Handle potential storage errors by using a `do-catch` block. ```swift let note = Note(text: "Spezi is awesome!", date: Date()) do { try localStorage.store(note, for: .note) } catch { // Handle storage errors ... } ``` -------------------------------- ### Configure LocalStorage Encryption Settings Source: https://context7.com/stanfordspezi/spezistorage/llms.txt Configure encryption for LocalStorage entries using LocalStorageSetting. Options include no encryption, externally-supplied key pair, Secure Enclave key, or Keychain-managed key. ```swift import SpeziLocalStorage extension LocalStorageKeys { // No encryption; file excluded from iCloud backup by default static let insensitive = LocalStorageKey( "edu.stanford.spezi.insensitive", setting: .unencrypted(excludeFromBackup: true) ) // Encrypted using a Keychain-managed key (default setting) static let sensitive = LocalStorageKey( "edu.stanford.spezi.sensitive", setting: .encryptedUsingKeychain(userPresence: false, excludeFromBackup: true) ) // Encrypted using a Secure Enclave key; requires user biometric/PIN static let highSecurity = LocalStorageKey( "edu.stanford.spezi.highSecurity", setting: .encryptedUsingSecureEnclave(userPresence: true) ) } ``` -------------------------------- ### Generate Cryptographic Keys Source: https://context7.com/stanfordspezi/spezistorage/llms.txt Creates a new EC key pair and stores it in the keychain or Secure Enclave, returning the private `SecKey`. Throws an error if the Secure Enclave is unavailable or key creation fails. ```swift func generateKey(_ keychainStorage: KeychainStorage) { do { let privateKey = try keychainStorage.createKey(for: .encryptionKey) print("Key created: \(privateKey)") } catch KeychainStorage.KeychainError.failedToCreateKeyPair(let tag, .secureEnclaveNotAvailable) { print("Secure Enclave not available for tag: \(tag.tagValue)") } catch { print("Key creation failed: \(error)") } } ``` -------------------------------- ### Access Storage Modules in SwiftUI Source: https://github.com/stanfordspezi/spezistorage/blob/main/README.md Access LocalStorage and KeychainStorage instances within a SwiftUI View using the @Environment property wrapper. ```swift struct ExampleStorageView: View { @Environment(LocalStorage.self) var localStorage @Environment(KeychainStorage.self) var keychainStorage var body: some View { // ... } } ``` -------------------------------- ### Define LocalStorage Entries with LocalStorageKey Source: https://context7.com/stanfordspezi/spezistorage/llms.txt Define type-safe keys for LocalStorage entries using LocalStorageKey. Specify identifier, encoding, and encryption settings. Keys must have unique reverse-DNS-style string identifiers. ```swift import SpeziLocalStorage struct Note: Codable { let date: Date let text: String } struct Person: Codable { let name: String let age: Int } extension LocalStorageKeys { // JSON-encoded, encrypted with keychain key (default) static let note = LocalStorageKey("edu.stanford.spezi.note") // PropertyList-encoded, unencrypted, included in iCloud backup static let person = LocalStorageKey( "edu.stanford.spezi.person", setting: .unencrypted(excludeFromBackup: false), encoder: PropertyListEncoder(), decoder: PropertyListDecoder() ) // Raw Data, encrypted using Secure Enclave static let rawBlob = LocalStorageKey( "edu.stanford.spezi.blob", setting: .encryptedUsingSecureEnclave() ) // Custom encode/decode handlers static let customEntry = LocalStorageKey( key: "edu.stanford.spezi.custom", encode: { Data($0.utf8) }, decode: { String(decoding: $0, as: UTF8.self) } ) } ``` -------------------------------- ### KeychainStorage.updateCredentials(withUsername:for:with:) Source: https://context7.com/stanfordspezi/spezistorage/llms.txt Atomically replaces the credentials identified by the given username and tag with new credentials (deletes old entry, then stores new one). ```APIDOC ## KeychainStorage.updateCredentials(withUsername:for:with:) ### Description Atomically replaces the credentials identified by the given username and tag with new credentials (deletes old entry, then stores new one). ### Method ```swift func updateCredentials(withUsername username: String, for tag: CredentialsTag, with newCredentials: Credentials) throws ``` ### Parameters #### Path Parameters - **username** (String) - Required - The username of the credentials to update. - **tag** (CredentialsTag) - Required - The tag of the credentials to update. - **newCredentials** (Credentials) - Required - The new credentials object to replace the existing one. ### Request Example ```swift let newCredentials = Credentials(username: "alice@stanford.edu", password: "n3wP@ssw0rd!") try keychainStorage.updateCredentials(withUsername: "alice@stanford.edu", for: .stanfordSUNet, with: newCredentials) ``` ``` -------------------------------- ### Define Keychain Credentials Entries Source: https://context7.com/stanfordspezi/spezistorage/llms.txt CredentialsTag defines how and where Credentials are stored in the keychain, distinguishing between internet and generic passwords with specific storage options. ```swift import SpeziKeychainStorage extension CredentialsTag { // Internet password, synchronized across the user's iCloud devices static let stanfordSUNet = CredentialsTag.internetPassword( forServer: "stanford.edu", storage: .keychainSynchronizable, label: "Stanford SUNet Account" ) // Generic password, stored locally on-device only static let appPIN = CredentialsTag.genericPassword( forService: "edu.stanford.spezi.pin", storage: .keychain ) } ``` -------------------------------- ### KeychainStorage.deleteCredentials(withUsername:for:) Source: https://context7.com/stanfordspezi/spezistorage/llms.txt Deletes all matching credentials for a tag. Passing nil for username deletes all entries for that tag. ```APIDOC ## KeychainStorage.deleteCredentials(withUsername:for:) ### Description Deletes all matching credentials for a tag. Passing `nil` for `username` deletes all entries for that tag. ### Method ```swift func deleteCredentials(withUsername username: String? = nil, for tag: CredentialsTag) throws ``` ### Parameters #### Path Parameters - **username** (String?) - Optional - The username of the credentials to delete. If `nil`, all credentials for the tag are deleted. - **tag** (CredentialsTag) - Required - The tag of the credentials to delete. ### Request Example ```swift try keychainStorage.deleteCredentials(withUsername: "alice@stanford.edu", for: .stanfordSUNet) ``` ``` -------------------------------- ### Define Custom LocalStorage Keys Source: https://github.com/stanfordspezi/spezistorage/blob/main/Sources/SpeziLocalStorage/SpeziLocalStorage.docc/SpeziLocalStorage.md Define unique storage keys for your data by creating static properties of type `LocalStorageKey` within an extension on `LocalStorageKeys`. You can customize encryption and encoding behavior. ```swift struct Note: Codable, Equatable { let text: String let date: Date } extension LocalStorageKeys { // By default, storage keys are encoded using JSON and stored encrypted. static let note = LocalStorageKey("edu.stanford.spezi.note") // You can customize these aspects: static let plistNote = LocalStorageKey( "edu.stanford.spezi.note2", setting: .encryptedUsingSecureEnclave(), encoder: PropertyListEncoder(), decoder: PropertyListDecoder() ) } ``` -------------------------------- ### Store Credentials in Keychain Source: https://context7.com/stanfordspezi/spezistorage/llms.txt Stores a `Credentials` object (username + password pair) into the keychain. Replaces duplicates by default. ```swift func saveCredentials(_ keychainStorage: KeychainStorage) { let credentials = Credentials(username: "alice@stanford.edu", password: "s3cur3P@ss!") do { try keychainStorage.store(credentials, for: .stanfordSUNet) print("Credentials stored") } catch { print("Failed to store credentials: \(error)") } } ``` -------------------------------- ### LocalStorageEntry SwiftUI Property Wrapper Source: https://context7.com/stanfordspezi/spezistorage/llms.txt A SwiftUI `DynamicProperty` wrapper that binds a view property to a `LocalStorageKey`. Changes to the property automatically persist to storage, and changes in storage update the view. ```APIDOC ## LocalStorageEntry: SwiftUI Property Wrapper ### Description `@LocalStorageEntry` is a `DynamicProperty` wrapper that automatically reads from `LocalStorage` and triggers SwiftUI view updates when the stored value changes. Writing to the wrapped value persists the new value to storage. ### Parameters #### Path Parameters - **key** (`LocalStorageKey`) - The key identifying the data in local storage. ### Request Example ```swift struct Person: Codable, Equatable { let name: String let age: Int } extension LocalStorageKeys { static let lastUser = LocalStorageKey("edu.stanford.spezi.app.lastUser") } struct ProfileView: View { @LocalStorageEntry(.lastUser) private var lastUser: Person? var body: some View { Group { if let lastUser { VStack { Text("Welcome back, \(lastUser.name)!") Text("Age: \(lastUser.age)") Button("Clear") { self.lastUser = nil } } } else { Button("Set User") { self.lastUser = Person(name: "Alice", age: 30) } } } } } ``` ``` -------------------------------- ### LocalStorage.store(_:for:) Source: https://context7.com/stanfordspezi/spezistorage/llms.txt Persists a Codable value to local storage. Passing `nil` for the value deletes the stored entry. This method overwrites any previously stored value for the given key. ```APIDOC ## LocalStorage.store(_:for:) ### Description Stores a `Codable` (or custom-encoded) value associated with a `LocalStorageKey`. Passing `nil` deletes the stored entry. Overwrites any previously stored value for the key. ### Parameters #### Path Parameters - **value** (`Codable?`) - The value to store, or `nil` to delete the entry. - **key** (`LocalStorageKey`) - The key under which to store the value. ### Request Example ```swift struct HealthRecord: Codable { let heartRate: Int let timestamp: Date } extension LocalStorageKeys { static let healthRecord = LocalStorageKey("edu.stanford.spezi.healthRecord") } // In a view or module: func saveRecord(_ localStorage: LocalStorage) { let record = HealthRecord(heartRate: 72, timestamp: Date()) do { try localStorage.store(record, for: .healthRecord) print("Saved successfully") } catch { print("Failed to save: \(error)") } } // Delete by passing nil func clearRecord(_ localStorage: LocalStorage) { try? localStorage.store(nil as HealthRecord?, for: .healthRecord) } ``` ``` -------------------------------- ### Manage Cryptographic Keys Source: https://github.com/stanfordspezi/spezistorage/blob/main/Sources/SpeziKeychainStorage/KeychainStorage.docc/KeychainStorage.md Use a CryptographicKeyTag to interact with the keychain storage for cryptographic keys. This includes creating new keys, retrieving public keys, and deleting existing keys. ```swift let privateKey = try keychainStorage.createKey(for: .databaseKey) let publicKey = try keychainStorage.retrievePublicKey(for: .databaseKey) // ... try keychainStorage.deleteKey(for: .databaseKey) ``` -------------------------------- ### Access LocalStorage Directly in SwiftUI Source: https://github.com/stanfordspezi/spezistorage/blob/main/Sources/SpeziLocalStorage/SpeziLocalStorage.docc/SpeziLocalStorage.md Access the `LocalStorage` module directly within SwiftUI views by using the `@Environment` property wrapper. This allows for more direct interaction with the storage module when needed. ```swift struct ExampleStorageView: View { @Environment(LocalStorage.self) var localStorage var body: some View { // ... } } ``` -------------------------------- ### Delete Keys with KeychainStorage Source: https://context7.com/stanfordspezi/spezistorage/llms.txt Shows how to remove cryptographic keys from KeychainStorage, either by their tag or by a direct SecKey reference. Handle potential errors during deletion. ```swift func removeKey(_ keychainStorage: KeychainStorage) { do { // Delete by tag try keychainStorage.deleteKey(for: .encryptionKey) print("Key deleted") // Delete by direct SecKey reference if let key = try keychainStorage.retrievePrivateKey(for: .signingKey) { try keychainStorage.deleteKey(key) } } catch { print("Key deletion failed: \(error)") } } ``` -------------------------------- ### KeychainStorage.retrievePrivateKey and retrievePublicKey Source: https://context7.com/stanfordspezi/spezistorage/llms.txt Retrieves the private or public SecKey for a given CryptographicKeyTag. Returns nil if the key does not exist. The public key is reconstructed from the private key if not stored directly. ```APIDOC ## KeychainStorage.retrievePrivateKey(for:) and retrievePublicKey(for:) ### Description Retrieves the private or public `SecKey` for a given `CryptographicKeyTag`. Returns `nil` if the key does not exist. The public key is reconstructed from the private key if not stored directly. ### Method ```swift func retrievePrivateKey(for tag: CryptographicKeyTag) throws -> SecKey? func retrievePublicKey(for tag: CryptographicKeyTag) throws -> SecKey? ``` ### Parameters #### Path Parameters - **tag** (`CryptographicKeyTag`) - Required - The tag identifying the key to retrieve. ``` -------------------------------- ### Access KeychainStorage in SwiftUI View Source: https://github.com/stanfordspezi/spezistorage/blob/main/Sources/SpeziKeychainStorage/KeychainStorage.docc/KeychainStorage.md Access the KeychainStorage instance within a SwiftUI view using the @Environment property wrapper. This allows your views to interact with the keychain. ```swift struct ExampleStorageView: View { @Environment(KeychainStorage.self) var keychain var body: some View { // ... } } ``` -------------------------------- ### Store Codable Value in LocalStorage Source: https://context7.com/stanfordspezi/spezistorage/llms.txt Persists a Codable value to local storage using a LocalStorageKey. Passing nil deletes the entry. Overwrites existing values. ```swift struct HealthRecord: Codable { let heartRate: Int let timestamp: Date } extension LocalStorageKeys { static let healthRecord = LocalStorageKey("edu.stanford.spezi.healthRecord") } // In a view or module: func saveRecord(_ localStorage: LocalStorage) { let record = HealthRecord(heartRate: 72, timestamp: Date()) do { try localStorage.store(record, for: .healthRecord) print("Saved successfully") } catch { print("Failed to save: \(error)") } } // Delete by passing nil func clearRecord(_ localStorage: LocalStorage) { try? localStorage.store(nil as HealthRecord?, for: .healthRecord) } ``` -------------------------------- ### Update Credentials in Keychain Source: https://context7.com/stanfordspezi/spezistorage/llms.txt Atomically replaces the credentials identified by the given username and tag with new credentials. This operation deletes the old entry and then stores the new one. ```swift func changePassword(_ keychainStorage: KeychainStorage) { let newCredentials = Credentials(username: "alice@stanford.edu", password: "n3wP@ssw0rd!") do { try keychainStorage.updateCredentials( withUsername: "alice@stanford.edu", for: .stanfordSUNet, with: newCredentials ) print("Password updated") } catch { print("Update failed: \(error)") } } ``` -------------------------------- ### Update Credentials in KeychainStorage Source: https://github.com/stanfordspezi/spezistorage/blob/main/Sources/SpeziKeychainStorage/KeychainStorage.docc/KeychainStorage.md Update existing credentials in the keychain by using the `updateCredentials` method. This replaces an existing item with new credentials, identified by username and tag. ```swift try keychainStorage.updateCredentials(withUsername: "lukas", for: .stanfordSUNet, with: newCredentials) ``` -------------------------------- ### Access LocalStorage in SwiftUI with @LocalStorageEntry Source: https://github.com/stanfordspezi/spezistorage/blob/main/Sources/SpeziLocalStorage/SpeziLocalStorage.docc/SpeziLocalStorage.md Use the `@LocalStorageEntry` property wrapper to easily access and manage individual `LocalStorage` entries within your SwiftUI views. Changes made to the property wrapper are automatically saved, and updates from other parts of the app will refresh the view. ```swift struct ExampleView: View { @LocalStorageEntry(.note) private var note var body: some View { // Use note within the view. // Assigning a new value to the property wrapper will automatically store it into the LocalStorage. // Furthermore, if some other part of your app stores a new value for the `.note` key, // the property wrapper will automatically update the view. } } ``` -------------------------------- ### LocalStorage.load(_:) Source: https://context7.com/stanfordspezi/spezistorage/llms.txt Retrieves the most recently stored value for a given `LocalStorageKey`. Returns `nil` if no value is found, or throws an error if decryption fails. ```APIDOC ## LocalStorage.load(_:) ### Description Loads the most recently stored value for a `LocalStorageKey`. Returns `nil` if no value has been stored, or throws if decryption fails. ### Parameters #### Path Parameters - **key** (`LocalStorageKey`) - The key of the value to retrieve. ### Response #### Success Response - **value** (`Codable?`) - The stored value, or `nil` if no value is associated with the key. ### Request Example ```swift func loadRecord(_ localStorage: LocalStorage) { do { if let record = try localStorage.load(.healthRecord) { print("Heart rate: \(record.heartRate), at: \(record.timestamp)") } else { print("No record stored yet") } } catch { print("Failed to load: \(error)") // Possible errors: LocalStorageError.decryptionNotPossible } } ``` ``` -------------------------------- ### Define a CryptographicKeyTag Source: https://github.com/stanfordspezi/spezistorage/blob/main/Sources/SpeziKeychainStorage/KeychainStorage.docc/KeychainStorage.md Define a custom tag for a cryptographic key, specifying its storage mechanism and a descriptive label. Use this to identify keys within the keychain. ```swift extension CryptographicKeyTag { static let databaseKey = Self("dbKey", storage: .secureEnclave, label: "Database Encryption") } ``` -------------------------------- ### Delete Data from LocalStorage Source: https://github.com/stanfordspezi/spezistorage/blob/main/Sources/SpeziLocalStorage/SpeziLocalStorage.docc/SpeziLocalStorage.md Remove previously stored elements from local storage using the `delete` method. Errors during deletion should be handled using a `do-catch` block. ```swift do { try localStorage.delete(.note) } catch { // Handle delete errors ... } ``` -------------------------------- ### Define Cryptographic Key Tags Source: https://context7.com/stanfordspezi/spezistorage/llms.txt Defines `CryptographicKeyTag` instances to identify EC key pairs stored in the keychain or Secure Enclave. The `storage` parameter controls the private key's location. ```swift import SpeziKeychainStorage extension CryptographicKeyTag { // 256-bit EC key stored in the Secure Enclave (no user presence required) static let encryptionKey = CryptographicKeyTag( "edu.stanford.spezi.encryptionKey", size: 256, storage: .secureEnclave, label: "Spezi Encryption Key" ) // 256-bit EC key stored in the regular keychain, synchronizable static let signingKey = CryptographicKeyTag( "edu.stanford.spezi.signingKey", size: 256, storage: .keychainSynchronizable ) } ``` -------------------------------- ### Delete Credentials from Keychain Source: https://context7.com/stanfordspezi/spezistorage/llms.txt Deletes all matching credentials for a given tag. Passing `nil` for `username` deletes all entries associated with that tag. ```swift func deleteCredentials(_ keychainStorage: KeychainStorage) { do { try keychainStorage.deleteCredentials(withUsername: "alice@stanford.edu", for: .stanfordSUNet) print("Credentials deleted") } catch { print("Deletion failed: \(error)") } } ``` -------------------------------- ### Delete Entry or All Entries from LocalStorage Source: https://context7.com/stanfordspezi/spezistorage/llms.txt Removes a specific entry using its key, or wipes all entries. Supports selective deletion based on a predicate. ```swift func deleteEntry(_ localStorage: LocalStorage) { do { try localStorage.delete(.healthRecord) print("Entry deleted") } catch { print("Deletion failed: \(error)") // Possible error: LocalStorageError.deletionNotPossible } } // Remove all entries func wipeAll(_ localStorage: LocalStorage) { try? localStorage.deleteAll() } // Remove only entries whose key contains "temp" func wipeTempEntries(_ localStorage: LocalStorage) { try? localStorage.deleteAll { rawKey in rawKey.contains("temp") } } ``` -------------------------------- ### LocalStorage.modify(_:_:) Source: https://context7.com/stanfordspezi/spezistorage/llms.txt Atomically modifies a stored value in place. It reads the current value, applies a transformation closure, and writes the result back, ensuring thread safety. ```APIDOC ## LocalStorage.modify(_:_:) ### Description Reads the current value for a key, applies a transform closure, then writes the result back—all under a write lock for atomicity. ### Parameters #### Path Parameters - **key** (`LocalStorageKey`) - The key of the value to modify. - **transform** (`(inout Value?) -> Void`) - A closure that takes a mutable reference to the current value (or `nil` if not present) and modifies it. ### Request Example ```swift extension LocalStorageKeys { static let visitCount = LocalStorageKey( "edu.stanford.spezi.visitCount", setting: .unencrypted() ) } func incrementVisitCount(_ localStorage: LocalStorage) { do { try localStorage.modify(.visitCount) { count in count = (count ?? 0) + 1 } } catch { print("Modify failed: \(error)") } } // Setting value to nil inside the closure deletes the entry: func resetVisitCount(_ localStorage: LocalStorage) { try? localStorage.modify(.visitCount) { $0 = nil } } ``` ``` -------------------------------- ### Load Value from LocalStorage Source: https://context7.com/stanfordspezi/spezistorage/llms.txt Retrieves the most recent value for a LocalStorageKey. Returns nil if no value is stored or throws if decryption fails. ```swift func loadRecord(_ localStorage: LocalStorage) { do { if let record = try localStorage.load(.healthRecord) { print("Heart rate: \(record.heartRate), at: \(record.timestamp)") } else { print("No record stored yet") } } catch { print("Failed to load: \(error)") // Possible errors: LocalStorageError.decryptionNotPossible } } ``` -------------------------------- ### Atomically Modify Value in LocalStorage Source: https://context7.com/stanfordspezi/spezistorage/llms.txt Reads, transforms, and writes a value back to local storage atomically. Use for in-place mutations like incrementing counters. ```swift extension LocalStorageKeys { static let visitCount = LocalStorageKey( "edu.stanford.spezi.visitCount", setting: .unencrypted() ) } func incrementVisitCount(_ localStorage: LocalStorage) { do { try localStorage.modify(.visitCount) { count in count = (count ?? 0) + 1 } } catch { print("Modify failed: \(error)") } } // Setting value to nil inside the closure deletes the entry: func resetVisitCount(_ localStorage: LocalStorage) { try? localStorage.modify(.visitCount) { $0 = nil } } ``` -------------------------------- ### CryptographicKeyTag Source: https://context7.com/stanfordspezi/spezistorage/llms.txt `CryptographicKeyTag` identifies an EC (`ECSECPrimeRandom`) key pair stored in the keychain or Secure Enclave. The `storage` parameter controls where the private key lives. ```APIDOC ## CryptographicKeyTag ### Description `CryptographicKeyTag` identifies an EC (`ECSECPrimeRandom`) key pair stored in the keychain or Secure Enclave. The `storage` parameter controls where the private key lives. ### Properties - **tagValue** (String) - The unique identifier for the key tag. - **size** (Int) - The size of the key in bits. - **storage** (KeyStorage) - Specifies where the private key is stored (e.g., `.secureEnclave`, `.keychainSynchronizable`). - **label** (String?) - An optional human-readable label for the key. ### Example Usage ```swift // 256-bit EC key stored in the Secure Enclave (no user presence required) static let encryptionKey = CryptographicKeyTag("edu.stanford.spezi.encryptionKey", size: 256, storage: .secureEnclave, label: "Spezi Encryption Key") // 256-bit EC key stored in the regular keychain, synchronizable static let signingKey = CryptographicKeyTag("edu.stanford.spezi.signingKey", size: 256, storage: .keychainSynchronizable) ``` ``` -------------------------------- ### SwiftUI Property Wrapper for LocalStorage Source: https://context7.com/stanfordspezi/spezistorage/llms.txt The @LocalStorageEntry wrapper automatically syncs a SwiftUI view with a LocalStorage value, updating the view on changes and persisting writes. ```swift struct Person: Codable, Equatable { let name: String let age: Int } extension LocalStorageKeys { static let lastUser = LocalStorageKey("edu.stanford.spezi.app.lastUser") } struct ProfileView: View { @LocalStorageEntry(.lastUser) private var lastUser: Person? var body: some View { Group { if let lastUser { VStack { Text("Welcome back, \(lastUser.name)!") Text("Age: \(lastUser.age)") Button("Clear") { self.lastUser = nil } } } else { Button("Set User") { self.lastUser = Person(name: "Alice", age: 30) } } } } } ``` -------------------------------- ### KeychainStorage.deleteKey Source: https://context7.com/stanfordspezi/spezistorage/llms.txt Deletes a stored cryptographic key by its CryptographicKeyTag or by a direct SecKey reference. ```APIDOC ## KeychainStorage.deleteKey(for:) and deleteKey(_:) ### Description Deletes a stored cryptographic key by its `CryptographicKeyTag` or by a direct `SecKey` reference. ### Method ```swift func deleteKey(for tag: CryptographicKeyTag) throws func deleteKey(_ key: SecKey) throws ``` ### Parameters #### Path Parameters - **tag** (`CryptographicKeyTag`) - Required - The tag identifying the key to delete. - **key** (`SecKey`) - Required - A direct reference to the `SecKey` to delete. ``` -------------------------------- ### Delete Credentials from KeychainStorage Source: https://github.com/stanfordspezi/spezistorage/blob/main/Sources/SpeziKeychainStorage/KeychainStorage.docc/KeychainStorage.md Remove credentials from the keychain using the `deleteCredentials` method. This operation targets credentials based on the provided username and tag. ```swift try keychainStorage.deleteCredentials( withUsername: "lukas", for: .stanfordSUNet ) ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.