How I Built My First Native OSX App in Just 6 Weeks Using RubyMotion

by Nicky Hajal

I am a web-developer – it’s something I’ve loved to do since I was 12 years old (now 27).

That being said, I’ve always had the idea that it would be nice to build native applications. The problem was that whenever I tried I’d hit a few barriers and end up back where I was comfortable: making web-apps.

Recently, that changed.

On July 16th 2014, I broke ground on my first ever OSX application, a productivity tool called ActionAlly. About 3 weeks later it was at the point where I could very roughly use it in my daily life and a month later I had my first customer.

So, what was different this time around?

Instead of fighting against Objective-C, as I’d tried in the past, ActionAlly was built entirely in Ruby, thanks to RubyMotion.

What RubyMotion Is and How it (Reluctantly) Came Into My Life

I initially had the idea for ActionAlly about 6 months ago. First I tried building it in Objective-C but it was going slow enough that it felt like my time was better invested on other projects (that probably wasn’t true, just a feeling that had me stuck).

I decided I’d at least try hiring a developer to build it for me but it turned out to be pretty difficult to find an OSX (not iOS) developer, especially at a price I’d be willing pay for a risky project.

Then my good friend Deon Don put me in touch with Stefan Haflidason who mentioned RubyMotion to me in an exchange of emails.

At first I totally ignored it, thinking it was like PhoneGap and other solutions that create products that just don’t feel as crisp and polished as true native apps.

It was only after a few weeks of continued stagnation that I gave it a closer look and fully understood that RubyMotion is very unique: it allows Ruby code to compile into the very same machine-code that Objective-C does.

RubyMotion results in truly native applications from a language that is much more comfortable to write in than Objective-C. (I actually had never programmed in Ruby before but found it completely painless coming from PHP/JavaScript/Coffeescript)

How I Learned RubyMotion

When I finally realized what RubyMotion made possible, I was excited. I bought a license (it costs $200 which was worth it for me) and started scouring the net for tutorials, books and screencaps.

I started by reading Clay Allsop’s PragProg RubyMotion Book on a flight from NYC to Barcelona. I got through most of the book in that one sitting (a testament to the structure of the book) and felt like I had a pretty good idea of how to get started.

Something I discovered was that because RubyMotion was initially released for iOS there are many, many more resources for mobile developers (Clay’s book included). This was great for picking up basic concepts but there are a lot of differences between the Cocoa (OSX) and Cocoa Touch (iOS) APIs.

As a result, there wasn’t really any start-to-finish example I could find to get me going. I spent a lot of time adapting what I found from iOS tutorials and banging my head against the wall to figure things out. (At the end of this article is a full list of the resources I used to learn RubyMotion).

A Simple Technique that Kept Me Focused on Specifically What I Needed to Learn

(This part isn’t exclusively RubyMotion related but a critical component of my learning process)

From the start, I created a list of “Questions to Answer” (QTAs) that I knew I’d need figure out in order to make ActionAlly work. Here’s an example:

  • How do I create an overlaid, fullscreen window?
  • How do I save and retrieve data?
  • How do I create and change views?
  • How do I detect when the machine wakes back up from being asleep?
  • How do I style an NSTextField?
  • And on and on…

Instead of trying to answer all the questions before I got started, I would figure out which question was the real bottle-neck and I’d just hone in on that.

To begin I focused 100% of my attention simply on getting a basic app running and a semi-transparent window overlaying the screen – the base canvas for ActionAlly.

Then I focused 100% of my attention figuring out how to detect when the machine wakes up so that I can call my transparent window to the top of the screen at that moment (an important ability for ActionAlly).

By simply adding questions whenever they came up and focusing my attention on resolving only the most important one at any given time, my effort was focused and I was constantly rewarded as my app steadily came together.

It may be that this technique would have helped in my past experiences with Objective-C but I think it’s partly thanks to Ruby and RubyMotion’s straight-forward nature that meant I was never fighting against the language itself.

All of my energy was invested learning the Cocoa APIs and finding RubyMotion libraries that could help (of which there are many, more on that below).

(Here’s an example of how I track QTAs): https://www.evernote.com/l/AAo_SSszyuRChq4DhNmh4UsgYeqSqzAdn2M)

(I wrote more about this process here: QTAs: A Dead-Simple Way to Steadily Make Progress Despite Challenges)

