DEV Community

Maksim Ponomarev
Maksim Ponomarev

Posted on

Modern Swift Storage

ModernSwiftStorage

Image description

ModernSwiftStorage is a comprehensive, type-safe storage solution for iOS, macOS, watchOS, and tvOS applications. It provides seamless integration between UserDefaults and Keychain with automatic security routing, SwiftUI property wrappers, and built-in analytics.

Why ModernSwiftStorage?

Traditional iOS storage solutions often require:

  • Manual decision-making between UserDefaults and Keychain
  • Boilerplate code for encoding/decoding
  • Separate implementations for different data types
  • Manual security considerations

ModernSwiftStorage solves these problems by providing:

Automatic Security Routing - Sensitive data automatically goes to Keychain

Type Safety - Generic methods with compile-time checking

SwiftUI Integration - Native property wrappers

Cross-Platform Support - Works on all Apple platforms

Built-in Analytics - Automatic usage statistics

Easy Testing - Protocol-based design with mock support

Installation

Add ModernSwiftStorage to your project using Swift Package Manager:

dependencies: [ .package(url: "https://github.com/Maxnxi/ModernSwiftStorage.git", from: "1.0.0") ] 
Enter fullscreen mode Exit fullscreen mode

Quick Start

1. Basic App Setup

import SwiftUI import ModernSwiftStorage @main struct MyApp: App { var body: some Scene { WindowGroup { ContentView() .withSimpleStorageService() } } } 
Enter fullscreen mode Exit fullscreen mode

2. Property Wrapper Usage

The most convenient way to use ModernSwiftStorage is through property wrappers:

struct SettingsView: View { @Storage("user.name") private var userName = "Guest" @Storage("notifications.enabled") private var notificationsEnabled = true @Storage("theme.isDark") private var isDarkTheme = false // Sensitive data automatically routed to Keychain @SecureStorage("auth.token") private var authToken = "" @SecureStorage("user.password") private var password = "" var body: some View { Form { TextField("Name", text: $userName) Toggle("Notifications", isOn: $notificationsEnabled) Toggle("Dark Theme", isOn: $isDarkTheme) SecureField("Auth Token", text: $authToken) } } } 
Enter fullscreen mode Exit fullscreen mode

Storage Types and Automatic Security

Automatic Storage Selection (Recommended)

ModernSwiftStorage automatically determines the appropriate storage based on the key name:

@Storage("user.name") private var userName = "Guest" // → UserDefaults @Storage("auth.token") private var token = "" // → Keychain (auto-detected) @Storage("user.password") private var password = "" // → Keychain (auto-detected) @Storage("api.secret") private var apiSecret = "" // → Keychain (auto-detected) 
Enter fullscreen mode Exit fullscreen mode

Sensitive Keywords that trigger automatic Keychain storage:

  • password
  • token
  • secret
  • key
  • credential
  • auth

Explicit Storage Types

You can also explicitly specify the storage type:

@UserDefaultsStorage("app.version") private var version = "1.0" @SecureStorage("biometric.data") private var biometricData = "" // Manual specification @Storage("data", storageType: .userDefaults) private var data = "" @Storage("secret", storageType: .keychain()) private var secret = "" 
Enter fullscreen mode Exit fullscreen mode

Keychain Accessibility Options

Control when Keychain data is accessible:

@Storage("token", storageType: .keychain(accessibility: .whenUnlocked)) private var token = "" @Storage("biometric", storageType: .keychain(accessibility: .whenPasscodeSetThisDeviceOnly)) private var biometric = "" 
Enter fullscreen mode Exit fullscreen mode

Available accessibility options:

