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
How to Copy SQLite Database When Application Is Launched in iOS
Get the Frame of Uibarbuttonitem in Swift
Alternative to Usernotificationcenterdelegate's Willpresent When App Is in Background
Programmatically Go Back to Previous Viewcontroller in Swift
Access Files in /Var/Mobile/Containers/Data/Application Without Jailbreaking Iphone
Why Is There an Frame Rectangle and an Bounds Rectangle in an Uiview
iPhone Uitableview. How Do Turn on the Single Letter Alphabetical List Like the Music App
How to Implement a Box or Gaussian Blur on iOS
Could Not Launch Process Launch Failed: Timed Out Waiting for App to Launch
Firebase Chat Push Notifications
Return Value from Completion Handler - Swift
Swift Force-Unwrapping Exception Not Propagated
Add a Watermark on Video After Merging Video and Audio Asset into One in Swift3 iOS
Run iPhone as an Ibeacon in the Background
Expanding and Collapsing Uitableviewcells with Datepicker
iOS Builds/Ipa Creation No Longer Works from the Command Line