People always ask me why I never find a programming language for the Mac and settle down down down. C’est absolument incroyable indeed as the answer should be pretty straight forward to those who have already written an application for the Mac using Objective-C.

As a matter of fact, in France, raising such a question is like asking whether or not Camembert goes well with French fries or not. The answer to this question is obvious as it’s a known fact that Camembert goes well with everything. Seeing as not everyone is blessed with a taste for fine French cuisine and/or is from French héritage however, I’ll try to give an approximation in this blog.

For this, we need to emphasize the differences of developing Mac applications with and without MacRuby.

After setting up MacRuby as described in our previous blog post and having set up Xcode, it is now time to create our very first Mac application in Ruby. In this series of articles, we assume that you have the latest version of OS X installed on your Mac. By the time of this writing, that is Snow Leopard and it is important to emphasize this seeing as some Cocoa API methods have been deprecated or added since its last iteration. Without further ado, allons-y!

Creating a new project

After starting up Xcode:

Select File

Select New Project

A dialog window as shown below should appear.

From this window

Select MacRuby application

Click on Choose…

A file save dialog should now appear as displayed below.

In this window:

Type in BornToBeAlive as our project name.

Click on Save.

A new window should now appear representing the newly created project in the Xcode environment.

By the time of this writing our environment is setup to target OS X 10.5 (Leopard). As mentioned before, we’re going to target OS X 10.6 (Snow Leopard) seeing as we want to utilize the most mature Cocoa API and platform. As such we need to set this target environment to OS X 10.6 in the overview popup menu.

Now that that’s been dealt with, we can finally really start working on our application.

Designing our Interface

Before we’re going to write even a single line of code, let’s first start off by designing the user interface to our application. To do this, we first need to discuss how our programming environment deals with this kind of information.

Certain information about our interfaces, in particular the one’s we’ve created using Interface Builder (the UI design application of Xcode) are usually stored in bundles called NIB files.

As of Xcode 3, these are actually stored in files with the xib file extension as opposed to the nib file extension. Worry not however, as they are just different formats for storing the same information: where xib files appear to be represented as XML text files, a nib file is an archive. More importantly however, the difference between xib and nib files are that the former will be compiled within the executable and will not appear in the final .app bundle.

In general, Xcode will ultimately know what to do with these files and how to treat them. It is for this reason that we’ll refer to both formats as nib files and basically assume no distinction between them.

In the Groups & Files panel, expand the NIB Files group.

In this panel

Double click on MainMenu.xib

This action should result in Interface Builder firing up for this MainMenu.xib file. Various windows of Interface Builder should now be visible to you.

In the MainMenu.xib window:

You see a collection of objects and proxy objects that are necessary for describing our user interface. The objects displayed in this window are objects that describe our application’s user interface and are stored in a freeze dried manner, i.e. serialized. Upon loading the NIB file, they get thawed and instantiated again.

In light of this, it is considered good practice to store only one window object per nib file. This way, we only load in the windows when we really need them (thus conserving memory) and will keep our design more maintainable too as each window and its components will be contained by its own NIB file. The latter will allow us to possibly recycle NIB files too in other projects.

Strictly speaking, we also shouldn’t store a window in our MainMenu.xib as is now the case, but in order to load this in, we require a notion of delegates. The latter is something we’ll discuss in a later article so for now, we’ll keep the window object in MainMenu.xib.

For now:

Double click on Window

This should put the focus on our initial window and via the inspector, we can edit some of its properties, such as the title of the window instance.

While having selected the Window in the MainMenu.xib window, in the inspector window:

Select the first tab, so that the inspector window is displaying the Window Attributes.

Edit the Title field to Born to be Alive

Hit the enter key

Your window should now bare the title Born to be Alive. How exciting!

Let’s add a Label to this window proclaim this message of life even bigger. To add cocoa components to our window, we need to drag and drop them from our Library panel to the window we want to add them to.

In the Library panel:

Set the focus on the search field at the bottom of the Library panel.

Type in Label. Notice that as we type character per character, the components get filtered based on our query.

Your Library panel should now display a few options for labels and should look something like the following:

Note that my Library panel will most likely display more options here in terms of labels as I’ve also installed the BWToolkit palette too. We won’t be using any of those components for our MacRuby applications though, so it’s safe for you to ignore them for now.

Now it’s time to actually add the Label component to our window. As mentioned earlier on, we do this by simply dragging and dropping the Label icon from our Library panel to the window we want to add it to. Doing so should result in something similar to the following:

