Archive for May, 2007

Failed Slower

Saturday, May 19th, 2007

Normally I refrain from linking to other blog posts. I don’t really consider myself to be part of the blogosphere. (We are the blogocube. We will add your biological and technological distinctiveness to our own. Bring your own beer.) Instead, I figure that you can follow my Favorite Feeds, which you can find in the sidebar of my blog, in my favorite feed reader, which you can get from Vienna Downloads. By the way, I just updated my Favorite Feeds today.

In the case of Fail Faster by Terrence Talbot, resistance to linking was futile. His blog was not yet in my Favorite Feeds, though it is now, and more important, he stole my idea! (I actually have a file named ideas.txt.) Anyway, read the post, because it’s a good idea, naturally. I implemented the idea back in Vienna revision 446, so I guess I can’t complain that I didn’t have enough time to publish it. D’oh! I’ll just add my two cents, which by coincidence is my standard consulting fee. Another source of build failures is Xcode itself. Believe it or not, Xcode still has a few bugs. Sometimes the only way to get your project to build correctly is to Clean first. When you Clean, however, any copied resources are deleted, so if your Copy Resources phase is at the beginning of the build, you may be needlessly repeating the copy phase.

This post as you know it is over. Length is irrelevant. Closing tags, on the other hand, are relevant. You must add </p>. Comply.

Working without a nib, Part 1

Wednesday, May 16th, 2007

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.