RubyMotion Compared to Web Development

RubyMotion is great for programmers coming from a web-development background as it bridges much of the gap in knowledge and workflow. Here are some similarities and differences:

You Can Use Your Favorite Text Editor

One great thing about RubyMotion is that you do not need to use XCode. Your favorite text editor will do fine (I use Sublime Text).

Create Layouts and Styles in Code (Using Interface Builder isn’t the Norm)

I use MotionKit to build and style views/layouts as well as manage constraints for auto-layout.

I’m pretty sure it’s possible to create layouts with Interface Builder and use them in RubyMotion but coming from HTML/CSS, I’m happier creating layouts programmatically.

Compiling is Weird

One thing I love about web-development is how quickly you can test code changes with just a quick page refresh.

There’s no getting around it, but compiling is a bit strange coming from web development and feels like it slows me down.

It is important to mention here that RubyMotion has a REPL that allows you to make changes and debug live as your application runs! It’s amazing and I’m still getting used to making the most of it.

Simple Things Aren’t Always So Simple (plus Subclasses Galore!)

This is always normal when learning something new but there were a lot of basic tasks (like adding padding to a text field or controlling the cursor when ontop of an element) that turned out to be pretty difficult to achieve effectively.

The solution almost always involved sub-classing the Cocoa APIs and writing the functionality you want. At first, this is a bit frustrating but it is something I’ve gotten used to over time.

Biggest Challenges

Generally speaking I have nothing but positive things to say about RubyMotion but of course there were some challenges along the way.

It’s Still a Budding Community
I am often amazed at the quality of the libraries for RubyMotion. That being said NodeJS and NPM definitely spoiled me with the quantity of packages available and the ease of getting them integrated.

Similarly, when searching for a problem or specific error it’s pretty common to get very few leads which leaves you sitting without many paths to go down.

The good news is that the community is friendly, responsive and RubyMotion support itself is always available and has been very helpful for me.

XCode Updates Causing Problems

The latest XCode updates created some problems with the motion-sparkle framework that I use for auto-updates. Thankfully there was active discussion as we resolved the issue but it did take more time than I would have liked if ActionAlly had been live at the time. (But really, big thanks to everyone who helped resolve that issue!)

Some Annoyances with RubyMotion’s View Selector 

RubyMotion allows you to set the REPL scope to a view simply by holding the command key and clicking on an element of your app. Unfortunately, this only tends work for more top-level views.

Additionally, since ActionAlly has keyboard shortcuts using the Command key, the view selector often appears and blocks out the app.

I emailed RubyMotion support about this and they were quick to respond and create this ticket http://hipbyte.myjetbrains.com/youtrack/issue/RM-664

Let’s Sum Things Up

Overall, I am a huge fan of RubyMotion.

The community is growing and has already provided most of the fundamental libraries most apps would rely upon. RubyMotion support is quick to reply and helpful.

Coming from a web-development background, it made building my first native application a much more approachable endeavor than it had been in the past. I’m constantly amazed that the result of my work is just as crisp and performant as apps built in Objective-C – but it is.

If you’ve been wanting to build native applications but struggled with getting started in Objective-C, I highly recommend giving RubyMotion a try!

Appendix

ActionAlly’s Stack

As I mentioned earlier, there are some great libraries available for RubyMotion. Here are the essentials that I’m using for ActionAlly:
– Motion-Model (using the SQL Adapter via FMDB) makes storing and retrieving data-objects straight-forward for someone coming from the world of relational DBs
– Motion-Kit for managing layouts and easily creating constraints/auto-layout
– SugarCube to make dealing with NSDates, Timers and NSColors a lot nicer
– Motion-Sparkle for handling auto-updates via the Sparkle framework

Objective-C Libraries

One of the great things about RubyMotion is that you can include existing Objective-C Libraries in a snap. Here are some useful libraries in Objective-C that ActionAlly uses:

  • DDHotKey (https://github.com/davedelong/DDHotKey) for handling global hotkeys
  • LaunchAtLoginController (https://github.com/Mozketo/LaunchAtLoginController) to handle launching the ActionAlly at login
  • UKIdleTimer (https://github.com/uliwitness/UliKit/blob/master/UKIdleTimer.m) for detecting idle time

Resources to Learn RubyMotion