RubyMotion is a powerful implementation of Ruby on top of Apple’s iOS framework (it’s hard for me to write a good descriptor for this — is it Cocoa or is it iOS? But you get the idea). People travel many paths to get to RubyMotion and a very frequent one is the “I know Ruby so now I can write iPhone apps” path. The other direction from which people arrive is “I know Objective-C and now I’d like to use RubyMotion instead.” This post is directed more toward the former group and suggests a preamble and then some tips on what works for me and what doesn’t. If you have had different experiences, please let me know in a comment.
RubyMotion Is Not A Framework
If you are a Rubyist, you probably have experience with Rails, Sinatra, or one of the other frameworks for server-side app development. RubyMotion is not like this at all. The framework is the underlying iOS API. What that means to you, the developer, is that you will not be able to forget about all that annoying Objective-C stuff — it’s not abstracted away for you. You will eventually need to read some Objective-C and most Q & A about iOS development techniques is couched in terms of Objective-C. So, in no particular order, here we go with my tips:
Learn to Read (and maybe write) Objective-C
I know the whole point of getting RubyMotion was to not use Objective-C, but you are still working in an
environment that was carefully crafted with Objective-C in mind. Take, for example Cocoapods. These are
analogous to Ruby Gems, but for Cocoa projects. They are written in Objective-C, and seldom documented
extensively. To know what methods and properties a given pod exposes, you will have to read the Objective-C
— at least in the headers — the
.h files located in
vendor/Pods/YourPodName. Don’t get me wrong —
some of the pods have great documentation, but there are a number of very useful ones that you just will
not be able to use without reading the source.
Another example is the #lazyprogrammer one. I’m being snarky here because it’s something I do sometimes as well. When I know I’m way outside the “It’s a RubyMotion” purview and I’ve spent quite enough time running down rabbit holes with Google, I may post a question on StackOverflow. But I don’t post it with the tag RubyMotion because a lot of the Cocoaheads will just ignore it. I post it with the keywords:
iOS, whatever, whatever
I also post all code translated from Ruby to Objective-C.
So, for example, if I were asking a question about customizing a
UITableCellView I would expect to
get a ton of “just create a separate NIB and blah, blah, blah” answers, which would be perfectly on
point, except that I want a programmatic example. So I might say: “I am creating a subclass of
UITableCellView programmatically — I know I can do this in IB but…”, which winnows out the
(probably correct) answers about how to accomplish the task with IB.
RubyMotion Still Requires You to Learn iOS
The more you know about iOS, the more your code will look right and work right. There are established conventions — some from Apple and some from people who’ve been doing this a while and know things about things. Here’s an example:
Initializing A Subclass of an iOS Class
It’s necessary in many cases to write subclasses of the standard iOS classes. For example:
class MyFineTableViewController < UITableViewController def initialize(some_variable) @some_variable = some_variable end end
Then in your calling code, you use a Rubyish sequence like:
@my_controller = MyFineTableViewController.new('hello')
This seems pretty straightforward but it’s not the way iOS classes initialize themselves. You’re better off doing something along the lines of:
class MyFineTableViewController < UITableViewController def initWithTitleString(title_string) initWithNibName nil, bundle:nil @title_string = title_string self end end
Then in your calling sequence, you use something like:
@my_controller = MyFineTableViewController.alloc.initWithTitleString('hello')
This Is Important: If you use the
alloc.initWithFoo initialization paradigm, you need to
call the superclass’s designated initializer and (read carefully) you need to return
Failure to return
self will cause you cryptic and evil errors that will cost you many gray-matter
cycles to decipher.
Initializing Your Own Ruby Classes
There’s no need to follow the
alloc.init paradigm for pure Ruby objects. You can use the Ruby
initialize as you normally would, and code like:
MyModel.new(:name => 'yojimbo')
to initialize. This also goes for classes that RubyMotion wraps like
As long as you are using the Ruby initialization, you’re ok.
an_array = Array.new # or another_array = 
these work just perfectly. As does:
an_array = NSMutableArray.arrayWithCapacity(10) # or an_array = NSMutableArray.alloc.initWithCapacity(10)
So many ways to achieve the same thing…
Mixins or Inheritance
One thing Ruby brings into the iOS programming world is a straighforward way to mix functionality into your classes. But let me back up to inheritance because it’s pretty straightforward to understand.
Inheritance is how you derive a specialization from an iOS class. So, for example,
is a kind of
UITableViewController, which makes sense.
PeopleController implements whatever is
specific to people and lets
UITableViewController provide the rest. This is a typical single
inheritance use of OOP. So, in code, you write:
class PeopleController < UITableViewController # more stuff end
Mixins are a different kettle of fish, so to speak. They allow you to mix functionality directly into the namespace of your class. I’m using it in MotionModel. Here’s an example:
class Person include MotionModel::Model include Validatable include MotionModel::Formotion # other stuff end
I’m not going to talk about writing mixins here except to remark that while inheritance observes an
“is_a?” relationship with a base class, mixins implement more of an “acts-as” relationship. In the
foregoing example, by including
MotionModel::Model I have a class that acts like a model. And by
Validatable, the class is further extended also to act like it is validatable. The point
is that it can act like a number of different things, not just its base class.
On a personal note, I’ve found mixins enormously productive in refactoring code because I am not
inheriting from a single monolithic base class that has all the code I’ve pulled out of subclasses.
Rather, each refactoring goes in a module I can mix in. In my current project I have modules for IAd,
BarButton, ViewHelpers, and so on. Not all controllers show ads, so I don’t mix that in when I’m
not showing an ad. Similarly, not every controller needs to add custom
UIBarButtonItems, so I only
mix that in where I need it. That’s my personal preference. I could just as easily have refactored
the code into a set of base classes like:
Person < FormotionWithValidationsModel < ValidatableModel < BaseModel
This strategy works too and is, in some cases, much easier to implement if you know the contracts your classes have and services they need to provide. Rails is implemented (at the user level) on a single-inheritance model for the most part. A good portion the magic stuff is done in mixins, though.
This is one of the coolest parts of Ruby. All classes are open and can be modified at runtime. Code that changed like this used to give me the shivers but now that I’ve used Ruby for a number of years, I’ve come to understand that in most cases it works just fine. Of course, if you plan to do this and share it, remember not to violate the priciple of least surprise. Especially if you override a method in a built-in class.
Method signatures in plain ol' Ruby are easy to understand. Either the method is defined or it isn’t. Either it has the right number of arguments in the invocation or it doesn’t. But in RubyMotion it’s a bit different. You might implement the delegate method:
def tableView(tableView, editingStyle, indexPath)
and wonder why the heck it isn’t being called. Well, it’s an honest mistake. The method signature for the delegate method is:
All this punctuation is significant. If you see a colon (:), it means “stick the receiving argument right here.” Inside iOS, when UIKit is figuring out whether to apply some kind of edit based on tapping an accessory, it will call this method by sending a selector. The selector is implemented in RubyMotion exactly conforming to it’s Objective-C signature. So it’s:
def tableView(tableView, commitEditingStyle:style, forRowAtIndexPath:indexPath)
Notice how each colon translated into a receiving argument?
What I often do when translating the Objective-C to Ruby is this:
- Paste the declaration from the documentation into my code:
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
- Turn it from an Objective-C instance method (the
-in the above line) to Ruby.
def tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
- Remove the first colon and the return type.
def tableView tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
- Insert commas between the arguments and remove the type information.
def tableView tableView, commitEditingStyle:editingStyle, forRowAtIndexPath:indexPath
- Optionally, parenthesize the argument list for readability.
def tableView(tableView, commitEditingStyle:editingStyle, forRowAtIndexPath:indexPath)
By doing this I save myself the time of guessing at method signatures and wondering why stuff doesn’t get called. As I find methods that are more frequently called, I wind up not doing this because I know the parameters, but you can see how this is useful when using some obscure set of methods for the first time. Also, check out the RubyMotion docset for Dash.
Only Observe What’s Yours to Observe
In iOS, it’s very efficient to subscribe/observe or “listen” for events. This works great until you forget to unhook one of the observers. If you set up an observer, make sure it is unhooked before the object is destroyed or you have a potential leak. It’s not clear how adversely forgetting to unhhok affects performance, but you can see how this kind of thing would build up if a number of controllers are watching events and running code on them even if it’s not appropriate to do so.
Live the Lifecycle
Views have a very specific lifecycle and their controllers are called at each stage. Here are some interesting ones:
init — ‘nuff said. It’s the designated initializer. This is called when any object derived from NSObject is instantiated
viewDidLoad — Called when iOS has successfully loaded a view but not yet displayed it
viewWillAppear — Called when iOS is about to load a view but before it has displayed it
viewDidAppear — Called when the view successfully loaded
viewWillDisappear — Called when iOS is about to hide a view but hasn’t yet
viewDidDisappear — Called when iOS has hidden the view
viewDidUnload — Called when iOS unloads the view
From my perspective, the best reason to use the callbacks during the presentation of the view are to:
Do any styling you might need
Add any subviews you require
Hook up any observers
The callbacks when the view disappears are often used to:
Free resources if you have any locked down
Typically, if you added a subview in an Objective-C program, you would remove it during teardown and
then set it to
nil so it could be reaped (or you could explicitly release it). ARC mitigates this
and you should not have to manually maintain the parallelism between adding and removing views or
Well, that’s about it for today. I just wanted to put a tips I’ve gathered from writing RubyMotion code down in writing and I hope they’re helpful.