Contents

smart-byte/userstorage

A lightweight Swift package for persistent key-value storage — powered by CoreData. Unlike `UserDefaults`, which is limited to property list types (`String`, `Int`, `Bool`, `Data`, `Date`, `Array`, `Dictionary`), UserStorage can persist **any `Codable` type**, including custom st

Why not just UserDefaults?

| | UserDefaults | UserStorage | |---|---|---| | Supported types | Property list types only | Any Codable type | | Storage backend | plist file | SQLite (CoreData) | | Thread safety | Not guaranteed | performAndWait on background contexts | | SwiftUI integration | @AppStorage (views only) | @PublishedUserStorage (anywhere) | | Custom structs/enums | Manual serialization needed | Works out of the box |

Installation

Add UserStorage to your Package.swift:

.package(url: "https://github.com/smart-byte/UserStorage.git", from: "0.2.0")

Then import it:

import UserStorage

Usage

Storing and Retrieving Data

import UserStorage

// Store a value
UserStorage.shared.save("Hello, UserStorage!", forKey: "greeting")

// Retrieve the value
if let greeting: String = UserStorage.shared.load(forKey: "greeting") {
    print(greeting) // Output: Hello, UserStorage!
}

// Works with any Codable type
struct UserProfile: Codable {
    var name: String
    var age: Int
    var tags: [String]
}

let profile = UserProfile(name: "Mario", age: 30, tags: ["swift", "ios"])
UserStorage.shared.save(profile, forKey: "profile")

let loaded: UserProfile? = UserStorage.shared.load(forKey: "profile")

Using @PublishedUserStorage

@PublishedUserStorage is a property wrapper that combines the persistence of @AppStorage with the reactivity of @Published. It can be used in any ObservableObject — not just SwiftUI views.

import SwiftUI
import UserStorage

class UserSettingsModel: ObservableObject {
    @PublishedUserStorage("username")
    var username = "none"

    @PublishedUserStorage("appTheme")
    var appTheme = AppTheme.light

    @PublishedUserStorage("isFirstLaunch")
    var isFirstLaunch = true

    enum AppTheme: String, Codable {
        case light
        case dark
    }
    
    init() {
        // Required: connect property wrapper publishers to ObservableObject
        let mirror = Mirror(reflecting: self)
        mirror.children.forEach { child in
            if let observedProperty = child.value as? PublishedUserStorageWrapper {
                observedProperty.objectWillChange = self.objectWillChange
            }
        }
    }
}

Then use it in your app like any other ObservableObject:

@main
struct ExampleApp: App {
    @StateObject var userSettings = UserSettingsModel()

    var body: some Scene {
        WindowGroup {
            ContentView()
                .environmentObject(userSettings)
        }
    }
}
struct ContentView: View {
    @EnvironmentObject var userSettings: UserSettingsModel

    var body: some View {
        VStack {
            Text("Hello, \(userSettings.username)!")
            Button(action: {
                userSettings.username = "User"
            }) {
                Text("Change Username")
            }
        }
    }
}

License

MIT — see LICENSE for details.

Package Metadata

Repository: smart-byte/userstorage

Default branch: main

README: README.md