Great! Let’s rename the newly added Label to something more fitting. In the window displayed above:

Double click on the newly added Label. This should now open up the ability to edit value of this Label.

Type in Born

Hit the enter key.

Your window should now look something like this:

Wouldn’t it be fun if we would have a button that we could press to unveil the full message of life? Let’s do just that by adding a button to the window we’re designing.

In the Library panel:

Type Button in the search box at the bottom of the Library panel. In a similar fashion as the label, it should filter away the components that do not match the Button search description.

Whoah, so many buttons! Let’s just stick with the first shiny one for now.

Drag and drop the first button that appears in the filtered selection to our window. Depending on where you dragged and dropped the button to, your window should now look something like this:

Double click on the newly added button to be able to edit its title.

Type in Unveil full message

Hit the enter key.

Your window should now be shaping up pretty nicely and look something like:

We can actually build this project at this stage and see what we’ve just designed in the form of a real application! Indeed, the standard MacRuby cocoa template has set up most of the boilerplate to get us started. At a later stage, we’ll dive into the details of the bootstrapping process, but for now let’s just assume that it set it up all correctly and save our changes by selecting File->Save from the Interface Builder main menu.

Compiling is not the task of Interface Builder, but the task of Xcode so let’s switch back to Xcode. In the project window, click on the Build and Run button in its toolbar.

After compiling for a few seconds and linking all the dependencies, your new application should launch in your OS X dock, and in particular, the window you just designed should show up.

Clicking on the Unveil full message button doesn’t do a lot though, but that should come as no surprise seeing as we’ve only been designing the interface up to this point. We haven’t yet specified what should happen if one presses the Unveil full message button button.

In terms of Model-View-Controller (MVC) (a separation of concerns design pattern Cocoa relies heavily on), we need a controller to handle this kind of user input.

Writing our very first controller in MacRuby

As mentioned in the previous section, Cocoa was designed with the MVC paradigm in mind. Within this paradigm, we need some kind of controller layer to process the input provided by the user. This input ranges from keyboard events to mouse events. For our intents and purposes, we’re interested in handling mouse events on our Unveil full message button. In order to be able to do that, we need to create a controller that will be able to deal with that.

Also, we may want to process events employed by the user onto the window itself. This would normally require a NSWindowController instance. Taking in mind we want to process events from components within the window as well, we could choose to create a separate controller for these components as well, but that would likely make things overly granular, especially for the simple scenario we’re currently dealing with.

So in place of the latter, we can also just choose to consolidate the ability to process the events of a window and its components all together into a subclass of NSWindowController. This allows us to handle the general case of controlling an NSWindow as well as specify our own actions for handling the components contained by our NSWindow.

To do this, in the Groups & Files panel, expand the BornToBeAlive project as displayed below by clicking on the triangle next to it.

In this expanded project outline view:

Right click (or ctrl+click) on Classes

Select Add->New File

A new window should now appear giving you the options of what kind of new file you would like to add.

From the Ruby user templates, select Ruby File.

Click on Next

This will open up a Save File dialog.

Type in MyWindowController.rb as the name for the new Ruby file.

Click on Finish.

Your window should now look something like this:

As decided upon earlier, we now need to implement our MyWindowController class in a consolidated way such that it can control both the window and the elements contained by that window. We will achieve this by implementing MyWindowController as being a subclass of NSWindowController, and in doing so, specialize the NSWindowController class for our window.

In the code editor, type in:

class MyWindowController < NSWindowController
end

Tres bien! But how do we access the label to modify its value after a user has clicked on the Unveil full message button?

Even though this is kind of true, the NSWindow class was designed for general cases as well and not just our specific window. This means that its design is well composed and that it will be rather tedious to acquire a reference to the element of the window we’re interested in manipulating. Most likely, it will involve a lot of chaining query calls which violates the principle of least knowledge.

To drive the point home, accessing an element in such a fashion in general will look something like: window.view.subviews[0]

A non-food solution to this would be to subclass our NSWindow to hold a direct reference to the elements we’re interested in manipulating, but that would require an assumption in terms of type seeing as our NSWindowController only deals with NSWindow and not OurSpecificWindow.

Taking all this into account, it’s probably just better to give our MyWindowController a direct reference to the element of the window we’re interested in manipulating.

In our particular case, we’d like to change the label’s string value after the user has clicked on the Unveil full message button to show the true message of life. This means we need a reference to the label within our MyWindowController and as such our code listing of MyWindowController now is:

