LocalData/README.md
Matt Bruce 8619728ffd Update Models, Services and docs
Summary:
- Sources: update Models, Services
- Docs: update docs for Proposal, README

Stats:
- 10 files changed, 1089 insertions(+), 348 deletions(-)
2026-01-18 13:43:07 -06:00

4.0 KiB

LocalData

LocalData provides a typed, discoverable namespace for persisted app data across UserDefaults, Keychain, and file storage with optional encryption.

Architecture

The package uses a clean, modular architecture with isolated actors for thread safety:

StorageRouter (main entry point)
    ├── UserDefaultsHelper
    ├── KeychainHelper
    ├── FileStorageHelper
    ├── EncryptionHelper
    └── SyncHelper

What Ships in the Package

Protocols

  • StorageKey - Define storage configuration for each data type
  • StorageProviding - Abstraction for storage operations

Services (Actors)

  • StorageRouter - Main entry point for all storage operations
  • KeychainHelper - Secure keychain storage
  • EncryptionHelper - AES-256-GCM encryption with PBKDF2
  • FileStorageHelper - File system operations
  • UserDefaultsHelper - UserDefaults with suite support
  • SyncHelper - WatchConnectivity sync

Models

  • StorageDomain - userDefaults, keychain, fileSystem, encryptedFileSystem
  • SecurityPolicy - none, keychain, encrypted (AES-256)
  • Serializer - JSON, plist, Data, or custom
  • PlatformAvailability - all, phoneOnly, watchOnly, phoneWithWatchSync
  • SyncPolicy - never, manual, automaticSmall
  • KeychainAccessibility - All 7 iOS accessibility options
  • KeychainAccessControl - All 6 access control options (biometry, passcode, etc.)
  • FileDirectory - documents, caches, custom URL
  • StorageError - Comprehensive error types
  • AnyCodable - Type-erased Codable for mixed-type payloads

Usage

1. Define Keys

Extend StorageKeys with your own key types:

import LocalData

extension StorageKeys {
    struct UserTokenKey: StorageKey {
        typealias Value = String
        
        let name = "user_token"
        let domain: StorageDomain = .keychain(service: "com.myapp")
        let security: SecurityPolicy = .keychain(
            accessibility: .afterFirstUnlock,
            accessControl: .biometryAny
        )
        let serializer: Serializer<String> = .json
        let owner = "AuthService"
        let availability: PlatformAvailability = .phoneOnly
        let syncPolicy: SyncPolicy = .never
    }
}

2. Use StorageRouter

// Save
let key = StorageKeys.UserTokenKey()
try await StorageRouter.shared.set("token123", for: key)

// Retrieve
let token = try await StorageRouter.shared.get(key)

// Remove
try await StorageRouter.shared.remove(key)

Storage Domains

Domain Use Case
userDefaults Preferences, small settings
keychain Credentials, tokens, sensitive data
fileSystem Documents, cached data, large files
encryptedFileSystem Sensitive files with AES-256 encryption

Security Options

Keychain Accessibility

  • whenUnlocked - Only when device unlocked
  • afterFirstUnlock - After first unlock until restart
  • whenUnlockedThisDeviceOnly - No migration to new device
  • afterFirstUnlockThisDeviceOnly - No migration
  • always - Always accessible (least secure)
  • alwaysThisDeviceOnly - Always, no migration
  • whenPasscodeSetThisDeviceOnly - Requires passcode

Access Control

  • userPresence - Any authentication
  • biometryAny - Face ID or Touch ID
  • biometryCurrentSet - Current enrolled biometric only
  • devicePasscode - Passcode only
  • biometryAnyOrDevicePasscode - Biometric preferred, passcode fallback
  • biometryCurrentSetOrDevicePasscode - Current biometric or passcode

Encryption

  • AES-256-GCM with PBKDF2-SHA256 key derivation
  • Configurable iteration count
  • Master key stored securely in keychain

Sync Behavior

StorageRouter can sync data to Apple Watch via WCSession when:

  • availability is .all or .phoneWithWatchSync
  • syncPolicy is .manual or .automaticSmall (≤100KB)
  • WCSession is activated and watch is paired

The app owns WCSession activation and handling incoming updates.

Platforms

  • iOS 17+
  • watchOS 10+

Sample App

See SecureStorgageSample for working examples of all storage domains and security options.