When Swift is updated to enable nested property wrappers, the way to do this will probably be to create a @UserDefault
property wrapper and combine it with @Published
.
In the mean time, I think the best way to handle this situation is to implement ObservableObject
manually instead of relying on @Published
. Something like this:
class ViewModel: ObservableObject {
let objectWillChange = ObservableObjectPublisher()
var name: String {
get {
UserDefaults.standard.string(forKey: "name") ?? ""
}
set {
objectWillChange.send()
UserDefaults.standard.set(newValue, forKey: "name")
}
}
}
Property wrapper
As I mentioned in the comments, I don't think there is a way to wrap this up in a property wrapper that removes all boilerplate, but this is the best I can come up with:
@propertyWrapper
struct PublishedUserDefault<T> {
private let key: String
private let defaultValue: T
var objectWillChange: ObservableObjectPublisher?
init(wrappedValue value: T, key: String) {
self.key = key
self.defaultValue = value
}
var wrappedValue: T {
get {
UserDefaults.standard.object(forKey: key) as? T ?? defaultValue
}
set {
objectWillChange?.send()
UserDefaults.standard.set(newValue, forKey: key)
}
}
}
class ViewModel: ObservableObject {
let objectWillChange = ObservableObjectPublisher()
@PublishedUserDefault(key: "name")
var name: String = "John"
init() {
_name.objectWillChange = objectWillChange
}
}
You still need to declare objectWillChange
and connect it to your property wrapper somehow (I'm doing it in init
), but at least the property definition itself it pretty simple.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…