108

I am not using a UIViewController from a storyboard and I want to have a custom init function where I pass in an NSManagedObjectID of some object. I just want to call super.init() like I have in Objective-C. Like this:

init(objectId: NSManagedObjectID) {
    super.init()
}

But I get the following compiler error:

Must call designated initializer of the superclass UIViewController

Can I simply not do this anymore?

shim
  • 9,289
  • 12
  • 69
  • 108
SirRupertIII
  • 12,324
  • 20
  • 72
  • 121

4 Answers4

140

The designated initialiser for UIViewController is initWithNibName:bundle:. You should be calling that instead.

See http://www.bignerdranch.com/blog/real-iphone-crap-2-initwithnibnamebundle-is-the-designated-initializer-of-uiviewcontroller/

If you don't have a nib, pass in nil for the nibName (bundle is optional too). Then you could construct a custom view in loadView or by adding subviews to self.view in viewDidLoad, same as you used to.

occulus
  • 16,959
  • 6
  • 53
  • 76
  • 4
    So it is currently impossible for me to make a custom init method in a UIViewController subclass that is not from a nib? – SirRupertIII Jun 07 '14 at 08:01
  • 1
    Ahh, thanks!! I was passing in "" for the nib name. – SirRupertIII Jun 07 '14 at 08:05
  • Overriding `initWithNibName:bundle:` alone is not enough; the compiler will throw an error and suggest you also implement the (required) `initWithCoder:`. I guess both are designated initializers? But only `withCoder` gets marked as "required" in swift... – Nicolas Miari Jul 16 '15 at 01:56
  • 2
    Oh, after a little digging, it turns out `initWithCoder:` comes from the `NSCoding` protocol... I still can't figure out what's its role in initializing a UIViewController instance, whether it is a "designated" initializer of UIViewController or any of its superclasses... – Nicolas Miari Jul 16 '15 at 02:01
  • 8
    Thanks! For quick copy/paste just call this after setting your `let` properties, if any: `super.init(nibName: nil, bundle: nil)`. – Ferran Maylinch Mar 29 '17 at 15:55
  • 3
    Init With coder is used for when you need to do state restoration. When you save the state of the viewcontroller by using the state restoration functionality, the coder will return the values you saved when the state was saved. From there you can rebuild the view controller to its last initial state from when you closed out the application. So to the user its where they left off from – Esko918 Nov 26 '17 at 07:18
47

Another nice solution is to declare your new initializer as a convenience initializer as follows:

convenience init( objectId : NSManagedObjectID ) {
    self.init()

    // ... store or user your objectId
}

If you declare no designated initializers in your subclass at all, they are inherited automatically and you are able to use self.init() within your convenience initializer.

In case of UIViewController the default init method will call init(nibName nibNameOrNil: String!, bundle nibBundleOrNil: NSBundle!) with nil for both arguments (Command-Click on UIViewController will give you that info).

TL;TR: If you prefer to programmatically work with UIViewControllers here is a complete working example that adds a new initializer with a custom argument:

class MyCustomViewController: UIViewController {
    var myString: String = ""

    convenience init( myString: String ) {
        self.init()

        self.myString = myString
    }
}
Eric Aya
  • 69,473
  • 35
  • 181
  • 253
Klaas
  • 22,394
  • 11
  • 96
  • 107
  • This is a more elegant solution and works in any generic case – Ciprian Mar 18 '15 at 12:42
  • 2
    I tried doing this in an extension, and it recursively called itself. – Danyal Aytekin May 08 '15 at 10:23
  • @DanyalAytekin when you provide your code as a gist, I can tell you why. – Klaas May 13 '15 at 08:31
  • 19
    The "trick" to this answer is that `myString` is set to `""` so the `init` is not required. This solution falls apart if you don't want to use an initial value, and in that case you need to use `self.init(nibName: nil, bundle:nil)` – Dan Rosenstark Jun 07 '15 at 20:23
  • 2
    @Yar or you make the `myString` property an optional – Klaas Jun 10 '15 at 12:22
  • 1
    @Klaas yes: my issue is that I was using the initializers ONLY to avoid optionals and get my code to compile. – Dan Rosenstark Jun 10 '15 at 14:14
  • @DanyalAytekin (and others): see my answer [in this related question](http://stackoverflow.com/a/43525331/5951226) to solve the infinite recursion problem. – Jamie Birch Apr 20 '17 at 16:46
33

To improve the occulus's answer:

init() {
     super.init(nibName: nil, bundle: nil)
}
Vyacheslav
  • 26,359
  • 19
  • 112
  • 194
17

Update: add the link

https://developer.apple.com/documentation/uikit/uiviewcontroller/1621359-init

According to the documentation for iOS, the designated initialiser for UIViewController is initWithNibName: bundle:.

If you subclass UIViewController, you must call the super implementation of this method, even if you aren't using a NIB.

You can do it as follows:

init(objectId : NSManagedObjectID) {

    super.init(nibName: (xib's name or nil'), bundle: nil)

   // other code...
}

or

Declare a new initializer as a convenience initializer:

 convenience init( objectId : NSManagedObjectID ) {

    self.init()

     // other code...

}

NcNc
  • 309
  • 2
  • 5
  • 1
    You should link to the original for attribution, rather than leaving it as a screencap. – Nathan Tuggy Jun 24 '17 at 04:50
  • I used the same process as @NcNc said. It worked for me. Here's [link](https://stackoverflow.com/questions/40997236/super-init-isnt-called-on-all-paths-before-returning-from-initializer/45920375#45920375) – Anil Gupta Aug 28 '17 at 13:52
  • @NathanTuggy No, he should not. Links has a feature to expire or move someday. Who wants to see a 404 error instead of an information he/she is looking for? – mykolaj Mar 15 '18 at 12:29
  • 2
    @mykolaj: The information is *already* here, in this answer. (Otherwise I would have been flagging for deletion as a link-only answer.) But attribution is also important so that anyone who reads this can find out where it came from and credit them. A screenshot does not work for that, but a link does. If a link rots, well that's unfortunate, but you can't possibly tell me that's a worse situation than never having any kind of credit given at all. The objections SO has to link-only answers are to link-**only** answers. Not to links. Link *and also* info is the desired formulation. – Nathan Tuggy Mar 15 '18 at 20:57
  • @NathanTuggy I just added the link. Thanks for your reminding – NcNc Mar 22 '18 at 01:09