67

I am unable to add init method to the following UIViewController class. I need to write some code in the init method. Do i have to write init(coder) method? Even when I add the coder and decoder methods I still get errors. I also tried using the init method without any parameters but that also does not seem to work.

class ViewController: UIViewController {

    var tap: UITapGestureRecognizer?

    override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?)   {
        super.init(nibName: nil, bundle: nil)
        tap = UITapGestureRecognizer(target: self, action: Selector("handleTap:"))
    }

...
...
}

If I call the super.init() method without parameters the error is "Must call a designated initializer of super class" and if I pass the parameter nib and bundle then error is "Required initializer init(coder)".

Even when I add init(coder) and init(decoder) it does not work.

Karthik Kumar
  • 1,375
  • 1
  • 12
  • 29
Ankit Goel
  • 6,257
  • 4
  • 36
  • 48
  • What exactly do you want to add in your `init()`? If it's just the tap gesture, you could do it in `viewDidLoad()` as well, or even make it a lazy var. – Eendje Jun 06 '15 at 06:43

8 Answers8

70

I used:

convenience init() {
    self.init(nibName:nil, bundle:nil)
}

Some people suggested:

convenience init(){
    self.init()
}

But this gives you an infinite loop.

Glauco Neves
  • 3,483
  • 1
  • 24
  • 36
43

All of these answers are half-answers. Some people are experiencing infinite loops in related posts because they are only adding the convenience init() (it recursively calls itself if you don't providing a distinct init() method for it to invoke), while other people are neglecting to extend the superclass. This format combines all the solutions to satisfy the problem completely.

// This allows you to initialise your custom UIViewController without a nib or bundle.
convenience init() {
    self.init(nibName:nil, bundle:nil)
}

// This extends the superclass.
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
    super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
    tap = UITapGestureRecognizer(target: self, action: Selector("handleTap:"))
}

// This is also necessary when extending the superclass.
required init?(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented") // or see Roman Sausarnes's answer
}

Edit: Also, if you want to initialise any class properties using parameters passed into your convenience init() method without all the overridden init() methods complaining, then you may set all those properties as implicitly unwrapped optionals, which is a pattern used by Apple themselves.

Community
  • 1
  • 1
Jamie Birch
  • 5,839
  • 1
  • 46
  • 60
  • Can you show how to pass in parameters as described? – Petrus Theron Jul 31 '18 at 10:52
  • 2
    This answer is 100% wrong :) You ***do not*** need the convenience initializer. (1) If your goal is to add more initialization (ie, earlier than viewDidLoad) you need the two inits (and not the convenience init). (2) If your goal is to hand-initialize a view controller (for some bizarre reason) then you need the convenience init (and not the other two). Any newbie reading this answer will completely misunderstand what is going on. – Fattie Sep 10 '19 at 11:01
29

You need to override the init(nibName:bundle:) initializer, and provide the init(coder:) required initializer, and initialize tap in both of them:

class ViewController: UIViewController {

    var tap: UITapGestureRecognizer?

    override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?)   {
        print("init nibName style")
        super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
        tap = UITapGestureRecognizer(target: self, action: Selector("handleTap:"))
    }

    // note slightly new syntax for 2017
    required init?(coder aDecoder: NSCoder) {
        print("init coder style")
        super.init(coder: aDecoder)
        tap = UITapGestureRecognizer(target: self, action: Selector("handleTap:"))
    }
...
...
}

Also, be sure when calling the superclass initializer that you don't just pass nil for the nibName and bundle. You should pass up whatever was passed in.


Note that the "convenience init" method mentioned in some answers is only relevant if you are actually "by hand" yourself initializing a view controller. (There are very few situations where you would do that.) If you want to "do something during normal initialization" - so for example create a gesture recognizer or just initialize some variables - the only possible solution is exactly the one given in this answer.

Fattie
  • 27,874
  • 70
  • 431
  • 719
