Saturday, March 22, 2014

To prevent unpleasant surprises, check the initializers of the classes used in storyboards and NIBs.

I love storyboards. I use them all the time. They act as blueprints of my apps. A glance at the storyboard is often is enough to get an idea of the workflow of an app, and where to look for things. I think it's a good idea to work at a higher abstraction level, and let the tool generate the code for you.

They bring back fond memories, from when I was using Rational Rose RealTime and its predecessor, ObjecTime, while I was working for Motorola, making history.

So, back on iOS land, I worked on this project where I had a class derived from QLPreviewController like so:

and I used the DocPreviewController class in the storyboard, like so:

My first attempt to run the app produced this crash:

Taking a closer look reveals this abomination:

Looks like docPreviewController object might not have been properly constructed. To confirm the hypothesis, let's use Apple's sample code that doesn't use storyboards, instead it constructs the objects  explicitly in code like so:

Yup. That's how a properly constructed object should look like. Hypothesis confirmed.

It's a known fact that when an object is loaded from a NIB or storyboard, its initWithCoder: method is called. It seems that for QLPreviewController, a class from Apple, that method is not doing the right thing. Probably that's a bug on Apple's side. We could file a bug report, or we could take the matter in our own hands:

and see it fixed:

In conclusion, if you encounter problems with objects loaded from NIBs or storyboards check the initWithCoder: and make sure that it's doing the right thing. For a subclass like mine, it means that it should call the designated initializer of its superclass. Since QLPreviewController doesn't define a designated initializer, it means that it inherits that of its parent, - (id)initWithNibName:(NSString *)nibName bundle:(NSBundle *)nibBundle. Normally I should have called that, passing nil as the value of both parameters, but I chose to use the same initializer used in Apple's sample code. It seems to achieve the same result.

No comments: