RubyMotion and Storyboards: two great tastes that taste great together
Thursday, May 17, 2012 at 12:11PM Ok, I'll admit it. I'm very excited by RubyMotion--the new framework that lets us write Ruby-based applications for our iOS devices. I've long been a huge fan of MacRuby, and I've been waiting impatiently for it to show up on iOS. I'm not sure how they managed it, but HipByte made the magic happen, and I purchased a developer's license as soon as I learned about it.
Unfortunately, my first impressions were somewhat tempered. Don't get me wrong, I loved the Ruby bits--I just wasn't that excited about building my interfaces by hand. Yes, I could use Xcode to create nib files and copy them into my project's resources directory--but that seemed like a lot of work…especially as the number of nibs grew.
However, RubyMotion is moving at near-light speed. In the few weeks since release, we've already seen four updates, and I must say, RubyMotion 1.4 made me giggle like a school girl. This update added support for Storyboards. Suddenly RubyMotion became a lot more useful to me.
To test out the new Storyboard integration, I've created a silly proof-of-concept project. It doesn't do anything useful, other than act as a test bed for trying various Storyboard fatues. Still, I'm quite pleased with the results. The process remains a bit more manual than I'd like--but it's a huge improvement over dealing with multiple nibs, or (shudder) building everything by hand.
There are a few steps that may not be obvious, so I'm going to quickly walk through everything at a high-level. For more details, be sure to check out the source code. It's small, and everything should be obvious. As always, if you have questions, just leave them in the comments.
Building the App
First things first, I could simply create a storyboard file with Xcode, and layout all my scenes. However, this would be largely useless. Yes, I can draw the segues and relationships. I can even change the view controller classes to the correct Ruby classes. However, I cannot connect my outlets or actions.
The problem is, Xcode doesn't know anything about my Ruby classes--so it cannot know which actions and outlets are available. Therefore, I have to make an entire Xcode project, and then create stub classes for my Ruby classes (at least, for any class with an outlet or action). I can then define the actions and outlets in these stub classes, and draw connections to my heart's content.
Note: The Xcode project doesn't need to compile or run. I just need header files with the proper IBOutlet and IBAction tags.
Once we have a completed storyboard, simply copy the storyboard file into our RubyMotion project's resources directory. You'll have to repeat this step any time you modify the storyboard. Usually, rake will detect the new storyboard file and update everything with the next build--however, if you're not seeing the expected changes, make sure you actually saved the storyboard, then copy it again. Delete the previously compiled version (it will also be in the resources directory, and will end in "c"), and then execute rake clean, just to be safe.
With the storyboard in place, I simply created Ruby classes whose names matched the class stubs in the Xcode project. Then I create their outlets and actions.
For outlets, I simply declared attributes with matching names:
attr_accessor :label, :textField, :delegate
Similarly, Actions are just methods. If an action method in the Objective-C header looks like this:
- (IBAction)textChanged:(id)sender;
The RubyMotion equivalent will be:
def textChanged(sender)
# do something here
end
Next, I need to load the storyboard when the app launches. In an Xcode project, this would be defined in the info.plist. In RubyMotion, we can set those values in the Rakefile:
app.info_plist['UIMainStoryboardFile'] = 'MainStoryboard'
Finally, my app delegate needs to implement the window property. Oddly enough, I cannot use attr_accessor :window for this. Instead, I must explicitly create the required accessor methods:
def window
@window
end
def setWindow(window)
@window = window
end
And, that's it. The storyboard-based app is good to go.
If you have any interest in Ruby and iOS, then I'd strongly recommend giving RubyMotion a look. Yes, the developer license is a bit steep. If you need a free trial, you can always play around with MacRuby on the OS X side--however, honestly, I think RubyMotion feels more slick and solid at the moment. Especially since I can't seem to get MacRuby's Xcode integration to work with the new App-store version of Xcode.
Note: If you look at my Rakefile you will see that I've explicitly set the codesign_certificate. Typically, you won't need to do this. However, I have multiple code sign certificates on my laptop, and by default, RubyMotion selects the wrong one.



Reader Comments (4)
Nice!
Source as a zip download? How about a GitHub repo.
metageoff--what, you're not satisfied with access to free sample code. ;)
Seriously, there are valid reasons to host the files myself. For example, I think a zipped download is easier for beginners. After all, you just need to click the link. It literally could not be easier.
However, mostly it's just laziness on my part. I've only ever used GitHub to download other people's projects. I probably should look into it, but it's a low priority right now.
This is a great resource that I wish had existed before I had to stumble through the same things you did.
BTW, I have a rakefile I use with RubyMotion that automatically updates any of the Xcode resource files in my motion project if they've been changed in my Xcode project. You can get it from https://github.com/AaronH/rubymotion-builder-tweaks
Aaron,
I'm glad you like my write-up. I can save so much time laying out my apps initial workflow with Storyboards--this really is a key feature for me. My only complaint about it is that all the code that interacts with UIKit ends up looking like Objective-C, not really like Ruby. However, I suppose that the parts of the code that don't interact with Objective-C so closely will begin to take on a more Rubyish feel.
I'll definitely take a look at your rake file. I'm OK having a separate Xcode project, as long as the rakefile automatically copies my resources over before building. Having to move everything manually is a bit annoying. But, still, better than laying everything out manually.