95

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)?

mfaani
  • 33,269
  • 19
  • 164
  • 293
BadmintonCat
  • 9,416
  • 14
  • 78
  • 129
  • How are you trying to instantiate your `MyViewController`? – Mike Pollard Aug 27 '14 at 12:58
  • Why not instantiate everything in viewDidLoad() ,there you can initialise them just the way you want – tudoricc Aug 27 '14 at 12:59
  • @MikePollard with dualViewCtrl = DualViewCtrl(leftVC: l, rightVC: r, gap: 50) ... I already found out I need to use the initWithNib initializer. – BadmintonCat Aug 27 '14 at 13:01
  • 3
    @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 Aug 27 '14 at 13:03
  • I am sorry is just that in my mind I see the viewDidLoad() as an initialiser. thanx for the explanation – tudoricc Aug 27 '14 at 13:05
  • @tudoricc yw! viewDidLoad() is not an initializer and to provide required properties other than over the initializer doesn't guarantee that they are available. In my case these are the two view controllers (leftVC & rightVC) and gap. – BadmintonCat Aug 27 '14 at 13:08
  • @Avalon I saw those things as buttons. – tudoricc Aug 27 '14 at 13:10
  • Initializers are good for programmatically created viewControllers, but for viewcontrollers created through storyboard [you're out of luck and have to work your way around it](https://stackoverflow.com/a/39400793/5175709) – mfaani Aug 27 '19 at 20:30

4 Answers4

134

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()
}
Matthieu Riegler
  • 31,918
  • 20
  • 95
  • 134
BadmintonCat
  • 9,416
  • 14
  • 78
  • 129
  • 38
    Get rid of those ugly semi colons :) – MobileMon Mar 31 '15 at 19:48
  • 9
    I like semicolons. They make code less ambiguous, especially with those ridiculously verbose Cocoa/Cocoa-Touch API names and signatures. Bad decision from the Swift designers to remove them IMHO. – BadmintonCat Jun 07 '15 at 14:30
  • 29
    IMHO, if you type a character and it doesn't effect the compiled binary, you shouldn't type it :) – Sam Soffes Jul 07 '15 at 19:10
  • 37
    @SamSoffes so you don't use (extra) whitespace at all? And you obfuscate all symbols to be single characters? – Victor Jalencas Oct 13 '15 at 10:00
  • There are situations where semicolons make code easier to read. It's also probably of advantage for (future) code formatters. More information is better than few information. – BadmintonCat Oct 16 '15 at 04:42
  • 1
    Also, when adding a semicolon, the line of code automatically indents itself. I'm keeping them in. – Buyin Brian Feb 17 '16 at 14:43
  • 4
    The code should automatically indent itself regardless. Also, semicolons are redundant in this case as both the compiler and human know that it's the end of a statement. Getting rid of the semicolon provides for cleaner code by getting rid of redundancies and getting straight to the point. – Harish Aug 02 '17 at 05:27
  • 14
    This discussion reminds me of tabs vs spaces in Silicon Valley – Aaron Sep 19 '17 at 15:41
  • I have a custom initializer in a `UITableViewController` subclass, and this doesn't seem to work for me. Even though I call `super.init(nibName: "MyNibName", bundle: nil)` in the custom initializer, after I initialize all my custom properties, Xcode still complains that I must provide `init(coder:)`. – Apoorv Khatreja Feb 12 '18 at 18:45
  • What if you don't have a nib for this VC? – Jake T. Jul 11 '18 at 15:13
  • Oh never mind, I see. Guess I blanked on what you were inputting since I saw the nibName and immediately was like "well, can't use this because I have no nib..." So, for anyone else who completely missed it, you can pass `nil` for the nibName and bundle. – Jake T. Jul 11 '18 at 16:21
  • 1
    I like to put two semi-colons.. ((Just kidding..)) – zekel Nov 23 '18 at 04:37
  • @BadmintonCat How could semicolons make the code easier to read? They always go at the end of the line (unless the line has multiple statements, which almost no one does), which means they don't provide any disambiguation. – Peter Schorn Jan 16 '21 at 03:11
19

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
    }
}
emrcftci
  • 3,355
  • 3
  • 21
  • 35
  • 2
    "You can write custom initializer for only Optional properties." is the key. That I missed initially. – sarawanak Jul 03 '21 at 08:43
18

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

init() {
    super.init(nibName: nil, bundle: nil)
}
Tr0yJ
  • 3,274
  • 1
  • 22
  • 30
11

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.

Michal
  • 15,429
  • 10
  • 73
  • 104
wlingke
  • 4,699
  • 4
  • 36
  • 52