  • .whenUnlocked - Default, accessible when device is unlocked
  • .afterFirstUnlock - Accessible after first unlock since boot
  • .whenPasscodeSetThisDeviceOnly - Requires passcode, device-specific
  • .whenUnlockedThisDeviceOnly - Device-specific, when unlocked

Type-Safe Storage Keys

For better organization and type safety, define custom storage keys:

extension ModernStorage.Keys { struct UserProfile: StorageKey { typealias Value = UserProfileModel let key = "user.profile.v2" let defaultValue = UserProfileModel() let storageType = StorageType.userDefaults } struct APICredentials: StorageKey { typealias Value = APICredentialsModel let key = "api.credentials" let defaultValue = APICredentialsModel() let isSensitive = true // Automatically uses Keychain } } // Usage struct MyView: View { @Storage(ModernStorage.Keys.UserProfile()) private var profile @Storage(ModernStorage.Keys.APICredentials()) private var credentials var body: some View { VStack { TextField("Name", text: $profile.fullName) SecureField("API Key", text: $credentials.apiKey) } } } 
Enter fullscreen mode Exit fullscreen mode

Direct Storage Service Access

Access the storage service directly for programmatic operations:

struct DataManager { @Environment(\.simpleStorageService) private var storage func saveUserData(_ data: UserData) { storage.set(data, forKey: "user.data") } func loadUserData() -> UserData { return storage.get(key: "user.data", defaultValue: UserData()) } func clearSensitiveData() { storage.remove(key: "auth.token", storageType: .keychain()) } func bulkUpdate() { storage.setBool(true, forKey: "feature.enabled") storage.setInt(42, forKey: "user.score") storage.setString("premium", forKey: "subscription.tier") } } 
Enter fullscreen mode Exit fullscreen mode

Working with Complex Data Types

Any Codable type is automatically supported:

struct UserProfile: Codable { var name: String var email: String var preferences: [String: String] var lastLogin: Date var settings: UserSettings } struct UserSettings: Codable { var theme: String var language: String var notifications: Bool } // Usage @Storage("user.profile") private var profile = UserProfile( name: "", email: "", preferences: [:], lastLogin: Date(), settings: UserSettings(theme: "system", language: "en", notifications: true) ) 
Enter fullscreen mode Exit fullscreen mode

Built-in Analytics and Statistics

ModernSwiftStorage automatically tracks app usage without additional setup:

struct StatisticsView: View { @Environment(\.simpleStorageService) private var storage var body: some View { VStack(alignment: .leading, spacing: 12) { Text("Days using app: \(storage.statistics.statistics.daysUsingApp)") Text("Total app opens: \(storage.statistics.statistics.timesUsingApp)") Text("Today's sessions: \(storage.statistics.statistics.timesDailyUsingApp)") Text("App installs: \(storage.statistics.statistics.timesAppInstalled)") Button("Update Statistics") { storage.statistics.updateStatistics() } Button("Reset Statistics") { storage.statistics.resetStatistics() } } } } 
Enter fullscreen mode Exit fullscreen mode

Data Migration

Migrate data between storage types when needed:

let migrationManager = StorageMigrationManager() // Migrate sensitive data from UserDefaults to Keychain await migrationManager.migrateFromUserDefaultsToKeychain( key: "old.token", accessibility: .whenUnlocked ) // Migrate data from Keychain to UserDefaults (if needed) await migrationManager.migrateFromKeychainToUserDefaults( key: "non.sensitive.data" ) 
Enter fullscreen mode Exit fullscreen mode

Testing and Mocking

ModernSwiftStorage is designed with testing in mind:

#if DEBUG struct TestView: View { var body: some View { MyView() .withSimpleStorageService( userDefaultsManager: MockUserDefaultsManager(), keychainManager: MockKeychainManager() ) } } #endif // Unit Testing class MyViewModelTests: XCTestCase { var storage: ModernStorage! override func setUp() { storage = ModernStorage( userDefaultsManager: MockUserDefaultsManager(), keychainManager: MockKeychainManager() ) } func testDataPersistence() { storage.set("test", forKey: "key") XCTAssertEqual(storage.get(key: "key", defaultValue: ""), "test") } } 
Enter fullscreen mode Exit fullscreen mode

Advanced Configuration

Customize ModernSwiftStorage for specific needs:

let config = StorageConfiguration( serviceName: "com.myapp.storage", userDefaultsSuiteName: "group.myapp.shared", defaultKeychainAccessibility: .afterFirstUnlock, enableStatistics: true, enableMigration: true ) let customStorage = SimpleStorageService(configuration: config) // Use in your app struct MyApp: App { var body: some Scene { WindowGroup { ContentView() .withSimpleStorageService(customStorage) } } } 
Enter fullscreen mode Exit fullscreen mode

Error Handling and Validation

Validate storage operations and handle errors:

do { // Validate key before use guard StorageValidator.validateKey("my.key") else { throw StorageError.invalidKey("my.key") } // Validate storage type appropriateness guard StorageValidator.validateStorageType(SensitiveData.self, storageType: .userDefaults) else { throw StorageError.unsupportedType } storage.set(data, forKey: "my.key") } catch { print("Storage error: \(error.localizedDescription)") } 
Enter fullscreen mode Exit fullscreen mode

Best Practices

1. Use Automatic Storage Selection

Let ModernSwiftStorage decide the appropriate storage type:

// Good - automatic selection @Storage("user.preferences") private var preferences = UserPreferences() @Storage("auth.token") private var token = "" // Only specify explicitly when needed @Storage("temp.data", storageType: .userDefaults) private var tempData = "" 
Enter fullscreen mode Exit fullscreen mode

2. Define Storage Keys for Type Safety

extension ModernStorage.Keys { struct AppSettings: StorageKey { typealias Value = AppSettingsModel let key = "app.settings.v2" let defaultValue = AppSettingsModel() let storageType = StorageType.userDefaults } } 
Enter fullscreen mode Exit fullscreen mode

3. Use Descriptive Key Names

// Good @Storage("user.profile.display.name") private var displayName = "" @Storage("auth.session.token") private var sessionToken = "" // Avoid @Storage("name") private var displayName = "" @Storage("token") private var sessionToken = "" 
Enter fullscreen mode Exit fullscreen mode

4. Group Related Settings

struct NotificationSettings: Codable { var pushEnabled: Bool = true var emailEnabled: Bool = false var frequency: String = "daily" } @Storage("notifications.settings") private var notificationSettings = NotificationSettings() 
Enter fullscreen mode Exit fullscreen mode

Platform Support and Requirements

  • iOS 13.0+
  • macOS 10.15+
  • watchOS 6.0+
  • tvOS 13.0+

Thread Safety

All storage operations are marked with @MainActor for thread safety in SwiftUI contexts. For background operations:

Task { @MainActor in storage.set(data, forKey: "background.key") } 
Enter fullscreen mode Exit fullscreen mode

Performance Considerations

  • UserDefaults operations are synchronous and fast for small data
  • Keychain operations have slightly more overhead but remain performant
  • Complex objects are automatically JSON encoded/decoded
  • Statistics tracking has minimal performance impact
  • Bulk operations are optimized internally

Conclusion

ModernSwiftStorage
https://github.com/Maxnxi/ModernSwiftStorage.git
provides a powerful, type-safe, and secure storage solution that eliminates the complexity of managing UserDefaults and Keychain manually. With its automatic security routing, SwiftUI integration, and built-in analytics, it's the perfect storage solution for modern iOS applications.

Whether you're building a simple settings screen or a complex app with sensitive data, ModernSwiftStorage adapts to your needs while maintaining security best practices automatically.

Top comments (0)