Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

The correct way to override an initializer in Swift 1.1

This used to work in Xcode 6.1 beta:

class MainViewController: NSViewController {
  convenience override init() {
    self.init(nibName: "MainView", bundle: nil)
  }
}

After I switch to 6.1 GM2, it doesn't compile. Looks like the issue is related to "failable initializers" introduced in Swift 1.1. I've tried convenience override init?(), convenience init?() and override init?(), neither worked.

So what's the correct way to override this kind of initializers as of today?

like image 880
Ethan Avatar asked Oct 08 '14 17:10

Ethan


People also ask

What does init () do in Swift?

Swift init() Initialization is the process of preparing an instance of a class, structure, or enumeration for use. This process involves setting an initial value for each stored property on that instance and performing any other setup or initialization that is required before the new instance is ready for use.

How do you designate a Failable initializer?

You write a failable initializer by placing a question mark after the init keyword ( init? ). Note: You cannot define a failable and a nonfailable initializer with the same parameter types and names. A failable initializer creates an optional value of the type it initializes.


1 Answers

You're trying to implement init() — a non-failable initializer — by delegating to init?(nibName:bundle:), which is a failable initializer. This doesn't work: if the super.init call fails, you'd be left with a non-initialized instance, which Swift won't allow.

Or to put it another way, the result of using a failable initializer is an optional, and you can't use an optional in place of a non-optional value. And in the case of class initialization and inheritance, you can't substitute a non-optional self for an optional one — you can only delegate the setup of self's state to a different initializer.

Instead, you can remove optionality/failability with a little song and dance:

class MainViewController: NSViewController {
    override init!(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) {
        super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
        // check state here and provide app-specific diagnostic if it's wrong
    }
    convenience override init() {
        self.init(nibName: "MainView", bundle: nil)
    }

    // need this, too, or the compiler will complain that it's missing
    required init?(coder: NSCoder) {
        fatalError("not implemented") // ...or an actual implementation
    }
}

An init! initializer produces an implicitly unwrapped optional (IUO) — just as an IUO type can be used to bridge between code that works with optional and non-optional values, an init! initializer can bridge between failable and non-failable initializers. You can't delegate from a non-failable initializer to a failable initializer, but you can delegate from a non-failable initializer to an init! initializer and from an init! initializer to a failable initializer.

Here, the NSViewController initializer you want to use is fully failable, so you override it with an init! initializer. Then, you can declare a non-failable convenience init that delegates to your new init! initializer.


We often tend to avoid IUOs, and by extension init! initializers, because we generally want to either explicitly allow for (and require handling) failure or explicitly disallow it. However, one of the strongest general use cases for IUOs and their kin is to turn conditions that are guaranteed only outside of your source code into assertions that your code can treat as infallible. IBOutlets are a great example of this — in your nib/storyboard you guarantee the state of your IBOutlet variables, but the compiler doesn't know about that — as are just about anything else to do with bundle resources.

This little delegation dance puts the burden of failure at a specific, easily debuggable place in your code — if the call from init() to super.init(nibName:bundle:) fails, your app will crash. But you can expect that call to fail only in very specific (and mostly at-development-time) conditions.

like image 118
rickster Avatar answered Oct 10 '22 16:10

rickster