Working without a nib, Part 5: Open Recent menu

Judging from the search phrases in my referrer log and from posts to Apple’s mailing lists, quite a few people are interested in developing Cocoa applications without using nibs. I’ve heard the demand, and you’ll be pleased to learn that the wait is over. I have a sweet solution. Today I’m announcing a Cocoa nibless SDK. You can download the SDK immediately — from the web! Specifically, from my blog. Just load this web page in Safari and select Save As… from the File menu.

(Don’t worry about me. I hear that the Chairman of the Board has a sense of humor. I’m sure that these two large gentlemen at my door are here to convey his appreciation of my wit and to deliver an invitation to lunch.)

(No! No! Stop, please! Not the iPod too! Have mercy!)

At the end of Part 4 of this series, I suggested that we needed to call setValue:@"NSRecentDocumentsMenu" forKey:@"name" to set the Open Recent menu. This is why they call me “Good Ol’ Oftenwrong”. Luckily, if you have a document-based application, Cocoa will generously create an Open Recent menu for you. All you need to do is put a menu item with the action @selector(openDocument:) in pretty much any menu, and Open Recent will magically appear as the next item in the menu. Now that’s a sweet solution!

If you want an Open Recent menu for a non-document app, on the other hand, you need to use an ugly hack. Although it was clear that the NSMenu ivar _name is set to @"NSRecentDocumentsMenu" for the Open Recent menu in a standard Cocoa MainMenu.nib, I couldn’t get the menu to populate with recent items in my nibless app even after setting _name. By pure trial and error, I discovered that you have to call _setMenuName:@"NSRecentDocumentsMenu" rather than setValue:@"NSRecentDocumentsMenu" forKey:@"name". (It was a natural choice after trying setName: and _setName:, which are not implemented by NSMenu.) The method _setMenuName: does set the _name ivar, but apparently it does some other crucial stuff too. Perhaps it asks a favor of the iGodfather. Anyway, I’ve updated my Nibless Xcode project to demonstrate this behavior.

In summary, we have succeeded (by we I mean the royal we) in creating a Cocoa application with a full main menu but without any nib (and without any error messages). For this purpose we’ve had to call two private methods, -[NSApplication setAppleMenu:] and -[NSMenu _setMenuName:], as well as poseAsClass: to override +[NSBundle loadNibNamed:owner:]. Not bad. And it’s taken us less than two months to reproduce what Interface Builder can do in less than two seconds. Isn’t this fun? The hardest part is finished, though. From now on, it’s just smooth sailing, on the Good Ship of Pyaray.

Oh, one more thing. Let’s dance! Anyway you want it.

5 Responses to “Working without a nib, Part 5: Open Recent menu”

  1. Jeff says:

    By the way, there’s a doc about how NSDocumentController automatically adds an Open Recent menu for document-based applications.

  2. [...] LapCat Software publishes Part 5 of the series of articles outlining the tricks and contortions necessary to create a Nibless Cocoa App. [...]

  3. David says:

    This has been a great series so far. Thanks for putting it together!

  4. Nico says:

    Thanks, that was very helpful. For posterity: It seems as if _setMenuName: has to be called before the application finished launching (the menu needs to be bound to a menuitem which needs to be inserted into the toplevel menu at that time too). Otherwise, this won’t work.

  5. Tony says:

    Thanx, this brings me new idea’s.

    ———
    Tony

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