UserDefaults 的类型安全、可观察和可注入的包装器。
struct AppKeys: KeyGroup {
let launchCount = KeyDefinition(key: "launchCount", defaultValue: 0)
let lastLaunchDate = KeyDefinition<Date?>(key: "lastLaunchDate")
}
@dynamicMemberLookup
读取/写入值let storage = KeyValueStorage<AppKeys>(backend: UserDefaults.standard)
// Read
let launchCount = storage.launchCount
// Write
storage.launchCount = launchCount + 1
strorage.lastLaunchDate = .now
KeyValueStorage
基于以下三个概念开发
Int
和 String
)以及您的自定义类型。 UserDefaults
或 InMemoryStorage
。KeyValueStorage
支持 Observation、AsyncSequence 和 Combine 的 Publisher。如上节所示,在键组中定义键使您的代码类型安全。
struct AppKeys: KeyGroup {
let launchCount = KeyDefinition(key: "launchCount", defaultValue: 0)
let lastLaunchDate = KeyDefinition<Date?>(key: "lastLaunchDate")
}
您可以将 UserDefaults 可以接受的所有类型指定为 KeyDefinition
的类型。如果像 lastLaunchDate
一样为 KeyDefinition 的类型指定 Optional
,则可以省略默认值。
您可以读取和写入值。
let storage = KeyValueStorage<AppKeys>(backend: UserDefaults.standard)
let lastLaunchDate: Int = storage.lastLaunchDate
storage.lastLaunchDate = lastLaunchDate + 1
您可以通过使类型符合 KeyValueStorageValue
来存储和读取您的自定义类型。
如果您的类型是 RawRepresentable
,则只需添加一致性。
enum Fruit: Int, KeyValueStorageValue {
case apple
case banana
case orange
}
struct AppKeys: KeyGroup {
let fruit = KeyDefinition<Fruit>(key: "fruit", defaultValue: .apple)
}
let storage = KeyValueStorage<AppKeys>(backend: UserDefaults.standard)
let fruit: Fruit = storage.fruit
在其他情况下,您可以编写自定义序列化/反序列化逻辑。
struct Person: KeyValueStorageValue, Equatable {
typealias StoredRawValue = [String: any Sendable]
var name: String
var age: Int
func serialize() -> StoredRawValue {
["name": name, "age": age]
}
static func deserialize(from dictionary: StoredRawValue) -> Person? {
guard let name = dictionary["name"] as? String,
let age = dictionary["age"] as? Int
else { return nil }
return Person(name: name, age: age)
}
}
struct AppKeys: KeyGroup {
let person = KeyDefinition<Person?>(key: "person")
}
let storage = KeyValueStorage<AppKeys>(backend: UserDefaults.standard)
let person: Person? = storage.person
此外,您可以使用 JSONKeyDefinition
轻松存储继承 Codable 的类型。
struct Account: Codable {
var name: String
var email: String
}
struct AppKeys: KeyGroup {
let account = JSONKeyDefinition<Account?>(key: "account")
}
let storage = KeyValueStorage<AppKeys>(backend: UserDefaults.standard)
let account: Account? = storage.account
KeyGroup
是键的组合,并且同一组中的所有键都确保存储在同一存储中。
并且,该组可以嵌套在另一个组中。
因此,例如,您可以按目的划分键并将它们组合成一个组。
struct AppKeys: KeyGroup {
let launchCount = KeyDefinition(key: "launchCount", defaultValue: 0)
let debug = DebugKeys()
}
struct DebugKeys: KeyGroup {
let showConsole = KeyDefinition<Bool>(key: "showConsole", defaultValue: false)
}
let standardStorage = KeyValueStorage<AppKeys>(backend: UserDefaults.standard)
let launchCount = standardStorage.launchCount
let showConsole = standardStorage.debug.showConsole
您可以轻松地将存储值的后端更改为任何 UserDefaults
。
let standardStorage = KeyValueStorage<StandardKeys>(backend: UserDefaults.standard)
let appGroupStorage = KeyValueStorage<AppGroupKeys>(backend: UserDefaults(suiteName: "APP_GROUP")!)
InMemoryStorage
也可用作后端。
当您想并行运行单元测试时,这很有用。
let standardStorage = KeyValueStorage<StandardKeys>(backend: InMemoryStorage())
默认情况下,KeyValueStorage
支持 Observation。
例如,当计数器更新时,此视图会自动更新。
struct Keys: KeyGroup {
let counter = KeyDefinition(key: "counter", defaultValue: 0)
}
struct ContentView: View {
var storage: KeyValueStorage<Keys>
var body: some View {
VStack {
Text("\(storage.counter)")
Button("add") {
storage.counter += 1
}
}
}
}
注意
请捕获 KeyValueStorage,只要您需要观察它,因为当 KeyValueStorage 释放时,观察就结束了。
您可以通过 AsyncSequence
观察键的变化
let storage: KeyValueStorage<Keys> = ...
Task {
for await _ in storage.stream(key: \.counter) {
print("New value: \(storage.counter)")
}
}
注意
请捕获 KeyValueStorage,只要您需要观察它,因为当 KeyValueStorage 释放时,流就结束了。
您可以通过 AsyncSequence
观察键的变化
let storage: KeyValueStorage<Keys> = ...
storage.publishers(key: \.counter)
.sink {
print("New value: \(storage.counter)")
}
注意
请捕获 KeyValueStorage,只要您需要观察它,因为当 KeyValueStorage 释放时,流就结束了。
Swift 6+
您可以通过 Swift Package Manager 添加此软件包。
dependencies: [
.package(url: "https://github.com/mtj0928/key-value-storage", from: "0.3.0")
],
targets: [
.target(name: "YOUR_TARGETS", dependencies: [
.product(name: "KeyValueStorage", package: "key-value-storage")
]),
]
包括几篇文章的文档可在此处获取:此处。