3
votes

Part 1

I am writing a Today Widget extension using NSExtensionPrincipalClass instead of storyboard. I implemented the following required init

required init(coder aDecoder: NSCoder) {
    tableView = UITableView()
    cellIdentifier = "kCellIdentifier"

    super.init(coder: aDecoder)
}

However, when I run the project I get the following error

use of unimplemented initializer 'init()'

So, by putting this additional code in the project, it will fix the problem but it doesn't seem right to initialize variables in multiple places

override init() {
    tableView = UITableView()
    cellIdentifier = "kCellIdentifier"

    super.init()
}

Part 2

So I answered part 1 of my question below but I still don't know why the following solution wouldn't work?

override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) {
    cellIdentifier = "kCell"
    tableView = UITableView()
    super.init(nibName: nil, bundle: nil)
}

As per Swift's documentation

“If your subclass provides an implementation of all of its superclass designated initializers—either by inheriting them as per rule 1, or by providing a custom implementation as part of its definition—then it automatically inherits all of the superclass convenience initializers.”

In UIViewController, init(nibName:bundle:) is the only designated initialized so why am I not automatically inherits init() and init(coder:)?

1
Swift's handling of initializers is sorta counterintuitive. In Obj-C, all initializers are inherited by every sub-class, no matter how distant they are in the hierarchy. In swift, initializers are only inherited in specific cases. What you're probably seeing is Obj-C calling an initializer which would automatically be inherited in Obj-C but isn't in Swift. I've run across this myself. Ultimately, I went back and re-read the initializers section of the Swift guide half a dozen times until it made sense. That's what I'd suggest. :-) - Anna Dickinson
Yes the NSCoder business is a mess with Swift. I am doing the same as you. If you init a controller from another controller, it doesnt need the aDecoder, but if you init from storyboard, it does. Messy, agreed, but so far I have found no work around. I even asked about it here: stackoverflow.com/questions/26394009/… - Aggressor

1 Answers

4
votes

So here is the solution to the first part of my question after re-read swift's initializer chapter and look at UIViewController header file

required init(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
}

override init() {
    cellIdentifier = "kCellIdentifier"
    tableView = UITableView()
    super.init(nibName: nil, bundle: nil)
}

One of the key to this solution is init(nibName:bundle:) which is a designated initializer which you must call whether you use storyboard or not.

Keys take away:

  • If you don't provide a value for your variables when you declare them then you lose all the initializers inheritance from your superclass. This means that you must provide your own init or override superclass initializers and call the appropriate designated initializer of your superclass. Additionally, you must implement all required initializers.
  • By default in UIViewController, the convenience init will call init(nibName:bundle:) for you using nil as arguments. So if you are not inherited the default init() method then it is your responsibility to override it and call init(nibName:bundle:)