ModernSwiftStorage
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")
]
Quick Start
1. Basic App Setup
import SwiftUI
import ModernSwiftStorage
@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
ContentView()
.withSimpleStorageService()
}
}
}
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)
}
}
}
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)
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 = ""
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 = ""
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)
}
}
}
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")
}
}
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)
)
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()
}
}
}
}
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"
)
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")
}
}
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)
}
}
}
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)")
}
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 = ""
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
}
}
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 = ""
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()
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")
}
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)