When Archive Object in the Func Encode(With Acoder: Nscoder) Method Crashed with Swift Enum in Real Revice

When archive object in the func encode(with aCoder: NSCoder) method crashed with swift enum in real revice

Try this for your Question

func encode(with aCoder: NSCoder) {
aCoder.encode(type.rawValue, forKey: "type")
}

For More Information

Let Discuss this problem in details

For example, this is my Enum:

enum PieceType : Int {
case empty
case notEmpty
}

And this is my Object which is child of NSObject

class Piece: NSObject, NSCoding {
var islogin: Bool
var type: PieceType
var username: String!
var password: String!

override init() {
islogin = false
type = PieceType.empty
username = ""
password = ""
}

required init(coder aDecoder: NSCoder) {
islogin = aDecoder.decodeBool(forKey: "islogin")
type = PieceType(rawValue: aDecoder.decodeObject(forKey: "type") as! Int)!
username = aDecoder.decodeObject(forKey: "username") as! String
password = aDecoder.decodeObject(forKey: "password") as! String

}

func encode(with aCoder: NSCoder) {
aCoder.encode(islogin, forKey: "islogin")
aCoder.encode(type.rawValue, forKey: "type")
aCoder.encode(username, forKey: "username")
aCoder.encode(password, forKey: "password")

}

}

When you call NSKeyedArchiver.archiveRootObject(::) it will call func encode(with aCoder: NSCoder) method and convert your NSObject to Data And when you try to Unarchive your object it will call init(coder aDecoder: NSCoder) method and convert Data to NSObject by using Key.

But in Enum case you can not encode enum directly B'Coz it is User Define data type but rawValue is must be inbuilt data type like Int, String,Float.....So on.
that's why when you try to encode enum you need to use rawValue

I Hope you will get point.

NSCoding - Save the parent attribute causes a crash

Think through what is currently happening in your code. You archive a parent. The parent archives its children. Each of those children then tries to archive its parent. Then each of those parents try to archive its children. This cycle then goes on and on until it goes "boom".

You have a few issues:

  1. In your Child class, the parent property needs to be weak. Otherwise you have a reference cycle and lots of memory issues.
  2. Your Child class should make no attempt to encode/decode its parent.
  3. Your Parent class should set itself as the parent of each child when the parent is decoded.

Your crash is caused by violating issue 2.

As a side note to issue 3, I would refactor your code. Do not pass a parent when creating a child. And do not directly expose the children array in your Parent class. I would add methods to the Parent class for adding and obtaining children. The method to add children should set the parent property of each child added to it.

How to archive and unarchive custom objects in Swift? Or how to save custom object to NSUserDefaults in Swift?

NSKeyedArchiver will only work with Objective-C classes, not pure Swift classes. You can bridge your class to Objective-C by marking it with the @objc attribute or by inheriting from an Objective-C class such as NSObject.

See Using Swift with Cocoa and Objective-C for more information.

NSKeyedArchiver.archivedData does not work in Swift 3 iOS

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()

NSKeyedArchiver - Unrecognized selector sent to instance error

Along with inputs from JPetric and Valentin Radu I have modified the code by a little and the error is removed.

Below is my solution:

class UserDetails : NSObject,NSCoding {

//var token:String?
var token:String!

//var agentId: Int?
var agentId: Int!

//var userId:Int?
var userId:Int!

//to directly get values from JSON output

init?(user: [String: Any]) {

guard let token = user["token"] as? String,
let agentId = user["agent_id"] as? Int,
let userId = user["user_id"] as? Int
else{
return nil
}

self.token = token;
self.agentId = agentId;
self.userId = userId;
}

init(pToken:String,pAgentId:Int,pUserId:Int)
{
token = pToken;
agentId = pAgentId;
userId = pUserId;
}

//TO SAVE IN NSUSERDEFAULTS

required init?(coder aDecoder: NSCoder){
self.token = aDecoder.decodeObject(forKey: "token") as? String
self.agentId = aDecoder.decodeObject(forKey: "agentId") as? Int
self.userId = aDecoder.decodeObject(forKey: "userId") as? Int

return self
}

public func encode(with aCoder: NSCoder) {
aCoder.encode(token,forKey:"token")
aCoder.encode(agentId,forKey:"agentId")
}

}

Saving custom Swift class with NSCoding to UserDefaults

As @dan-beaulieu suggested I answer my own question:

Here is the working code now:

Note: Demangling of the class name was not necessary for the code to work in Playgrounds.

import Foundation

class Blog : NSObject, NSCoding {

var blogName: String?

override init() {}

required init(coder aDecoder: NSCoder) {
if let blogName = aDecoder.decodeObjectForKey("blogName") as? String {
self.blogName = blogName
}
}

func encodeWithCoder(aCoder: NSCoder) {
if let blogName = self.blogName {
aCoder.encodeObject(blogName, forKey: "blogName")
}
}

}

let ud = NSUserDefaults.standardUserDefaults()

var blog = Blog()
blog.blogName = "My Blog"

ud.setObject(NSKeyedArchiver.archivedDataWithRootObject(blog), forKey: "blog")

if let data = ud.objectForKey("blog") as? NSData {
let unarc = NSKeyedUnarchiver(forReadingWithData: data)
let newBlog = unarc.decodeObjectForKey("root") as Blog
}


Related Topics



Leave a reply



Submit