Swift: Required initializer giving me an error message - ios

I have a class named Alarm inheriting from NSObject, and in it, I have a property I'm having an issue with, alarmLastTriggeredDate:
class Alarm: NSObject {
var alarmLastTriggeredDate: NSDate
override init() {
super.init()
}
func encodeWithCoder(aCoder: NSCoder) {
aCoder.encodeObject(alarmLastTriggeredDate, forKey: "alarmLastTriggeredDate")
}
required init(coder aDecoder: NSCoder) {
if let alarmLastTriggeredDateDecoded = aDecoder.decodeObjectForKey("alarmLastTriggeredDate") as? NSDate
{
alarmLastTriggeredDate = alarmLastTriggeredDateDecoded
}
}
}
I'm new to Swift, and not sure why I'm getting the following errors:
#override init: Property 'self.alarmLastTriggeredDate' not initialized at super.init call
#required init: Property 'self.alarmLastTriggeredDate' not initialized at implicitly generated super.init call
It seems the only way to fix this problem is to initialized it in both places, but that's redundant code, and seems wrong. Am I missing something?

The compiler must be sure that every non optional property is successfully initialized:
before a call to a super init is performed
AND before the object initialisation has been completed
That's why you need to populate alarmLastTriggeredDate inside bot initializers.
And no, it's not redundant code since someone could use one of the 2 initializers to create your Alarm object.

Related

Error in Swift class: Property not initialized at super.init call - How to initialize properties which need use of self in their initializer parameter

I am overriding a UITableViewController in swift, in which I have two required variables which are initialized by using a weak reference of self since these are used to implement UITableViewDataSource protocol and need self reference to use its tableView property
class VideosListViewController: UITableViewController {
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.datasourceOfflineVideos = ASDataSource(tableViewController: self)
self.datasourceOnlineVideos = ASDataSource(tableViewController: self)
}
// MARK: - Variables
var datasourceOnlineVideos:ASDataSource
var datasourceOfflineVideos:ASDataSource
}
Now the problem is as it is giving me error Property not initialized at super.init call which is expected as explained here: Error in Swift class: Property not initialized at super.init call.
Swift’s compiler performs four helpful safety-checks to make sure that two-phase initialization is completed without error
Safety check 1 A designated initializer must ensure that all of the “properties introduced by its class are initialized before it delegates up to a superclass initializer.
Excerpt From: Apple Inc. “The Swift Programming Language.” iBooks. https://itunes.apple.com/us/book/swift-programming-language/id881256329?mt=11
So my question is:
If an instance variable in swift class needs self reference to be initialized like ASDataSource type variables here, how can I do that??
Since swift doesn't allow me call the super.init() before initiazing all the instance variables And also doesn't allow me to user self within the initializer in the init() before calling super.init()
Currently I am using optional variable(s) to resolve the issue. But for learning purpose I want to know how to do that. Thanks in advance :)
You just have to invert the order super.init/properties in your initializer:
required init(coder aDecoder: NSCoder) {
self.datasourceOfflineVideos = ASDataSource(tableViewController: self)
self.datasourceOnlineVideos = ASDataSource(tableViewController: self)
super.init(coder: aDecoder)
}
instance properties comes first, then the superclass initializer can be invoked. But that's not possible in your case, because you are referencing self.
The workaround in this case is to make the properties implicitly unwrapped optionals:
var datasourceOnlineVideos:ASDataSource!
var datasourceOfflineVideos:ASDataSource!
Since optionals don't need to be initialized, you can safely initialize them after the super.init method has been called. Being implicitly unwrapped, you use them as any other non optional property.
This pattern is used by Apple in several classes, including UIViewController: when you add an outlet from IB, the component property is always declared as implicitly unwrapped. That's because the control itself is not instantiated in the initializer, but at a later stage.

UITableViewCell and Cannot invoke 'init' with an argument list of type '(coder: NSCoder)'

