What Exactly Is Init Coder Adecoder

The optionality of init?(coder:) vs init(coder:). What to do if nil?

You'd better not return nil.

As my test in Xcode 8.3.2 (8E2002), return nil in init(coder:) cause NSKeyedUnarchiver.unarchiveObject crash or return unexpected result.

Prepare a class which encode wrong data type for "test2":

class MyClass: NSObject, NSCoding {
var x: String

init(_ x: String) {
self.x = x
}

required init?(coder aDecoder: NSCoder) {
guard let x = aDecoder.decodeObject(forKey: "x") as? String else {
return nil
}
self.x = x
}

func encode(with aCoder: NSCoder) {
if x == "test2" {
aCoder.encode(Int(4), forKey: "x")
} else {
aCoder.encode(x, forKey: "x")
}
}
}

TestCaseA: archive a dictionary which contains above MyClass, then unarchive.

Result: crash on NSKeyedUnarchiver.unarchiveObject.

    let encodedData = NSKeyedArchiver.archivedData(withRootObject: [
"k1":MyClass("test1"),
"k2":MyClass("test2"),
"k3":"normal things"
])
UserDefaults.standard.set(encodedData, forKey: "xx")

if let data = UserDefaults.standard.data(forKey: "xx"),
let _data = NSKeyedUnarchiver.unarchiveObject(with: data) {
if let dict = _data as? [String:Any] {
debugPrint(dict.count)
}
}

TestCaseB: archive an array which contains above MyClass, then unarchive.

Result: return an empty array (but expected is an array with 1 element)

    let encodedData = NSKeyedArchiver.archivedData(withRootObject: [
MyClass("test1"),
MyClass("test2")
])
UserDefaults.standard.set(encodedData, forKey: "xx")

if let data = UserDefaults.standard.data(forKey: "xx"),
let _data = NSKeyedUnarchiver.unarchiveObject(with: data) {
if let dict = _data as? [Any] {
debugPrint(dict.count)
}
}

Using methods in the required init(coder aDecoder: NSCoder) in Swift

@Paulw11 was on the money, you have to do the following:

required init(coder aDecoder: NSCoder) {
selectedFocusAreas = Array()
setUpSelectedFocusAreaData()
super.init(coder: aDecoder)!
}

func setUpSelectedFocusAreaData(){
for _ in 0...focusAreas.count {
selectedFocusAreas.append(false)
}
}

i.e initialize the value first and then call the method.

What is the problem with this init(coder:) function?

Given that box and hero are not optional, they must be initialized in all initializers.

You have done it for your custom initializer but you also need to do it for init with coder since that initializer can be invoked separate from your custom initializer.

Handling Object init with coder vs from Json?

Make your decoder function a required init and not convenience init.

required init(coder aDecoder: NSCoder) {
self.apiKey = aDecoder.decodeObject(forKey: "apiKey") as! String
self.userID = aDecoder.decodeObject(forKey: "userID") as! String
self.userEmail = aDecoder.decodeObject(forKey: "userEmail") as! String
self.lastName = aDecoder.decodeObject(forKey: "lastName") as! String
self.firstName = aDecoder.decodeObject(forKey: "firstName") as! String
self.company = aDecoder.decodeObject(forKey: "company") as! String
self.userImage = aDecoder.decodeObject(forKey: "userImage") as! String
self.jobTitle = aDecoder.decodeObject(forKey: "jobTitle") as! String
}

Thats all :) Hope it helps

What is the issue ?

In iOS all convenience init should ultimately call designated initializer. In your case you already have a designated initializer which accepts jSON. Now two solutions.

One declare a designated init which takes all the values like the one you used in last line of init(coder aDecoder: NSCoder) of your code and make your init(_ json: JSON) also a convenience initializer and finally in both init(coder aDecoder: NSCoder) and init(_ json: JSON) call designated initializer

else simply use the second and easy approach I specified above.

EDIT:

if i make it required, how am i doing to init with JSON ? ill need to do this too?

Yes you need to write required init(coder aDecoder: NSCoder) { because you are confirming to NSCoding protocol and NSCoding protocol expects you to implement this init. Hence its a required init.

where as your init(_ json: JSON) { is a designated initializer of your class.

Whats the difference between designated initializer and required initializer ?

Please read : What's the difference between a required initializer and a designated initializer?

As it clearly concludes

  • Required initialisers don't have to be designated.
  • Designated initialisers don't have to be required.

Why should we use init(coder) when we can just dump everything inside awakeFromNib?

If you have lets that need to be initialized in an init, you have to use that instead of awakeFromNib.

Doing so allows you to avoid implicitly unwrapped optionals.

EDIT:

If you want your class to have properties, you can either do

 let a: String

or

 var a: String! = nil // this is called an "implicitly unwrapped optional" -- it's the ! at the end of the type that makes it that.

The first is preferable because it is safe. In the second, you run the risk of accessing a before it is initialized.

But, to make sure a is always initialized, it needs to get its value in an init of the class.

So,

init(coder aCoder : NSCoder) {
a = "hello" // usually this is something more complex
// read in coder or whatever else you need to do
}

If you don't have an init, then you can't have an init that gets initialized later.



Related Topics



Leave a reply



Submit