I have custom class named User witch conforms to NSCoding protocol. But when I'm trying to store it in NSUserDefaults I get this error:
Property list invalid for format: 200
(property lists cannot contain objects of type 'CFType')
And this warning:
NSForwarding: warning: object 0x7f816a681110 of class 'Shikimori.User'
does not implement methodSignatureForSelector: -- trouble ahead
Unrecognized selector -[Shikimori.User _copyDescription]
User class:
class User: NSCoding {
private enum CoderKeys: String {
case ID = "user.id"
case Username = "user.username"
case Email = "user.email"
case Avatar = "user.avatar"
}
let id: Int
let username: String
let email: String
let avatar: String
init(id: Int, username: String, email: String, avatar: String) {
self.id = id
self.username = username
self.email = email
self.avatar = avatar
}
required init(coder aDecoder: NSCoder) {
id = aDecoder.decodeIntegerForKey(CoderKeys.ID.rawValue)
username = aDecoder.decodeObjectForKey(CoderKeys.Username.rawValue) as String
email = aDecoder.decodeObjectForKey(CoderKeys.Email.rawValue) as String
avatar = aDecoder.decodeObjectForKey(CoderKeys.Avatar.rawValue) as String
}
func encodeWithCoder(aCoder: NSCoder) {
aCoder.encodeInteger(id, forKey: CoderKeys.ID.rawValue)
aCoder.encodeObject(username as NSString, forKey: CoderKeys.Username.rawValue)
aCoder.encodeObject(email as NSString, forKey: CoderKeys.Email.rawValue)
aCoder.encodeObject(avatar as NSString, forKey: CoderKeys.Avatar.rawValue)
}
}
Update Saving code:
let userKeyForDefaults = "apimanager.user"
let defaults = NSUserDefaults.standardUserDefaults()
defaults.setObject(user, forKey: self.userKeyForDefaults)
NSUserDefaults does not support all objects, just things like NSString, NSDictionary, and most importantly, NSData. This is where the NSCoding protocol comes in handy. It allows us to transform custom objects into chunks of NSData. To do this, use the NSKeyedArchiver class to turn a custom object that conforms to NSCoding to the equivalent NSData
let obj = User()
let data = NSKeyedArchiver. archivedDataWithRootObject(obj)
/// Now you can store data
The docs can be found here and the method you are looking for is
class func archivedDataWithRootObject(_ rootObject: AnyObject) -> NSData
To get things out of NSUserDefaults, use the "inverse" class, NSKeyedUnarchiver. Its appropriate method is as follows
class func unarchiveObjectWithData(_ data: NSData) -> AnyObject?
Here is a sample.
let data = NSUserDefaults.standardUserDefaults().objectForKey("somekey")
let obj = NSKeyedUnarchiver. unarchiveObjectWithData(data) as User?
Note: I may have mixed up some optionals, but you get the jist. The documentation for NSKeyedUnarchiver can be found here.
Edit: Fixing the OP's problem was as simple as correctly using NSKeyedArchiver and subclassing NSObject.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With