Aaron Rasmussen
  • 13,082
  • 3
  • 42
  • 43
  • 2
    Inside of `required init(coder aDecoder: NSCoder)`, you have `super.init()` ... shouldn't that be `super.init(coder: aDecoder)`? – Jim Rhoades Sep 29 '15 at 14:15
  • Indeed there were two trivial typos in the code example. You can see the typos easily just using autocomplete in Xcode. Roman, if you see this, I did edit the typos. Naturally feel free to change or unwind. This is the only correct answer here so I fixed the typos, cheers men. – Fattie Feb 05 '17 at 13:49
  • why do we need to initialize `tap` in both inits? thanks! – joliejuly May 06 '18 at 12:00
  • 2
    @joliejuly The "initialize tap" is in both inits because the inits are exclusive, either one or the other is called not both. That being said, it is duplicate code which should be extracted into a private function (e.g. private func commonInit() ) that is called by both public inits. – Vince O'Sullivan Oct 04 '18 at 08:09
8

You can just create a convenience initializer instead, convenience initializers don't override the init() instead it just calls it making things easier.

class ViewController: UIViewController {
    var tap: UITapGestureRecognizer?
    convenience init(){
        self.init()
        tap = UITapGestureRecognizer(target: self, action: Selector("handleTap:"))
    }
}
Icaro
  • 14,585
  • 6
  • 60
  • 75
6

I think you have to add

required init(coder aDecoder: NSCoder) {
    print("init coder")
    super.init(coder: aDecoder)
}

or

convenience init() {
    self.init()
}

You have to write init too. Don't remove it

var tap: UITapGestureRecognizer?

override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?)   {
    super.init(nibName: nil, bundle: nil)
}

Hope it helps

Jean-Baptiste Yunès
  • 34,548
  • 4
  • 48
  • 69
Ashish Kakkad
  • 23,586
  • 12
  • 103
  • 136
  • 3
    This gives the error "Must call a designated initializer of superclass" – Ankit Goel Jun 06 '15 at 05:24
  • I think we should overwrite all the designed init constructor from UIViewController, then the convenience "init()" method will be auto inherits from UIViewController, then we can create a convenience init method in subclass now. – Frank Cheng Dec 21 '18 at 22:27
3

I came across this Question of You asked long way Back. But no one has provided a basic answer to the question. It's a basic in Swift that:- Subclass must initialize its variables before it's super class initialization is complete.

Hence as Ankit Goel mentioned the crash :- "Must call a designated initializer of superclass"

Hence updated code would be:-

class ViewController: UIViewController {
    var tap: UITapGestureRecognizer?

    override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?)   {
        print("init nibName style")
        self.tap = nil
        super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
        self.tap = UITapGestureRecognizer(target: self, action: Selector(("handleTap:")))
    }

    required init?(coder aDecoder: NSCoder) {
        print("init coder style")
        self.tap = nil
        super.init(coder: aDecoder)
        tap = UITapGestureRecognizer(target: self, action: Selector(("handleTap:")))
    }
}
Nico S.
  • 3,056
  • 1
  • 30
  • 64
Vinod Supnekar
  • 143
  • 1
  • 7
3

I was facing the same issue with Swift 5 in Xcode 12.0 and this is my solution in 2020.

class Foo: UIViewController {
    private var bar: Bar!

    override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
        super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)

        setupController()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)

        setupController()
    }
    
    private func setupController() {
        bar = Bar()
    }
}

and an alternative I am using is to do the initialization inside loadView function.

class Foo: UIViewController {
    private var bar: Bar!
    
    override func loadView() {
        setupController()
    }

    private func setupController() {
        bar = Bar()
    }
}
XY L
  • 25,431
  • 14
  • 84
  • 143
1

Correct flow is, call the designated initializer which in this case is the init with nibName,

init(tap: UITapGestureRecognizer)
{
    // Initialise the variables
    tap = UITapGestureRecognizer(target: self, action: Selector(("handleTap:")))

    // Call the designated init of ViewController
    super.init(nibName: nil, bundle: nil)

    // Call your viewcontroller custom methods here
    setupViewControllers()
}
Rahul Bansal
  • 1,382
  • 15
  • 22