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:
- In your
Child
class, theparent
property needs to beweak
. Otherwise you have a reference cycle and lots of memory issues. - Your
Child
class should make no attempt to encode/decode its parent. - 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
Dynamic Uitablecellview Height
What Is the Simplest Way to Retrieve the Device Serial Number of an iOS Device Using Monotouch
How to Convert a Persian Date into a Gregorian in Swift
Avcapturevideopreviewlayer Add Overlays and Capture Photo in iOS
Snapshot of Swiftui View Is Partially Cut Off
iOS Sound Not Playing in Swift
Nsurlsession/Nsurlconnection Http Load Failed (Kcfstreamerrordomainssl, -9802) on a Subdomain
Problems with Cropping a Uiimage in Swift
Wkwebview Decidepolicyfornavigationaction Being Call After the Long Press Recogniser Ended
Subview Gesture Recognizer Not Being Called
Xcode 11 Archive Gives Phasescriptexecution Failed
Game Exits from Pause State After Resuming It from Background in Swift
Records, Zone Doesn't Displayed in Dashboard and Delete Zone Issue Cloudkit
iOS 10.3 - How to Change App Icon Programmatically
Swift - Update/Refresh Label That Displays Time
Invert Calayer Mask That Is Based on a Stroke (No Fill)