I've been looking for the perfect UserDefaults wrapper that
- seamlessly encodes and decodes Data objects from storage
- works with @Published
My starting point was this StackOverflow answer, but it uses object:forKey
which doesn't work with custom objects (encoding URLs is always non-trivial for me).
My idea is to be able to use it like so:
struct Server: Identifiable, Codable, Equatable, Hashable { /* vars */ }
class ServerPickerViewModel: ObservableObject {
@Published(wrappedValue: Server.defaultServers.first!,
type: Server.self,
key: "currentServer")
var currentServer: Server?
}
To achieve this, I modified the code from @VictorKushnerov's answer:
import Combine
private var cancellables = [String: AnyCancellable]()
extension Published {
init<T: Encodable & Decodable>(wrappedValue defaultValue: T, type: T.Type, key: String) {
let decoder = JSONDecoder()
var value: T
if
let data = UserDefaults.standard.data(forKey: key),
let decodedVal = try? decoder.decode(T.self, from: data) {
value = decodedVal
} else {
value = defaultValue
}
self.init(initialValue: value) // <-- Error
cancellables[key] = projectedValue.sink { val in
let encoder = JSONEncoder()
let encodedVal = encoder.encode(val) // <-- Error
UserDefaults.standard.set(encodedVal, forKey: key)
}
}
}
There are currently two errors I can't get through, which are the following:
Cannot convert value of type 'T' to expected argument type 'Value'
it still relying on the underlying@Published
'sValue
generic type, I wish I could override that with my type T.Instance method 'encode' requires that 'Published<Value>.Publisher.Output' (aka 'Value') conform to 'Encodable'