Working without a nib, Part 1

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 NSPrincipalClass in Info.plist, call [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.

9 Responses to “Working without a nib, Part 1”

  1. j o a r says:

    You can add the main menu programmatically:

  2. j o a r says:

    Oops, I guess I used an incorrect syntax to add the URL. I’ll give it another try:

    http://www.cocoabuilder.com/archive/message/cocoa/2003/12/31/84618

  3. Jeff says:

    It appears that the method setAppleMenu: has disappeared from AppKit, unfortunately. I’ve heard of some people recreating the declaration, but I’d be wary of relying on that in a shipping app.

  4. Jeff says:

    I’ve reconsidered my opinion and written a new post on this issue.

  5. Philip Weaver says:

    Re: “no inspecting bindings”

    I always thought nib stood for Next Interface Builder. Maybe that’s why you don’t like nibs. :-) But seriously this is a good topic. I do all of my Java Swing work very well entirely in code – and it’s nice to know what you’re doing and where everything is. So knowing how to create interfaces dynamically is cool. Think: SmallTalk and Squeak.

  6. [...] I bear a heavy burden now, for with the ongoing Hollywood writers strike I am the lone remaining source of pablum in the world. Actually, I was on strike too, from the bagel shop. You know I can’t blog without bagels! However, the recent reconciliation of DLH and EVH inspired us to put aside our differences, and I thought the offer of SCO stock options and tickets to a Broadway show was more than fair. Thus, there will be a series finale to Working without a nib. Maybe they’ll even name a space shuttle after my cat. [...]

  7. syd says:

    My open source toolkit. Trixul, is an instance of a nibless application that has no problem with creating menus, and does so without a NIB. For source code, visit http://trixul.cvs.sourceforge.net/trixul/trixul/layout/cocoa/cocoamenubarimpl.mm?view=markup

  8. [...] in nib bindings. Yet another reason to do without nibs. But that’s also a subject for a different post and horse of a different color (dried [...]

  9. Tony says:

    …My open source toolkit. Trixul,..

    Thanx, just what I needed for my new Mac apps

    Tony

    Cognitive AB, Stelvio Bokföring Lön Ekonomi för Macintosh
    http://www.ct.se/