There are a number of reasons why you might want to build your application without a nib. As you may know, ‘nib’ is an acronym for
no inspecting bindings. Anyone who uses version control (in other words, anyone except R.B.) can see in the diffs that nib files are rather opaque. The next Interface Builder, the one in Mac OS X 10.5 Leopard, may offer some improvements in this area, although I can’t really say yet. This is not to say that I’m under NDA. It’s not to say that I’m not under NDA either. I am in fact under NDA, so many NDA it makes my head spin. I can’t even say how many NDA. This is not to say that my NDA prevent me from saying how many NDA I’m under. I just don’t know. Anyway, I don’t have a Leopard seed. Even if I did have Leopard, which I don’t, it isn’t scheduled for release until October, and besides, many developers will still want to support Tiger at the very least for years to come. Thus, my topic is not necessarily obsolete at startup, unlike the computer you just bought.
It is indeed possible to to run a Cocoa application entirely without a nib: create a Cocoa application project in Xcode, delete
MainMenu.nib, and remove the
NSMainNibFile entry from
Info.plist. (You do get the log error
Could not connect the action buttonPressed: to target of class NSApplication.) You can allocate your own objects in
main(), though you must do that before calling
NSApplicationMain(), not after. (It’s too late, Jim, he’s dead already.) Remember, Jim,
main.m is just another Objective-C source file. Objects that you allocate before
NSApplicationMain() can even register to receive notifications such as
NSApplicationDidFinishLaunching, so you can hook into the launching process and perform whatever setup you need.
For all practical purposes, however, you can’t really work without a nib. The problem is the main menu, or more specifically, the application menu. In a normal Cocoa application, the application menu is what would be returned by
[[[NSApp mainMenu] itemAtIndex:0] submenu]. In
MainMenu.nib of a default Xcode Cocoa application project, the application menu is the submenu for the item
NewApplication. Actually, it doesn’t matter what the title is of this menu item, because regardless of how you set it in Interface Builder, Cocoa replaces the title at runtime with an empty string, and the name of the application boldly goes on the menu bar. The title of the application menu itself is
Apple, which is highly misleading, considering that its left-side neighbor on the menu bar has an Apple icon at the top. Just to be clear, the
Apple menu is not the menu with an Apple. Got it?
What prevents our nibless existence is that the application menu refuses to be set programmatically. If there is no main nib file or no main menu in the main nib, Cocoa creates an application menu automatically, and there appears to be no API to access it. You could create your own
NSMenu with an
NSMenuItem at index 0 having a submenu with the title
@"Apple", but if you try to set it as the main menu with
-[NSApplication setMainMenu:], all of your submenus, including your Apple menu, will appear to the right of the automatically generated application menu. Like a poor marksman, you’ll keep missing the target!
When faced with a no-win situation, what do we do? Cheat! My workaround to the problem is to keep
MainMenu.nib in the Cocoa project but leave only the most minimal main menu possible. The Window can be deleted entirely, and in the MainMenu, delete everything except the
NewApplication menu item and its submenu, which is listed as the Application Menu in the Inspector. The submenu’s menu items can be deleted. If you allow the main menu to come from the nib, then the
Apple menu will automatically become part of
-[NSApplication mainMenu], and you can populate it and the rest of the main menu yourself. The most elegant way of populating the main menu, in my opinion, would be to subclass
NSApplication, set your subclass as
[self mainMenu] in your subclass
awakeFromNib, and add items to the returned menu. (The
NSPrincipalClass of your app needs to be a subclass of
NSApplication, otherwise your app crashes on launch with a
selector not recognized exception.)
In Part 2 of the series, I’ll attempt to add some functionality to my currently useless, empty app. My ongoing mission will be to explore the strange new world of life without (much of) a nib. It’s a difficult concept, perhaps not very logical, but we learn by doing.