92
votes

How do you add custom initializers to UIViewController subclasses in Swift?

I've created a sub class of UIViewController that looks something like this:

class MyViewController : UIViewController
{
    init(leftVC:UIViewController, rightVC:UIViewController, gap:Int)
    {
        self.leftVC = leftVC;
        self.rightVC = rightVC;
        self.gap = gap;

        super.init();

        setupScrollView();
        setupViewControllers();
    }
}

When I run it I get a fatal error:

fatal error: use of unimplemented initializer 'init(nibName:bundle:)' for class 'MyApp.MyViewController'

I've read elewhere that when adding a custom initializer one has to also override init(coder aDecoder:NSCoder) so let's override that init and see what happens:

override init(coder aDecoder: NSCoder)
{
    super.init(coder: aDecoder);
}

If I add this, Xcode complains that self.leftVC is not initialized at super.init call. So I guess that can't be the solution either. So I wonder how can I add custom initializers properly to a ViewController subclass in Swift (since in Objective-C this seems not to be a problem)?

4
How are you trying to instantiate your MyViewController? - Mike Pollard
Why not instantiate everything in viewDidLoad() ,there you can initialise them just the way you want - tudoricc
@MikePollard with dualViewCtrl = DualViewCtrl(leftVC: l, rightVC: r, gap: 50) ... I already found out I need to use the initWithNib initializer. - BadmintonCat
@tudoricc Because often you want instance properties that are safely initialized in the initializer with given parameters. You can't do that in viewDidLoad since then it would not be guaranteed that the properties are available when needed. - BadmintonCat
I am sorry is just that in my mind I see the viewDidLoad() as an initialiser. thanx for the explanation - tudoricc

4 Answers

130
votes

Solved it! One has to call the designated initializer which in this case is the init with nibName, obviously ...

init(leftVC:UIViewController, rightVC:UIViewController, gap:Int)
{
    self.leftVC = leftVC
    self.rightVC = rightVC
    self.gap = gap

    super.init(nibName: nil, bundle: nil)

    setupScrollView()
    setupViewControllers()
}
15
votes

For a more generic UIViewController you can use this as of Swift 2.0

init() {
    super.init(nibName: nil, bundle: nil)
}
13
votes

Swift 5

If you want to write custom initializer to UIViewController which is initialized with storyBoard.instantiateViewController(withIdentifier: "ViewControllerIdentifier")

You can write custom initializer for only Optional properties.

class MyFooClass: UIViewController {
    var foo: Foo?

    init(with foo: Foo) {
        self.foo = foo
        super.init(nibName: nil, bundle: nil)
    }

    public required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        self.foo = nil
    }
}
11
votes

Not sure if you managed to fully solve this... but depending on how you want your class's interface to look and whether or not you actually need the coder functionality, you can also use the below:

convenience required init(coder aDecoder: NSCoder)
{
    //set some defaults for leftVC, rightVC, and gap
    self.init(leftVC, rightVC, gap)
}

Since init:leftVC:rightVC:gap is a designated initializer, you can fulfill the requirement of implementing init:coder by making it a convenience initializer that calls your designated initializer.

This could be better than

override init(coder aDecoder: NSCoder)
{
    super.init(coder: aDecoder);
}

because if you need to initialize some properties, then you would need to rewrite them.