这就是在 SwiftUI 中转换计算属性的方法,方法是将其设为 @propertyWrapper。如果您需要,我添加了一个使用组合读取数据的解决方案。我还让你的属性是可选的。
struct AuthData {
var name: String
var email: String
}
- 为您的属性包装器准备协议,以便能够使用 Optional 将其设置为 nil。
public protocol AnyOptional {
var isNil: Bool { get }
}
extension Optional: AnyOptional {
public var isNil: Bool { self == nil }
}
- 扩展 UserDefaults 以符合此协议并且是可选的。
extension UserDefault where Value: ExpressibleByNilLiteral {
init(key: String, _ container: UserDefaults = .standard) {
self.init(key: key, defaultValue: nil, container: container)
}
}
- 创建与 SwiftUI 视图的 getter 和 setter 相同的属性包装器。这是一个通用的,可以用于任何类型。您可以在此代码块之后的 UserDefaults 扩展中设置它们。
import Combine
@propertyWrapper
struct UserDefault<Value> {
let key: String
let defaultValue: Value
var container: UserDefaults = .standard
// Set a Combine publisher for your new value to
// always read its changes when using Combine.
private let publisher = PassthroughSubject<Value, Never>()
var wrappedValue: Value {
get {
// Get the new value or nil if any.
container.object(forKey: key) as? Value ?? defaultValue
}
set {
// Check if the value is nil and remove it from your object.
if let optional = newValue as? AnyOptional, optional.isNil {
container.removeObject(forKey: key)
}
else {
// Set your new value inside UserDefaults.
container.set(newValue, forKey: key)
}
// Add the newValue to your combine publisher
publisher.send(newValue)
}
}
var projectedValue: AnyPublisher<Value, Never> {
publisher.eraseToAnyPublisher()
}
}
- 在 UserDefaults 中创建扩展以在代码中使用您的属性包装器。
extension UserDefaults {
@UserDefault(key: "authDataStorageKey", defaultValue: nil)
static var savedAuthData: AuthData?
// You can create as many @propertyWrapper as you want that fits your need
}
使用示例
// Set a new value
UserDefaults.savedAuthData = AuthData(name: "Muli", email: "muli@stackoverflow.com")
// Read the saved value
print(UserDefaults.savedAuthData as Any)
// When using combine
var subscriptions = Set<AnyCancellable>()
UserDefaults.$savedAuthData
.sink { savedValue in
print(savedValue as Any) } // Yours saved value that changes over time.
.store(in: &subscriptions)