Dictionary with NSCoding based values
In the earlier 3.x version I had this:
class SomeClass: NSObject, NSCoding {
var someVariable: String?
init(bookIdentifier: String) {
super.init()
self.someVariable = someVariable
}
///NSCoding
func encode(with aCoder: NSCoder) {
aCoder.encode(someVariable, forKey: "someVariable")
}
///NSCoding
required init?(coder aDecoder: NSCoder) {
self.someVariable = aDecoder.decodeObject(forKey: "someVariable") as? String
}
}
extension DefaultsKeys {
static let someVariables = DefaultsKey<[String: SomeClass]>("someVariables")
}
extension UserDefaults {
subscript(key: DefaultsKey<[String: SomeClass]>) -> [String: SomeClass] {
get {
if let someVariables = unarchive(key) {
return someVariables
}
else {
return [:]
}
}
set { archive(key, newValue) }
}
}
According to migration guide it should be now just:
class SomeClass: NSObject, NSCoding, DefaultsSerializable {
var someVariable: String?
init(bookIdentifier: String) {
super.init()
self.someVariable = someVariable
}
///NSCoding
func encode(with aCoder: NSCoder) {
aCoder.encode(someVariable, forKey: "someVariable")
}
///NSCoding
required init?(coder aDecoder: NSCoder) {
self.someVariable = aDecoder.decodeObject(forKey: "someVariable") as? String
}
}
extension DefaultsKeys {
static let someVariables = DefaultsKey<[String: SomeClass]>("someVariables", defaultValue: [:])
}
But I'm getting following error:
[User Defaults] Attempt to set a non-property-list object {
9789511310716 = "<AppName.SomeClass: 0x28085fc40>";
} as an NSUserDefaults/CFPreferences value for key someVariables
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Attempt to insert non-property list object {
9789511310716 = "<AppName.SomeClass: 0x28085fc40>";
} for key someVariables'
So it doesn't seems to support dictionary with NSCoding values, even though you state that [String: Any] is supported.
I'm able to fix it for me with changing the code in DefaultsSerializable+BuiltIns.swift to:
extension Dictionary: DefaultsSerializable where Key == String {
public typealias T = [Key: Value]
public static var _defaults: DefaultsBridge<[Key: Value]> { return DefaultsKeyedArchiverBridge() }
public static var _defaultsArray: DefaultsBridge<[[Key: Value]]> { return DefaultsArrayBridge() }
}
Shouldn't DefaultsKeyedArchiverBridge mean it works the same way as my custom subscript before? Is there way to get same effect without modifying SwiftyUserDefaults source code?
Hey @jompu, great question. So initially we removed the dictionary support from 4.*, but we brought back the [String: String] and [String: Any] support for compatibility reasons. The thing is, the Any really means that we support [String: Any], but only if your type is a primitive that is accepted by the default UserDefaults methods. We do not yet provide support for the dictionary that use our DefaultsSerializable.
It could be improved to save/retrieve the values using the bridges we provide, but I don't really have time to add it to the library right now. I would gladly review PRs for that, though.
Thank you for your answer! I will consider to do it with bridges.