My infinite part series "Working without a nib" — most recent part here — explains how you can work without a nib, but I haven't spent a lot of time explaining why you should work without a nib. That was left as an exercise for the reader. It turns out, however, that readers tend to be sitting down rather than exercising. They really need to get up and go outside more. (Later, of course, after reading this article.) While I did talk about the issue a bit on a podcast, I'm not sure many people caught that episode, so I've decided to elaborate for the first time in print. Please print out this web page.
By "nib" I mean a
.storyboard file. The old uncompiled
.nib file format (or more accurately, formats) is (are) now obsolete. And that alone should give you some pause about nibs. Did you know they buried a big pile of nibs next to the Atari E.T. game cartridges? In what follows, I'll offer a number of reasons why you should avoid using nibs. Admittedly, this will be a purely one-sided argument. I make no attempt to defend nibs or give reasons for using them. Such reasons may exist, but finding them is left as an exercise for the reader. If you're not already out of breath.
A programming language has a publicly documented syntax. It's possible for anyone to read and understand source code files. It's possible for anyone to write computer programs to parse source code files. In contrast, nibs are a private, undocumented, propriety file format, known fully only inside of Apple, and subject to change at any time without notice. We know that nibs are (now) XML, but that's about all we know for sure. This is a major obstacle to ensuring the correctness of software that uses nibs. It has been claimed that more recent versions of the nib format have made it easier to perform "diffs" of changes to nib files under version control. In some sense this claim is true, but in what sense? I'd say mainly in the sense of making diffs shorter. Fewer lines changed. However, the fundamental problem remains. With some experience at reading nib diffs, you may be able to make decent guesses about what the changes mean, in many cases. But you don't know, you never know, because the file format is not documented. And guessing is not good enough for code review. A goal of professional software development is to minimize guessing. Best practice is to audit and test every line of code. That's impossible with nibs. Open a nib in a text editor and tell me what everything means. Can you? Can anyone?
It's worth comparing nib files to
.pbxproj files, for the sake of argument. Both are private, Apple-proprietary, undocumented formats, but I do not advocate working without a project. There are several crucial differences. First, it's much more difficult to work on Mac or iOS apps without an Xcode project. You'd probably have to employ a tool like
make and write all of the command-line invocations yourself. Not an impossible task, but it gets pretty ugly pretty quickly. Whereas there's a perfectly good alternative to nib files: writing the user interface in code. We write source code for everything else in the app, so it's not a big leap to write more source code. Second,
.pbxproj has a "flatter", non-XML file format that's a bit easier to work with, especially if in a pinch you need to edit something manually with a text editor (which I've had to do frequently with
.pbxproj and, unfortunately, once or twice with nib files). Finally, I do advocate removing as much as possible from your
.pbxproj files and putting all of your build settings in
.xcconfig files. Though not completely documented,
.xcconfig is at least partially documented by Apple, unlike
.pbxproj or nibs. (The most thorough documentation is probably by Samantha Marshall.) The
.xcconfig format is even flatter and simpler than
.pbxproj. Moreover, Apple and Xcode officially support manual creation and editing of
.xcconfig files. Try writing a nib from scratch in XML!
As a consequence of having an undocumented file format that at best allows only trivial fixes by hand, there are no external editors for nibs. The only place you can create nibs is in Xcode's Interface Builder view. You are totally dependent on Xcode. There is no feasible way to ever work on a nib anywhere but in Xcode. You can't even view a nib in a useful, informative way without opening it in Xcode. This is in stark contrast to source files, which can be created, viewed, and edited almost anywhere, in countless editors on any platform. Certainly there are benefits to editing source files in Xcode, and Xcode is my personally preferred IDE, but even with software projects designed exclusively for Apple platforms, you severely limit your options with a file format that is only compatible with Xcode.
Speaking of Xcode, have you ever simply opened a nib and had Xcode decide to modify the file on disk? Maybe this is just a minor complaint, but the phenomenon is really annoying, and it's happened to me frequently over many years. It should never happen, and it never does happen with source files.
You can't merge changes to nib files on different version control branches. That is, you shouldn't merge changes to nib files on different version control branches. I'm sure people have attempted it before, and maybe even avoided disaster, but it's like successfully walking across a highway with your eyes shut. You could do it, theoretically, but you're incredibly lucky if you don't get hit by something.
You might say that developers shouldn't make changes to the same nib file on different branches, and that is indeed good advice given the limitations of nibs. Sometimes it's unavoidable though. For example, there may be different teams working independently on features for the same large app. In any case, this limitation does not apply to source files; if you write the user interface in code, then you make it possible and practical to merge user interface changes on different branches.
Unlike source code, nibs have no place for comments written by the developer. I've heard many developers say they'd love to be able to put comments in nibs. Maybe Apple could implement this in the future, but … NeXT Interface Builder (yes, that's where "nib" comes from) was introduced in 1988, so at this point, don't hold your breath.
Speaking of 1988, it's unlikely that nibs created back then would be readable today by Xcode. Yet every source code file would still be readable. Some might even still compile! As long as they weren't written in a dead programming language. (Like Swift 3.) Xcode offers no guarantee of backward compatibility with old nib files. Furthermore, even if Xcode can open a nib, it may become hopelessly broken in various ways over time. For instance, a few months ago I downloaded the Apple sample code project BezierPathLab. (I won't link to it here, because Apple's online documentation tends to suffer from "link rot".) The project was last modified 5 years ago, which is within the lifespan of a successful consumer app. When I built and ran BezierPathLab, it crashed on launch in nib loading:
Assertion failed: (s->stack->next != NULL), function CGGStackRestore, file /Library/Caches/com.apple.xbs/Sources/Quartz2D/Quartz2D-1070.22/CoreGraphics/Context/CGGStack.c, line 77.
When I opened
MainMenu.nib in the Interface Builder view, it said "Opens in Unknown Xcode Version" and "Builds for macOS 10.13 and Later", which is odd because the macOS Deployment Target in the project is set to 10.7, and I was running 10.12. After I fixed those in the nib and saved, I still got the same crash on launch. And after I saved the nib in Xcode 8 format, Xcode could no longer open the nib: "Unrecognized file content." So that's fun.
A couple years ago I was working on an app that was itself around 10 years old. I wanted to fix a bug, and that required a change in a nib file, but when I made the change, that somehow caused another problem even worse than the one I was trying to fix. It was a total mystery. Ultimately, we just had to declare that nib file off-limits, untouchable, in order to avoid the problem caused by modifying the file. This is a terrible position to be in as a developer.
No matter how outdated the source file, no matter how many deprecations and build errors, you can always at least read the old source code and try to refactor it to work again. The same is not true of nibs. Old nibs can become completely unrecoverable, and you just have to trash them and start over from scratch. You may not even be able to use them as a model for the new UI. Remember Interface Builder 3 Plugins? Any nib with an IBPlugin dependency is now garbage.
A nib is an archive. It's important to understand that by its very nature, a nib contains old things. I've heard the objects stored in nibs described as "freeze-dried". You know how scientists say that global warming will melt the permafrost, exposing ancient organisms that modern humans are unprepared for? Well, that's more or less what happens in
initWithCoder. Seriously, though, if a nib was created 5 years ago with an
NSWindow and an
NSTextView, they aren't necessarily the same as if you created them with
alloc init now. You don't get a 10.13 SDK
NSTextView from the nib, you get a 10.8 SDK
NSTextView. The nib objects may have different default properties and lack certain newer features that you'd normally expect when you
alloc init with the current SDK of your project, and this situation is very difficult to ferret out because of the opaqueness of the nib format. I've seen this happen before. Most of the "wisdom" I preach in these (web) pages comes from hard experience. Behind every storyboard, there's a horror story.
I've never been fond of the launch screen API on iOS. I'd prefer that the system animate the app icon in some way, like how apps bounce in the Dock on the Mac. Nonetheless, Apple has continued to, shall we say, innovate in the screen size genre with the introduction of iPhone X, pronounced "Xen". (Caution: Do not pronounce iPhone X, or type it, in your app's release notes, on pain of metadata rejection.) Given this state of affairs, I would recommend using a launch screen storyboard for iOS apps. Thus you can't work entirely without a nib. But you can get away with something extremely simple and boring, nothing more than an opaque view in your storyboard. It's just one concession we have to make to Interface Builder, a little nod to the coding gods, that perfection is unattainable.