In Cocoa, such a reference is usually referred to as an outlet, in our case, representing the metaphor of “being able to plug a label object into the outlet”. C’est bon indeed!

We now need to define the action to be taken when a user has clicked on our Unveil full message button. You might be wondering right now whether or not we also need a reference to the button from within MyWindowController as was the case for the label. This however, is not necessarily the case for us seeing as the button — an instance of NSButton — is an action emitter. More specifically, an NSButton inherits from NSControl and NSResponder allowing it to receive user input (e.g. mouse events) and convert these to action events and dispatch the latter to our code. We may touch base on this subject in more detail in a later article, but for now, suffice it to say that we don’t need such a property reference.

When the button receives a mouse event from the user that is of our interest, i.e. a click event, it will convert this event into a click action and try to dispatch the action message to our code. To that end, the button expects us to provide an object with an action handler method to handle the action on delivery. In Cocoa terminology, this object containing the action handler method is referred to as the target of the button.

An action handler method in MacRuby has a distinct method signature: it is a method with one parameter named sender. It is important to adhere to this rule for action handler methods as Xcode and Interface Builder will only then recognize them as being actions.

Let’s open up our code editor in Xcode for MyWindowController.rb and change the listing to:

Here, we’ve added the unveil_full_message_clicked action, which will change the stringValue property of our label to the true message of life. Excellent! We’ve now set up all our code, and what remains now is to tell our button to who it should dispatch its action to.

We can setup this target-action for our button programmatic, but Interface Builder provides us with a visual solution for setting these up as well. For the sake of clarity and convenience, the visual solution that we’ll go over with in a few moments enjoys our preference for now.

Hooking our MyWindowController up to our window

In the previous sections we’ve gone from designing our window to providing an implementation for our MyWindowController class. They still need to be hooked up to one another however as they are now still disjoint entities that don’t know of eachothers existence. In this section, we’ll describe how we’ll be able to hook these things up visually using Interface Builder.

In Xcode:

Double click on MainMenu.xib to open up Interface Builder for this nib file.

As we’ve discussed earlier, objects are stored in a freeze dried manner in our NIB file that get “thawed for use” when the NIB file is loaded again. In this case, we need an instance of MyWindowController to hook it up to our freeze dried window instance.

In Interface Builder in the Library Panel:

Type in NSObject in the search field.

Drag and drop the Object from the panel to the MainMenu.xib window of Interface Builder. This will create a new freeze dried object in the NIB file that will be instantiated when the NIB file gets loaded in.

Select the object.

In the Inspector panel:

Click on the Object Identity tab.

As we can see, the object is of class type NSObject. We need to change this to be MyWindowController to let this object be an instance of that type.

In the class identity field, type in MyWindowController.

Our object is now of type MyWindowController. Tres bien!

Now that we’ve created a freeze dried MyWindowController object, it’s time to hook it up to our window instance.

Recall from the code listing on MyWindowController that we have defined an accessor — i.e. outlet — for our label, but have not yet set this property. In particular, we need to set the my_label property of our MyWindowController instance to point to the label of our window. In Cocoa jargon, we need to “plug our window’s label into our MyWindowController‘s my_label outlet”.

In the MainMenu.xib window of Interface Builder:

Hold down the right button (or hold ctrl+click) on the MyWindowController object and drag your mouse to the label in the designer.

Release the right mouse button above the label. A popup menu as displayed below should now appear showing you the outlets to which you can hook the label up to.

Click on my_label in the Outlet popup menu to hook the label in the window up to our MyWindowController‘s my_label property.

Our MyWindowController now knows what object it should refer to when we use @my_label in MyWindowController. What remains is to set up the target-action for our button to our controller’s action handler method. We’re almost there indeed!

To set the target-action for our button:

Hold down the right mouse button (or ctrl+click) on the button in our window and drag to our My Window Controller object in the MainMenu.xib window as illustrated below.

Release the right mouse button. A popup menu should now appear displaying the controller’s action handler methods we can dispatch the button’s (click) action to.

Click on unveil_full_message_clicked: to allow the button to dispatch the click action event to this method of our MyWindowController instance.

Hit cmd+s to save our changes in Interface Builder.

Return to Xcode. Make sure we’ve saved all the changes we’ve made to the files here too via cmd+s.

Hit the Build & Run button in the toolbar as we’ve done before.

Congratulations, your very first MacRuby application should now be in working order! Go ahead, click on the button to unveil the full message of life!

Download code

Epilogue

For the sake of brevity — something I know is hard to believe looking at the volume of this first tutorial — we’ve omitted a few steps you’d normally like to take into consideration.

