18 January 2008

Building My First RubyCocoa App - Some Notes

Today I started work on my first app built using RubyCocoa, which is now a first class citizen in MacOS X 10.5/Leopard. I had read the docs and tutorial found here. I proceeded, and ran into a few bumps along the way, so here are some notes maybe someone else will find useful...

The code for the example app RSSPhotoViewer, is not quite the same as that shown in the tutorial. Specifically:


  • The tutorial says you need to put "ib_action :method_name" after your methods that are Actions. The example code does not do that, and I found I didn't need to do it either.

  • The example code does not require osx/cocoa or include OSX, yet I had to do this in my Ruby source code in order for it to recognize the class names properly (or at least so I didn't have to prefix them with OSX).



I couldn't get Interface Builder to recognize my Ruby window controller class - i.e. it didn't show it's outlets and actions. I tried a variety of things here, but basically I finally had to go to the command line and run "rake" and let it do a command line build. I have no idea what that did differently, as I can't see any new files it generated, etc., but that resolved it - now IB can see all my outlets and actions.

My app wouldn't run, and I got a strange error in Xcode saying "The debugger is still running" etc. It appears that if your app crashes this will be the case. And, in this case, Console is your friend. Open up Console and you should see messages that will help you assess what's gone wrong.

And now for the win... dealing with Apple Events. My app wants to receive a particular apple event from NetNewsWire. NNW documents this protocol nicely, however, when registering as a handler for the event (using NSAppleEventManager.sharedAppleEventManager.setEventHandler_andSelector_forEventClass_andEventID_), you need to pass in the class and event ID that are not standard ones (they're defined by NNW/external blog protocol). Well, in Cocoa code, these are just a four character string, but as I found, a string that an unsigned long via string packing. So, it was a question of how I get these ID's in via Ruby. Luckily this turned out to be rather simple, as you can use String#unpack. And, in this case, you pass unpack "N" as the format, which is an unsigned long packed in network byte order.

Lastly, debugging. As mentioned above, Console and such are your friend. I haven't tried any shenanigans with ruby-debug or such from the command line, so that might work. But, Xcode can't debug into the Ruby code in your project (it can into the code in main.m just fine though). So, if you have weird crashes and such, check Console. Also, use NSLog, or pop alerts or what not. If someone knows a better way please do tell.

Regardless, I'm quite excited by RubyCocoa and have another couple apps that I may do with it. This app was a small one, and of course is not done in just a couple hours, but it's going to be a nice addition to the tool belt.

2 comments:

axel said...

Hi
You write:
* The tutorial says you need to put "ib_action :method_name" after your methods that are Actions. The example code does not do that, and I found I didn't need to do it either.

and you also write:
I couldn't get Interface Builder to recognize my Ruby window controller class - i.e. it didn't show it's outlets and actions.

Don't you think the two might be related?
I put ib_action and ib_outlets in my ruby source, drag the ruby file onto my NIB window, and all the outlets and actions are there to be used!


Axel

Chris said...

Axel, I agree, should be directly related! But, what I was finding was that when I had ib_action specifiers, they still weren't getting picked up. I've never had problems with ib_outlets, those always work, but the actions have been hit or miss. I have ib_action statements in all my stuff now, but it seems like there's some kind of initial thing that has to happen for IB to figure out anything is there (once it does, it seems like it keeps up from then on). I tried having IB re-read my source and all that. Usually, doing a command line build seemed to get it to notice.