Cocoa Programming with Nu

Tuesday, 27 May 2008

Late last week I got a copy of the new edition of Aaron Hillegass’ Cocoa Programming for Mac OS X, and since then I have been porting examples from selected chapters to Nu. To organize and share the results, I posted a git repository and since then, Adam Solove and Jonathan Yedidia have pitched in to help with the conversions.

So far, we’ve covered 12 of the 33 chapters that contain code examples, and I hope to get the rest converted in the next few weeks.

If you’d like to contribute, get the source for the original examples from Aaron’s Big Nerd Ranch site, and after you’ve finished a conversion, grab a github account, fork my repository, commit and push your changes up to your fork, and then send me a pull request.

To make things easier, here are some tips for converting existing Objective-C projects to Nu:

Building Nu projects with Xcode

Use Xcode. Although I like to use nuke, it’s also possible to build Nu-based applications with Xcode, and for this conversion process, its easiest to start there.

First, in the project directory, replace the application’s main.m with this:
int NuMain(int argc, const char *argv[]);

int main(int argc, const char *argv[])
{
    return NuMain(argc, argv);
}
Now, also in the project directory, create a subdirectory named nu. In it, place the following main.nu file:
;; file main.nu
;; discussion Entry point for a Nu program.

(import Cocoa)        ;; bridgesupport
(load "console")    ;; interactive console

(set SHOW_CONSOLE_AT_STARTUP nil)

;; @class ApplicationDelegate
;; @discussion Methods of this class perform general-purpose tasks that are not appropriate methods of any other classes.
(class ApplicationDelegate is NSObject

     ;; This method is called after Cocoa has finished its basic application setup.
     ;; It instantiates application-specific components.
     ;; In this case, it constructs an interactive Nu console that can be activated from the application's Window menu.
     (- (void) applicationDidFinishLaunching:(id) sender is
        (set $console ((NuConsoleWindowController alloc) init))
        (if SHOW_CONSOLE_AT_STARTUP ($console toggleConsole:self))))

;; install the delegate and keep a reference to it since the application won't retain it.
((NSApplication sharedApplication) setDelegate:(set $delegate ((ApplicationDelegate alloc) init)))

;; this makes the application window take focus when we've started it from the terminal (or with nuke)
((NSApplication sharedApplication) activateIgnoringOtherApps:YES)

;; run the main Cocoa event loop
(NSApplicationMain 0 nil)

This is more than what is strictly necessary, but the console that it includes is worth it. The applicationDidFinishLaunching: method creates a console window and adds a new item to the Window menu to control its display. You can use this console to type Nu expressions that let you interact with your running application.

Now, open the Xcode project. Right-click on the topmost entry in your Groups & Files list and add a new group. Name it “Nu”. Right-click on this new entry and select Add > Existing Files… Navigate to main.nu and add it. You don’t need to check the “Copy items…” box; the file is already in the right place. Look down a few entries to the Frameworks folder. Click on its “expose” triangle, then right-click on Linked Frameworks and select Add > Existing Frameworks…. Use this to add /Library/Frameworks/Nu.framework to your project (make sure that you’ve installed Nu!). Now build the application in Xcode. When it starts, look in the Window menu for the Show Nu Console menu item. You might want to try something fun now, like typing some of the following commands (use the up and down arrow keys for command history):
(NSApplication sharedApplication)
((NSApplication sharedApplication) windows)
((((NSApplication sharedApplication) windows) 0) title)
((((NSApplication sharedApplication) windows) 0) setTitle:"Nu!")
((NSApplication sharedApplication) terminate:nil)

Clearly, there’s a lot that you can do with that.

Now you can begin converting your application code from Objective-C to Nu. I usually do this one class at a time and begin with the simplest class. It’s not even necessary to convert the entire class at once; just pick one or two methods, add them to main.nu and rerun your application. For example, the following code defines the isOpaque method of BigLetterView in the TypingTutor example in Chapter 19:

(class BigLetterView
    (- (BOOL)isOpaque is YES))

If you want to be sure that your Nu method got called, add a call to NSLog:

(class BigLetterView
     (- (BOOL)isOpaque is
        (NSLog "isOpaque")
        YES))

Check the Xcode debugger console for the results.

More tips

Any methods declared with Nu will replace similarly-named methods implemented in Objective-C.

Be sure to get the type declarations right. All Objective-C object types should be declared as type id in Nu method declarations.

If you want to manipulate something from the console and can’t figure out how to access it, set a global variable in its init function. For example, for BigLetterView, you might do this to make set the $view global variable:

(class BigLetterView
     (- (id)initWithFrame:(NSRect)frame is
        (super initWithFrame:frame)
        (NSLog "initWithFrame:")
        (set @bgColor (NSColor yellowColor))
        (set @string " ")
        (set $view self)
        self)
You can then use that in the console to change the view’s background color:
($view setValue:(NSColor redColor) forIvar:"color")
($view setNeedsDisplay:YES)

Remember that since Nu handles memory management automatically, there’s no need to call retain and release in your converted code (so please don’t!).

Currently Objective-C garbage collection is unsupported by Nu. If someone wants to sit with me at WWDC and help get that working, that would be welcome.

Again, do a little bit at a time and check your progress at each step. Convert all of a class’s methods before moving the primary class declaration to Nu. Remember that the syntax for extending a class in Nu omits the “is superclass” and does not add instance variable declarations.

To get automatic Nu accessors for a class’s instance variables, use the (ivar-accessors) operator in the class body.

Finally, use the source! There’s lots of examples that have already been converted, and Nu itself is completely open-source.

Comments (2) post a reply
  1. Steve Sunday, 08 Jun 2008, 07:47 AM PDT

    A brief warning – install Nu from source code for the above to work, otherwise it hangs before opening the window.

  2. Tim Sunday, 08 Jun 2008, 01:19 PM PDT

    Steve, thanks for letting me know about this. I just posted a new release, hopefully it will fix this problem.