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 UserStorageUsage
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