David Cordero

Swift: Raiders of the Lost Nib

Published on 03 Dec 2014

If you are used to work with Objective-C you are most probably familiar with the following documentation from UIViewController.nibName property:

If you use a nib file to store your view controller’s view, it is recommended that you specify that nib file explicitly when initializing your view controller. However, if you do not specify a nib name, and do not override the loadView method in your custom subclass, the view controller searches for a nib file using other means. Specifically, it looks for a nib file with an appropriate name (without the .nib extension) and loads that nib file whenever its view is requested. Specifically, it looks (in order) for a nib file with one of the following names:

If the view controller class name ends with the word “Controller”, as in MyViewController, it looks for a nib file whose name matches the class name without the word “Controller”, as in MyView.nib.
It looks for a nib file whose name matches the name of the view controller class. For example, if the class name is MyViewController, it looks for a MyViewController.nib file.

It basically means that you can have a Nib file with the same name than your ViewController and you don’t need to indicate anything in your inits, the Nib file will be loaded automatically for you.

The Bug ?

The problems start when you try to do it with Swift. In this moment you will discover that it is not working anymore. At least it is not working on the same way than before. And you will get a blank screen or a crash because the Nib couldn’t be found.

If you look for it in stackoverflow, you will find many people complaining about this “bug” in Xcode. If you want to test it Mr. Matt Neuburg created this project in GitHub trying to reproduce the bug in a isolated context making it easier to analyze.

But, wait a moment, is it a real Bug?

If you read again the documentation more carefully, you will notice it says:

…If the view controller class name ends with the word “Controller”…

It doesn’t say anything about the file name but about the class name.

Debugging

Let’s create a super simple Class in Swift. Something like:

class MyClass {
    func myMethod () {
        print("Breakpoint line")
    }
}

Let’s check now the name of our class. After all that is what the previous documentation was speaking about. So just creating a breakpoint in the line with the print and calling it when the App starts we get:

(lldb) po MyClass.self
TestBugSwift.MyClass

(lldb) po NSStringFromClass(self.dynamicType)
    "TestBugSwift.MyClass"
    {
        _core = {
        _baseAddress = 0x00007fe1d158b521
                _countAndFlags = 4611686018427387924
                _owner = (instance_type = Builtin.RawPointer = 0x00007fe1d158b510 -> 0x000000010f7034b8 (void *)0x000000010f703468: __NSCFString)
        }
    }
(lldb)

:boom: … what is this prefix? I named my class as MyClass without any prefix, and now I am getting back TestBugSwift.MyClass. What’s the matter?

Well, TestBugSwift is actually the name of my Module. And what I am getting from lldb is the complete name of my class composed as ModuleName.ClassName

So, that is the problem of the Lost Nib. Apple’s code is looking for a Nib file file called ModuleName.ClassName.xib instead of ClassName.xib

Conclusion

Now we know where is the problem, there are a few ways to make it work as we expected:

1. Rename your Nib file to have the expected name, with your Module name as prefix

2. Change the name of your class when exposing. @objc(MyClass) class MyClass

3. Create the initializers to let your ViewController know the appropriate Nib file

Feel free to follow me on github, twitter or dcordero.me if you have any further question.