Reputation: 6544
When try to encode my custom object in iOS swift get this error from Xcode 8.3
unrecognized selector sent to instance 0x60800166fe80 *** -[NSKeyedArchiver dealloc]: warning: NSKeyedArchiver deallocated without having had -finishEncoding called on it.
And my code like this:
import UIKit import Foundation
class Place: NSObject {
func setCustomObject(CustomObject obj:Any,Key key:String) {
let encodedObject : Data = NSKeyedArchiver.archivedData(withRootObject: obj)
UserDefaults.standard.set(encodedObject, forKey: key)
}
}
Upvotes: 6
Views: 8687
Reputation: 4391
Here's an example how to make an object to conform to NSCoding
. Basically you need to provide implementation of two methods - required convenience init?(coder decoder: NSCoder)
and encode(with aCoder: NSCoder)
class Book: NSObject, NSCoding {
var title: String?
var pageCount: Int?
// Memberwise initializer
init(title: String,pageCount: Int) {
self.title = title
self.pageCount = pageCount
}
// MARK: NSCoding
// Here you will try to initialize an object from archve using keys you did set in `encode` method.
required convenience init?(coder decoder: NSCoder) {
guard let title = decoder.decodeObject(forKey: "title") as? String else { return nil }
self.init(title: title, pageCount: decoder.decodeInteger(forKey: "pageCount"))
}
// Here you need to set properties to specific keys in archive
func encode(with aCoder: NSCoder) {
aCoder.encode(self.title, forKey: "title")
aCoder.encodeCInt(Int32(self.pageCount), forKey: "pageCount")
}
}
Also I would recommend changing your setCustomObject
method to this:
func setCustomObject(obj:NSCoding, key:String) {
let encodedObject : Data = NSKeyedArchiver.archivedData(withRootObject: obj)
UserDefaults.standard.set(encodedObject, forKey: key)
}
This way compiler prevent you passing NSKeyedArchiver
an object that does not conform to NSCoding
protocol.
If you don't want to provide all properties in the init
method you can use default values:
init(title : String? = nil, pageCount: Int? = nil){
self.title = title
self.pageCount = pageCount
}
Now you can just init your object without any properties. Like that Book()
Upvotes: 5
Reputation: 3580
Here is the solutions, you have to implement the two methods
Encode Method
func encode(with aCoder: NSCoder)
Decoding method
required init?(coder aDecoder: NSCoder)
Complete Example code
class User: NSObject , NSCoding
{
var userID : Int = 0
var name : String = ""
var firstName : String = ""
var lastName : String = ""
var username : String = ""
var email : String = ""
override init(){
super.init();
}
func encode(with aCoder: NSCoder) {
aCoder.encode(self.userID, forKey: "id");
aCoder.encode(self.firstName, forKey: "first_name");
aCoder.encode(self.lastName, forKey: "last_name");
aCoder.encode(self.name, forKey: "name");
aCoder.encode(self.username,forKey: "username");
aCoder.encode(self.email, forKey: "email");
}
required init?(coder aDecoder: NSCoder) {
super.init()
self.userID = aDecoder.decodeInteger(forKey: "id");
self.firstName = aDecoder.decodeObject(forKey: "first_name") as! String;
self.lastName = aDecoder.decodeObject(forKey: "last_name") as! String;
self.name = aDecoder.decodeObject(forKey: "name") as! String
self.username = aDecoder.decodeObject(forKey: "username") as! String
self.email = aDecoder.decodeObject(forKey: "email") as! String;
}
init(data : [String: AnyObject]) {
super.init()
self.userID = String.numberValue(data["user_id"]).intValue;
self.firstName = String.stringValue(data["first_name"]);
self.lastName = String.stringValue(data["last_name"]);
self.email = String.stringValue(data["email"]);
self.username = String.stringValue(data["user_name"]);
}
class func loadLoggedInUser() -> User {
if let archivedObject = UserDefaults.standard.object(forKey:"CurrentUserAcc"){
if let user = NSKeyedUnarchiver.unarchiveObject(with: (archivedObject as! NSData) as Data) as? User {
return user;
}
}
return User()
}
func saveUser(){
let archivedObject : NSData = NSKeyedArchiver.archivedData(withRootObject: self) as NSData
UserDefaults.standard.set(archivedObject, forKey: "CurrentUserAcc");
UserDefaults.standard.synchronize();
}
func deleteUser(){
UserDefaults.standard.set(nil, forKey: "CurrentUserAcc")
UserDefaults.standard.synchronize();
}
}
Upvotes: 4