For instance, we’ve not connected the window outlet of MyWindowController (an outlet it inherited from NSWindowController) to our window instance to allow it to manage the window. Connecting this outlet occurs in a similar fashion as was the case with connecting the label outlet by holding down the right mouse button on the MyWindowController instance and dragging it to the window instance.

Releasing the right mouse button will display a popup menu outlining the outlets we can connect the window to. Clearly we should select the window outlet here.

We’ve also not yet discussed how we can adhere to the one window per nib file, moving the window object from our MainMenu.xib to its own nib file and load it in programmatically. As mentioned before, this will require knowledge of a programming concept called delegates which we’ll discuss in our next tutorial. This will also allow us to explain the concept of File’s Owner and so on, which will require us to refine some of the techniques we’ve explained up to this point.

For now, go out and celebrate your victory of writing your very first MacRuby application.

Homework Assignment

Port the following Born to be Alive application by Johannes Fahrenkrug to MacRuby! Post your GitHub links in the comments for extra street credit 😉 Having a hard time being able to do so? Stay tuned for our next installment in the MacRuby series with moi as we’ll dive into objective-c for rubyists!

As for the problem you’re encountering, are you sure unveil_full_message_clicked has a parameter *named* sender? It’s very important that it’s called sender for it to be registered by Xcode as being an action method. You made mention that it showed the my_label popup which leads me to suspect that you’re either dragging the connection in the wrong direction or from the wrong object. Technically speaking, it should not appear as an option at all when assigning target actions and I suspect you’ve “dragged the connection” in the wrong direction as its trying to connect it to the outlet now. Make sure that you’re dragging from the button to the controller and not the other way around in this case.

Thank you for your respond. I did drag from the opposite direction. The direction where you drag is important. It now works, I am happy to unveil the full secret of live!

This is my first Mac desktop app ever, and it is written in Ruby! Now I am going to learn/play some more with MacRuby in Xcode. I am look forward to those coming tutorials.

Lucas

Wow, that’s what I’ve always wanted to do!

Please, I need some help, I tried many times, repeated all step-by-step instructions ( at first I didn’t because I tought I could handle it ), but turns out that my controller in the interface builder doesn’t have any “hookable” things to use.

I can’t access the my_label variable, and my method either.

I’m using Snow Leopard and the 3.2.1 Xcode with the latest MacRuby ( downloaded both 2 days ago ).

@Lucas:
Can you confirm that you’ve created a subclass of the NSWindowController and created a attr_accessor :my_label ? Also, make sure that you work with the most stable version of MacRuby, which by the time of this writing is version 0.5. Usage of the nightlies is discouraged in general as they are just snapshots of trunk which can break on a daily basis, so go for stable releases 🙂

In any case, I’ve not encountered this problem before so I’ve notified Laurent Sansonetti (principle maintainer of MacRuby) about this too. Hopefully he’ll be able to shed some light on the situation 🙂

Lucas

Yep, I’ve set everything to the newest version, there are still 2 errors, one saying it cannot find the my_label, and the other saying it cant find the unveil method.

That’s very weird, even for me who are used to java crazy stuff.

If I can compile and run, then the macruby framework is working fine (I guess).

It could be the “macruby xcode/I.B. plugin”…

Anyway, I’m gonna try to find a way to remove all my Xcode versions and try to install it all again. Who knows…

If I figure it out I’m gonna post here.

Thanks for the feedback!

Lucas

Got it!

(or sort of)

It was certainly some issue with my xcode+tools.

I performed a full uninstall (for those unaware, this command will remove all apple’s development tools, all versions of xcode, and whatever comes in the way. I’ve been told. ) :

sudo /Developer/Library/uninstall-devtools –mode=all

And then I installed again the latest Xcode (3.2.1) and MacRuby (0.5).

It’s just awesome!

Thanks again for the article !

Björn

Hi!

thanks for the article… I am currently researching a script language to learn… originally just for simple text file manipulations, but I might also want to start using it for programming… I am really new to this – but I have been suggested to start with Python, not Ruby (or Perl). My question is: is it also possible to do sth. similar (building a native Mac Application) using Python with XCode and Interface Builder??? Does anyone of You know of a similar tutorial as this for this purpose?
Probably, I know, there might be tons of info an this on Google… but it is always better to get comments by humans… particularly, when You are searching (and have to evaluate a lot of options fast)… Humans can give experience-based comments much faster than Google…

