Clay Allsopp

The RubyMotion Way

Jul 8, 2012

I love RubyMotion. And the way things are looking now, I'll never have to write Apple-platform software entirely in Objective-C again.

Don't get me wrong, I think Objective-C is a great language. I've enjoyed using it nearly every day for the past four years and it's getting better all the time.

But I find working in Ruby to be just plain fun. If you've tried projects in Ruby and just didn't like it, RubyMotion probably won't change your mind. But if you've ever wanted to make iOS or Mac apps but didn't want to get over the hump that is Objective-C, maybe this will get you going.

Rubyfication

I've done some work on BubbleWrap, which is the largest and most widely used RubyMotion gem. It aims to take all of the existing Cocoa frameworks and make them more pleasant in Ruby.

The low-hanging fruit for this effort is taking the callback-centric Cocoa APIs, which are mostly handled in Objective-C using delegates or targets, and transitioning them to Ruby blocks. Here's a quick comparison of handling button callbacks in Objective-C:

[button addTarget:self action:@selector(buttonTapped:) forControlEvents: UIControlEventTouchUpInside];

//  Elsewhere

- (void)buttonTapped:(id)sender {
    self.view.backgroundColor = [UIColor redColor];
}

and RubyMotion:

button.when(UIControlEventTouchUpInside) do
  self.view.backgroundColor = UIColor.redColor
end

Pretty nice right? BubbleWrap and other efforts are systematically going through the Cocoa APIs and making things lessLike:objectiveC: and more like_ruby.

Some magical things are happening in the process:

  • Nitron makes CoreData more like ActiveRecord
  • Teacup adds stylesheets to UIViews
  • My own project Formotion lets you build interactive UIs with just a hash.

Rubyists get a lot of flak for the kind of DSLs and black magic method_missing tricks the above libraries use; but you know what, maybe that attitude is just what Objective-C has been missing.

Objective-Ruby

One of the criticisms of RubyMotion is that, while a solid technical achievement, it does not alleviate the pain that is the verboseFunctionNames:inObjectiveC:.

Well, that's absolutely true. In fact, RubyMotion goes out of its way to preserve Cocoa APIs exactly as intended. To keep Objective-C-style parameters, RubyMotion adds new.functions(with, this: syntax), which looks quite like [old functions:with this:syntax] (Ruby 2.0 will have the same sort of named arguments).

This is definitely a step up from other Objective-C bridges, which pollute their languages with nasty hacks. I imagine that's one reason why iOS development in other environments hasn't caught on quite like RubyMotion has.

Maybe another reason for RubyMotion's relative success is that Objective-C, [brackets] aside, is actually a lot like Ruby. Notably, they use message passing for functions, which are interceptable and hackable in both languages. Objective-C has moved towards Ruby in recent years with blocks, literals (@[], @{}), and indexes on normal objects (like someNSObject[2]).

So actually, that UIButton example from earlier? You can now write a wrapper in Objective-C that looks remarkably Ruby-like:

[button when:UIControlEventTouchUpInside do:^{
    self.view.backgroundColor = [UIColor redColor];
}];

But that's not "the way" for most Objective-C developers, so there's no similar movement towards adopting these nice wrappers.

So I guess it turns out Objective-C is a language that's nearly statically translatable to Ruby, so long as you don't use lots of custom C in your iOS or Mac apps. And even if you do use some plain-old C, most of Apple's C APIs and libraries actually work as expected in RubyMotion!

Performance

That all seems great, but do the niceties of Ruby come at a cost?

Some are inherently skeptic of RubyMotion's performance. That's pretty reasonable, since normal Ruby has pretty dodgy performance. But there's a giant difference between Ruby and RubyMotion: compilation.

RubyMotion code is compiled to machine code and uses the Objective-C runtime. There's no interpreter, no garbage collector, nothing except bare-metal machine code that looks basically identical to normal Objective-C. MacRuby does use the Objective-C 2.0 GC, but RubyMotion uses an ARC-like system for memory management like every other modern iOS app.

1.x

All that said, RubyMotion is still 1.x software. There are bugs in some under-used APIs, occasional memory management hiccups, and some tool flakiness.

But damn does HipByte patch things quickly. Every time I've reported some weird behavior to @RubyMotion, it's fixed within a week. And they aren't just pushing bug fixes: the team continues to push awesome new features like better functional tests and static library compilation on a weekly basis. And if you want to contribute, part of the source is open to pull requests.

Unlike MacRuby, RubyMotion isn't an internal project that Apple manages according to its resources. RubyMotion is an independent, commercial product with a passionate and amazingly talented team. They have a vested interest in continuing to make it better, and I'm incredibly excited to see where it goes from here.

Why not?

With all that RubyMotion can do, why aren't you moving to use Ruby for your Apple apps right this minute?

I asked a friend a similar question the other day. He said learning iOS with RubyMotion would be like "starting front-end dev with jQuery". At the moment, there's a lot of truth in that statement. Knowing Ruby will only get you so far, as the Cocoa APIs which will make up 80% of your project are preserved in all their Objective-C glory.

But that's changing. The Rubyfication of Cocoa is going strong and some, myself included, are betting their money that it's the way of the future. And in that future, all you need to make an app is idiomatic Ruby.

So what are you waiting for? Get to it.

Enjoy this? Follow for more on Twitter