Skip to main content

MacRuby - Expressiveness with power

In the last few months, I’ve made time to migrate some old RubyCocoa code to the latest and greatest version of MacRuby. Not only did I see a huge performance increase across the board, I found I could also create a stand-alone binary which could easily be distributed to end users. The entire framework is fast becoming a first class citizen on Mac OS X.

It was during the porting process that I ran into a small issue when attempting to read ID3 tags from mp3 files. I’d previously used the native Ruby gem ruby-mp3info, but this had some compatibility issues with MacRuby 0.6, and refused to install. After hacking around with the gem source, I worked around the issues, but the resulting performance was awful, so I reviewed my choices:

  1. Investigate the cause of the performance problems in ruby-mp3info
  2. Switch to using id3lib-ruby
  3. Investigate bundles and use a C/C++ library directly
I didn't sink a lot of time into fixing ruby-mp3info's specific problems on what is still a relatively niche platform, and I felt that using id3lib-ruby would complicate my ability to create standalone binaries. id3lib-ruby is so named because it depends on the libid3 C++ library, which would introduce another layer of complexity into the build process. Lastly, the id3lib gem didn't support the latest version of ID3 tags, and wasn't under active development.

That left the last option, which I had always wanted a good excuse to investigate. I had originally looked into this when I originally started looking at RubyCocoa in Summer ‘09, but linking with native Objective-C seemed excessively difficult and poorly-documented.

One of the many improvements that the MacRuby project brought over RubyCocoa was to make this functionality as easy as loading bundles in native Objective-C applications. This is achieved with use of the NSBundle class, so I thought all I needed to do was to take a C or C++ ID3 library, and wrap it in a very small Objective-C wrapper. Through the magic of runtime introspection, all of the wrapper’s methods would become available when you loaded the bundle into the MacRuby environment.

After reading up on existing tutorials, I managed to get this to work, and because it’s using the native compiled code, it’s extremely fast. This process of using interpreted Ruby code with compiled C/C++/Objective-C code is tremendously powerful, and allows for a mix and match approach to development that enables both rapid prototyping, and an ability to optimise for performance, from within a single codebase.

So powerful is this technique that I wanted to write up my experiences for others, who like myself, had maybe had just enough experience with Ruby (through Rails) and iOS development to feel that merging them together might be fun. With Mac OS X as my primary desktop environment, I’ve now achieved a triumvirate of control:

  1. I can write web-based code that lives in the cloud and is available from anywhere in the world.
  2. I can write mobile applications that I can carry with me at all times.
  3. I can write larger, more flexible and powerful applications when a desktop environment is appropriate.
I can remember buying my first Powerbook back in 2002 and getting the distinct feeling that Mac OS X had successfully channeled all of the potential energy that the original NeXTSTEP had so successfully created. That this was a futuristic platform that eagerly awaited the craftsman's touch. Something that could be deftly moulded it into whatever shape you could conceive, if only you invested a little time in the tools.

Eight years on, the choice has grown richer. MacRuby occupies a unique place within the spectrum of development tools on the Mac, offering a high level ease in harnessing low level power. If you haven’t looked at it yet, I can recommend a good tutorial.