Python and Ruby aren’t really that different from one another and imo resemble to the degree that they’re often considered to be niece/nephew from eachother. I don’t think you can go wrong with either one of them. Having said that, Python / Ruby are just as full fledged a programming language as the next. It is entirely possible to do something similar as described in this tutorial with Python using the Py-Objc bridge, but as the name hints, it acts as a bridge which incorporates two separate runtimes (one from Python and one from Objective-C) and in that regard is very similar to RubyCocoa. MacRuby however, consolidates both runtimes into a single runtime, i.e. you don’t deal with a Ruby and Objective-C runtime, but just with one Objective-C runtime. This is important to understand as it’ll allow you to easily mix and match objective-C and ruby code with one another should you have to work with native code as well. On top of that, MacRuby will allow you to compile your code into a native binary ahead of time, eliminating the need of a Ruby interpreter altogether. This is something that will remain the case with bridges, where you will always need to have a Python or Ruby interpreter. Performance wise, this can definitely be noticeable and it is for this reason that we like MacRuby a lot for its ahead of time compilation as well as its just in time compilation abilities. Hope that answers your question.

Björn

Hi, Ninh Bui!

Thanks for Your long answer! 😀 As You may have guessed, I am – although I have used programming for quite some time (beginning with BASIC 20 years ago on 64kB computers, such as the Commodore…)… nevertheless (or probably due to this level of exposure), I have never really gotten into all this compiler-runtime-API-business… it still is kind of hard for me… and a bit theoretical.

But I understand Your comment in the way that both Ruby as well as Python would be able to build a native OS X Cocoa-based application with, although MacRuby makes it easier to precompile the script into an executable file to use with the UI built in X-Code… As far as I know, the Python interpreter in fact compiles the Python script before running it… isn’t it true that thus, the compiled script would behave like a real program, very much as one written with MacRuby? Maybe, it is a bit more work do get things going – but as far as I see it, particularly since I am still at the beginner level, I would really like to understand how my application works (thus I wouldn’t care that much about the complications You mention – it is okay for me to do some things by hand – particularly since I’d like to understand how those X-Code built applications really are made, and how they work…).

Well, thanks for Your comments! Best,
Björn

ma_il

Thanks for a very nice & concise article! I was under the impression that the Ruby/Cocoa bridge had been discontinued but it really works great under Snow Leopard.

@Björn:
Well, with MacRuby you can write native OSX Cocoa-based applications because it’s capable of compiling it to native code and using only one runtime, being the same objective C runtime native OSX apps utilize. A bridge however, incorporates two runtimes as mentioned before, where in the case of Py-Objc for example, a python runtime interacts with the objective-C runtime to for example, interface with the Cocoa API. A similar scenario is applicable to RubyCocoa. With MacRuby however, you don’t have these separate runtimes anymore to deal with: just one, being the Objective-C runtime which allows you to mix and match Ruby and Objective-C code, something we’ll touch on in the follow up to this article. I hope this has clarified that a bit up: don’t confuse RubyCocoa for MacRuby, the former is a bridge, the latter allows for a single runtime native solution.

As for your claim about the Python interpreter compiling the script before it runs, unfortunately, we haven’t had enough experience with the Python internals to verify whether or not this is the case. However, if true, it would mean that the Python interpreter would “just-in-time” compile it. By the time of this writing MacRuby does this as well, but I believe heuristics may be on its way to only JIT when its responsible too (e.g. depending on caller frequency etc…) as JIT comes at the cost of memory (e.g. storing the native code in RAM) and performance (e.g. translating the code to native). As far as I’m aware of, there were some add-ons that would allow Python to JIT compile (Psyco for example), but by default Python does not: it probably only compiles it down to VM opcodes (bytecode), similar as Ruby 1.9 does this. This however, is not native. It simply speeds up the interpretation process by not having to traverse and interpret the abstract syntax tree (AST), but just a series of opcodes instead. MacRuby does not do this, instead, it just compiles it directly to native code for execution.

Really nice tutorial. One thing I’ve noticed though, is that it won’t work on xcode 4.1, 4.2 and Lion, because of an issue with xcode. Bug and a possible workaround are described here: http://www.macruby.org/trac/ticket/1322, but it seems that the problem is in xcode, not macruby, so I guess we’re gonna have to wait until the next release and hope it gets resolved.

“Phusion” and “Phusion Passenger” are registered trademarks of Phusion. “Rails”, “Ruby on Rails” and the Rails logo are registered trademarks of David Heinemeier Hansson. All other trademarks are property of their respective owners.