I apologize for the fault in the subtitle. Those responsible have been sacked. What I intended to say was that I’ve discovered the Holy Grail of Cocoa hacks: how to create a functional Cocoa application without any nib. In Part 1 of this series, I suggested that you needed a nib for the main menu of the app; some would even argue that having a nib is the essence of a Cocoa app. It turns out that I was wrong, and some (they, the unspecified straw persons) were wrong too. I apologize for any faults in my previous post. Those responsible would be sacked, but those responsible for sacking have just been sacked.
As you may recall if you have a photographic memory or nothing better to do, the main problem with getting rid of nibs is setting the application menu. I had been using a minimal nib as a workaround, but now I have a reliable, though undocumented, solution to the problem. I came upon the solution by using gdb and JJApp (along with a herring) to override
initWithCoder: while the main menu is loaded from a nib. The class
NSMenu has a private ivar
_name, declared in
/System/Library/Frameworks/AppKit.framework/Headers/NSMenu.h, that is usually
nil, but a few menus — including the application menu — have a string value for the ivar. Although there are no public accessors, we can set the value anyway through the magic of key-value coding. For example, I called
setValue:@"NSMainMenu" forKey:@"name" on my main menu and
setValue:@"NSAppleMenu" forKey:@"name" on my application menu. This must be done before the return of
-[NSApplication finishLaunching], otherwise it will have no effect. I find
applicationWillFinishLaunching: to be a good place to set your main menu.
Another caveat is that the old deleting-the-nib trick doesn’t work at all if you change
Info.plist. When I set it to my
NSApplication subclass, the app refuses to launch, complaining,
No NSMainNibFile specified in info dictionary, exiting. Strangely, it works fine if I leave
NSPrincipalClass alone and call
[[JJApplication class] poseAsClass:[NSApplication class]] in
main.m. A special treat is that Cocoa automatically adds the Special Characters item to the Edit menu at runtime. Thanks, Cocoa! You can see all of this yourself by downloading my sample Xcode project, Nibless. If you use my code in your app, I may sue you, or I may kiss you. In either case, it’s a risk you’ll have to take. (It!)
Despite the fact that my solution is for the most part unsupported by official API (consult the book of armaments!), there’s good reason to think that it’s stable and should survive Leopard at least. Apple is unlikely to remove an ivar from such an important class as
NSMenu. More important, the value of the ivar seems to be the way that archived menus indicate their function to Cocoa and to Interface Builder, as reflected by the “Special Menus” setting in the Inspector. Try opening
keyedobjects.nib with BBEdit, and you’ll see that the file is just a plist containing definitions of the objects in the nib.
In Part 5 of this series — sorry, Part 3 — I’ll investigate the cause of the log message
Could not connect the action buttonPressed: to target of class NSApplication when launching without a nib. I’ll also attempt to populate the Open Recent menu. The Clear Menu item seems to work, but for some reason I can’t get items to appear in the menu. So, um, anything that you could do to help would be very helpful.
I’d like to end this post on a personal note. Many of my legions of fans have sent emails asking for more information about me: birth date, hobbies, pet peeves, dress size, etc. I prefer not to start a cult of personality, but I’ve decided that you, the heroes, deserve something. Thus, I’m going to share one particularly juicy tidbit. My favorite color is … blue. No, yellooooooooow!