So Ive built a custom cell class for my UITableView. Xcode insists that this UITableViewCell has the following initializer (Xcode actually auto-fills this code for me):
class customCell: UITableViewCell {
required init(coder aDecoder:NSCoder) {
fatalError("init(coder:) has not been implemented")
}
Ive modified the contents of this initializer to contain the following:
// fatalError("init(coder:) has not been implemented")
super.init(coder: aDecoder)
If I remove the keyword required then Xcode kicks out an error and wants to correct me by putting the keyword back in.
So in my UITableView class I try to register this cell like so:
override func viewDidLoad(){
super.viewDidLoad()
let coder: NSCoder = NSCoder()
let customCell: AnyClass = customCell(coder: coder)
table?.registerClass(customCell, forCellReuseIdentifier:"cell")
The penultimate line (the one where I initialize the customCell with the NSCoder, as Xcode wants me to) produces the error:
Cannot invoke 'init' with an argument list of type '(coder: NSCoder)'
If I can I'd prefer to build my own initializer without an NSCoder.
If I try to use any other initializer then Xcode kicks out an error along the lines of:
Extra argument 'reuseIndenifier' in call
or
Missing argument in call
It will continue complaining until I am using the initializer with NSCoder. I am forced into using this initializer.
Please correct me if I am way off here, but to me it seems like I am caught in an catch 22 situation - Xcode complains if I build an initializer without a coder, and then when I try to use an initializer with a coder it complains that I am using a coder.
Despite this apparent contradiction I am fairly convinced all of this is a result of me being a dumbass somewhere.
Would anyone out there be kind enough to point me in the right direction?
You need to register the class itself not an instance of it.
eg.
tableView?.registerClass(customCell.self, forCellReuseIdentifier:"cell")
The issue with requiring init(coder aDecoder:NSCoder) is a separate one and discussed here: 'required' initializer 'init(coder:)' must be provided by subclass of 'UITableViewCell'`

Changes on NSCoding protocol

I just came back on the development of a Swift application for iOs that I started back on August. It seems that a lot of changes happened. Now my NSCoding protocol is not valid anymore and I cannot find what's wrong. Actually I do but I don't know how to implement my old code with the new protocol. Here is my old init(coder: NSCoder) :
required init(coder: NSCoder!) {
if (coder != nil) {
//Variables initialization
}
else {
//Defaults values
}
super.init()
}
Actually, I cannot use NSCoder! anymore so I cannot check if there is a coder so if I have to set default values or not. The question is : When coder is nil, what initializer is called ? Is it init or init(coder: NSCoder!) ? If it's the second one, how to do what I use to do (with the code above) ?
With the updated protocol it is illegal for a Swift class implementing NSCoding to receive nil in initWithCoder. This should simplify your code since a nil coder is an exceptional case and the program should have taken an alternative path when it couldn't create the decoder.
If you really have two paths you can break your init into two methods:
override init() {
//Variables initialization
super.init()
}
required init(coder: NSCoder) {
//Defaults values
super.init()
}
This does have implications for Objective-C code consuming this class since passing nil will crash the app. Swift code will be safe since it will generate a compilation error is you pass an optional.

Swift: odd XCode behaviour with required initializer in subclass of NSArray

Issue background: I have a class that is subclass of NSArray, not directly but it is.
Structure is more or less like this MyClass -> Class1 -> Class2 -> NSArray.
Everything except MyClass is in Objective-C, MyClass is in Swift. It worked well until I upgraded to Yosemite and Xcode 6.1.
Now, during compilation it throws an error
'required' initializer 'init(arrayLiteral:)' must be provided by subclass of 'NSArray'
Which is pretty odd because there are other classes, siblings to MyClass without compilator complaining about them.
When I add the initializer,
required convenience init(arrayLiteral elements: AnyObject...) {
fatalError("not implemented")
}
XCode throws another error saying Declarations from extensions cannot be overridden yet.
Does anybody have any idea what can I do? Code has zero changes at all.
You might just not see the compilation errors in the other files since compilation tries to stop at the first problematic file.
If you don't specify any designated initializers in your subclass or if you override all designated initializers but no convenience initializers, the problematic initializer is inherited automatically. In that case it compiles fine:
class MyArray1: NSArray {
override init() { fatalError("todo") }
override init(objects: UnsafePointer<AnyObject?>, count cnt: Int) { fatalError("todo") }
required init(coder aDecoder: NSCoder) { fatalError("todo") }
}
class MyArray2: MyArray1 {
}
This compiles just fine on Xcode 6.1.
See Automatic Initializer Inheritance in The Swift Programming Guide.

Using superclass's convenience initializers in subclass instantiation

This occurred in a playground in XCode6-Beta5
I'm trying to add some functionality to SKSpriteNode in a subclass, Sprite, but I still want to use the convenience initializers in SKSpriteNode. However, I get the following error:
"Cannot convert the expression's type 'Sprite' to type 'Sprite'"
from this code:
import Cocoa
import SpriteKit
class Sprite : SKSpriteNode {
// The following code makes this not compile
// required init(coder aDecoder: NSCoder!) {
// super.init(coder: aDecoder)
// }
}
var sprite = Sprite(imageNamed: "Rayman1.png") // Error occurs on this line
The convenience initializer in question is declared like this:
convenience init(imageNamed name: String!)
What am I doing wrong?
Initializer Chaining rules as specified in the Swift Programming Guide, which reads:
Designated initializers must call a designated initializer from their immediate superclass.
If you are calling superclass's convenience initialize in sub class instantiation then this is not allowed.
Hope this helps.. :)
I had to get rid of the init(coder aDecoder: NSCoder!) method, and that fixed everything. I have no idea why.

Resources