00:00:16.560
All right, hello everybody! I'm Brian Sam-Bodden and first of all, smile. All right, so um.
00:00:25.519
Whoa! I'm here to talk about RubyMotion, and I titled this talk 'TDD in iOS Apps for Fun and Profit with RubyMotion.' The profit part hasn't arrived yet; I'm working on that, but I'm having a lot of fun with it.
00:00:30.640
Let me tell you a little bit about me. I'm not from Portland. Actually, this is awesome to be the last session of the conference. I hope you enjoyed the conference. I had a great time, and I hope to see you next year.
00:00:42.440
I am a longtime programmer and have gone through pretty much every language you can think of. I started with Lisp, which lasted about three months before we ran out of money, and then I ended up doing Java for a long time. In 2005, I met DHH here at OSCON.
00:01:08.000
I was talking about a clone of Rails, and he promptly made fun of me. Fast forward a year later, I was running my own Rails company. What do you know? I live in Phoenix. I'm not in the witness protection program, which seems to be the tendency in Phoenix.
00:01:36.880
I want to start with some confessions: I'm an Apple fanboy. There should be no reason a 41-year-old man has a white leather case on his iPhone. But you know, I've been paying that price willingly.
00:01:54.799
I work with Ruby every day; we're a Rails consultancy, and we do TDD (Test Driven Development) for our clients, using agile practices—the whole shebang. Before that, I used to do desktop applications back in the 90s with Borland Delphi. Do you guys remember that? Remember 4GLs?
00:02:28.480
I really wanted to create iPhone and iPad applications, but Apple wants you to use Xcode. And Xcode took me back in time to those Delphi and Visual Basic days; it felt pretty retro.
00:02:54.560
I tried it, but I just couldn’t get myself to go back to that mode of operation—basically dragging and clicking and connecting things with dropdowns. It just didn't feel right; it felt like a complex environment where there was no flow.
00:03:13.480
Everything about it slowed me down. Trying to do simple things typically resulted in me actually restarting the application from scratch.
00:03:24.560
Then there's Objective-C. I know there's a lot of people who really like the language, but for me, I’ve done C, C++, and Java. It felt like a weird combination of some good things that turned out pretty bad, like a Rosemary's Baby version of a programming language.
00:03:44.920
So I tried it, but I couldn’t do it. Then I heard about something called RubyMotion. Actually, let me rewind a little bit. I found MacRuby because I wanted to create beautiful apps on the desktop for the Mac, so I started playing with MacRuby, which is a Ruby interpreter for the Mac.
00:04:05.720
I built upon Objective-C, but luckily somebody else did that work, not me. However, there were a lot of constraints that iOS imposes which would not allow you to build an iOS app with MacRuby.
00:04:31.680
Therefore, someone created RubyMotion, which you can think of as a fork of MacRuby that deals with the restrictions imposed by iOS on the environment.
00:04:48.479
RubyMotion is a commercial product, but it is relatively cheap at $199. If you stick with me till the end of the session, I’ll give you a surprise slide.
00:05:10.039
The source is available, but not all of it; there are some parts hidden from the public. However, there are contributors doing open-source work for RubyMotion. A lot of gems and DSLs are appearing to help us deal with the sometimes complex nature of iOS development.
00:05:41.680
They’ve titled it a Ruby-based toolchain for iOS. It is based on MacRuby but targets iOS, compiling and creating binary artifacts that you can send to the App Store and sell.
00:06:05.920
RubyMotion was created by Lawrence Santini—I'm probably mispronouncing his name, but I'm sure he'll be okay with it. He worked at Apple for seven years, focusing on iLife and OS X, so he knows the environment well. He was also a Rubyist on the side.
00:06:40.360
MacRuby came out of his effort, as well as a few other people. His gripe was that Xcode just isn’t a good environment for dealing with MacRuby.
00:06:52.760
When I was playing with MacRuby, I was using TextMate or Vim, because I didn't like Xcode either. Xcode appears complex and beautiful initially, but after a couple of hours of struggling with it, it just didn't work for me.
00:07:06.360
So who is RubyMotion's target audience? It’s really for us. If you are an expert in Xcode, Objective-C, and iOS development, you will probably laugh at RubyMotion because it's a language you may not understand, following processes you are not used to.
00:07:30.560
One of the things I started doing when I began working with iOS was trying to determine how they do their test-driven development, and they barely test at all. It seems testing has devolved into user testing and throwing things over the fence for luck, reminiscent of the old QA Department approach.
00:08:05.439
I can’t work that way anymore; it’s been too long since I adopted better habits. So, RubyMotion is aimed at Rubyists who might not know iOS at all.
00:08:20.400
That was me; I started learning the iOS APIs through the lens of RubyMotion. Web developers should feel pretty comfortable with RubyMotion since it provides a Rails-like development experience.
00:08:45.560
You’ll see that we have models, views, and controllers, and there are gems that provide APIs to make each of these MVC stack areas more digestible. Also, you can use whatever shell you prefer to work in.
00:09:03.760
You can write tests, fail them, pass them, and progress along your design and development process in that way. So TDD (Test-Driven Development) and BDD (Behavior-Driven Development) complement RubyMotion.
00:09:24.480
It might not be as fluid and agile as a modern Rails application yet, as we've evolved our processes over the years, but we are getting there.
00:09:50.360
My goal for this session was to prove to myself that I could practice TDD with a RubyMotion app. So let's jump a little off-track for a second. An obvious question comes up: why not just build a web application?
00:10:11.560
Users still get this tactile responsiveness from native applications that they don’t seem to achieve from HTML5 web apps. I'm not going to spend too much time on this, but I left a few graphs you can take a look at.
00:10:52.000
I think that to hit the market, having an HTML5 application is quite good, but learn how your users interact with your application. Eventually, you might want to target a native application if your domain requires it.
00:11:15.200
If you're dealing with gaming or using any of the hardware devices on the phone, you probably want to go native.
00:11:32.480
Now, let's do our first example: a Hello World in French. I hope that's right! I'm going to show you a quick slide about RubyMotion, which is encapsulated in the motion command.
00:11:48.320
Using the motion command, which is reminiscent of the Rails command, you can create your applications. In this simple example, I'm creating a Hello World application.
00:12:05.680
RubyMotion also supports rapid development. As you create a RubyMotion application, there are a set of Rake tasks available for different purposes.
00:12:34.560
You can build for specific devices, for the simulator, and run your tests. In most cases, you'll use Rake to run the application live and RSpec to actually execute your tests.
00:12:53.680
Those are typically the two commands I use throughout the day while building iOS apps. So, I have a cheat sheet for my demo, and I have the simulator right on screen.
00:13:15.480
The first thing I’m going to do is show you the version of RubyMotion that I’m running, and then I'm going to create my first RubyMotion application.
00:13:53.920
But if I run the right command, it's rapidly going to fail because I'm not in the directory of my application. This is that moment as a presenter where you're like 'Oops!' So, let me cd into the directory.
00:14:12.200
This application does nothing at this point, but as you can see, it launched the application on the simulator. If I exit out of the application, you can see that the icon for the app has been created.
00:14:34.000
The only problem is that it doesn’t have a single view, so you end up with a blank screen. Now, let me run it one more time—notice that my console is now sitting at a prompt.
00:14:57.760
That’s the REPL (Read-Eval-Print Loop) of RubyMotion, which is like the IRB or Rails console. That became my experimental lab to learn about iOS.
00:15:14.440
You know what the number one method you call on a class you don't know? It's often to figure out what methods are available! Typically, I started digging into the iOS APIs through the console, experimenting with different options.
00:15:41.280
The nice thing about the console is that whatever you do there reflects live on the simulator. So, let me take some of my examples and show you what’s going on.
00:16:07.679
The first thing you notice is that the 'self' object is 'main,' just like in many interpreters. I'm going to create something called an alert.
00:16:28.599
An alert is essentially a UIAlertView. It’s one of the iOS classes we use to create pop-up dialogues. I’m going to set some values including a title and a message.
00:16:50.720
Now I can go ahead and show my alert! And that’s the cool thing about it: yay! One dialogue on an iPhone app—awesome!
00:17:03.760
I know it looks simplistic, but having that feedback loop allowed me to actually learn iOS. I’m not an expert by any stretch of the imagination, but it has helped me to delve into something that once felt so foreign.
00:17:24.160
I can also dismiss that alert so it's a pretty simple environment to learn about iOS. If you're a Ruby developer and you've been wanting to learn about iOS, to me, this is the best vehicle you could find.
00:17:53.760
And it's only $199, so let’s go back to the slides for a moment. Again, that’s my primary reason to love RubyMotion: it's the REPL—that console allows you to explore the RubyMotion and iOS APIs.
00:18:20.320
RubyMotion essentially creates a thin wrapper around all of the classes available in iOS, similar to how JRuby and MacRuby operate. The REPL was what initially drew me into this platform.
00:18:52.480
You can interact with the running iOS application, learning and discovering along the way. Now, let me tell you about my second reason to like RubyMotion: bacon! Who doesn’t like bacon?
00:19:14.320
In particular, RubyMotion uses something called MacBacon, which is a small clone of RSpec. It's not completely syntax-wise a 1:1 match with RSpec, but it’s the framework we'll use for BDD and TDD with RubyMotion.
00:19:35.320
Here’s a little slice of MacBacon: you'll see that you have a describe block with a string indicating the context. Then you have before blocks like in most BDD frameworks.
00:20:01.360
You have your 'it' examples, so for example, an array should be empty or should not include certain things. There’s more fluid API chaining similar to RSpec.
00:20:22.280
Now, RubyMotion TDD highlights for the upcoming demo include that all testing is done with the red-green-refactor cycle for iOS.
00:20:40.440
Sometimes, this isn't as easy as it sounds. The RubyMotion environment is meant to test units; the largest unit you can test is a controller.
00:21:07.760
If you’re testing transitions from one controller to another, you need to go with something outside RubyMotion—like Cucumber. There are a few products out there that function similarly.
00:21:30.400
I'm using two libraries for this demo: MotionModel—an ActiveRecord-like clone for iOS applications using a simple local storage mechanism—and FormMotion, which implements a simple form for iOS.
00:21:51.840
I’m a fairly lazy developer, so I look for ways to declare a model and then create a form for data input in the app. I'm using both libraries combined for the demo.
00:22:16.760
There are now likely hundreds of gems available, covering everything from 2D gaming to handling various hardware devices on the phone.
00:22:37.240
Let’s take a look at the second demo. I have an application called 'Okona,' which according to Google Translate means 'done.' But I’m not sure if it means completed with a task or just done as in a mistake.
00:23:02.920
The application is on GitHub, so if you get a copy of RubyMotion, you can grab it and play with it. This is what it looks like.
00:23:28.880
I'm using a get presenter, which allows me to fast forward through my commits. Notice how I have a large list of commits; I'm going to show you the initial commit.
00:23:55.440
By default, a new RubyMotion application has pretty much nothing—it includes an application delegate, which is your entry point into the iOS stack.
00:24:16.360
From there, you wire up your controllers, views, and models to present something to the user. The only test it includes checks for the presence of a window.
00:24:43.440
Now, I’ll run those tests, and you’ll see that I have a failed test. I believe Lauren intended for people to start at the red state of TDD and then start building the application from there.
00:25:06.920
But most examples I've found follow the iOS mantra: not testing or testing after the fact. If you have a powerful environment like Ruby and Rails, I think you should use it for TDD.
00:25:32.880
Now let me move forward to my first passing test. My first test says that I should have a window. I'm trying to pass the built-in test that comes with RubyMotion.
00:25:57.680
To do that, I just added a window inside the delegate. You can see some syntactical things that may look foreign to a Rubyist. For example, the word 'alloc' means to allocate memory.
00:26:21.080
You might remember 'malloc' from C and C++. In this case, I’m creating an instance variable called window and initializing it to fill the entire screen.
00:26:47.200
If I run my application now, you will see that it's still a blank screen, but it has one window—it’s just blank.
00:27:10.680
Now I’ll fast forward; this is a to-do application. I was aiming to build a simple list of to-dos with a due date and a boolean indicating if they are completed.
00:27:31.840
So I'm going to fast forward to my first failing test for real functionality—commit number five—which tests if it displays the given to-dos.
00:27:52.680
Let’s go to number five and run my test; I should see the test fail. Ignore the stack trace.
00:28:14.000
Now, with TDD, one of the challenges for newcomers is where to start. Sometimes you might try to start with something too complex.
00:28:37.360
With RubyMotion development, I decided to test something simple, like whether the to-do controller exists. Even if it’s a silly test, it’s a start.
00:28:55.960
Typically, I add a test stating the to-dos controller should exist. The original application test, which checks for the presence of one window, is now passing.
00:29:12.320
However, my test for displaying the given to-dos is still failing. Let’s now fast forward to the next commit where I implement the minimal code needed to pass that test.
00:29:32.080
For that, I actually used the console to learn how to show a table of data on an iOS application.
00:29:48.600
It turns out, there's a UI Table View, and that's where I started exploring. This exploration process involved digging into the APIs through the console.
00:30:05.920
You’ll see memory errors as you experiment, but eventually, you flesh it out to understand and build an app.
00:30:22.640
Let me show you the code to pass that test. I’m going to make it smaller.
00:30:44.640
I have my window as before, then I'm creating a table—which is a UI Table View. My table view also takes up the full width and height of the screen.
00:31:03.440
I created a simple array of items I should buy for my pantry. Then I created a data source object that the table view will use to get its data.
00:31:21.120
Notice that I'm implementing two methods here: number of rows in section and cell for row at index path. This will allow the table to ask how many elements it contains.
00:31:41.680
This is a good stopping point to discuss the weird syntax you're seeing. First, notice that there is a lot of camel case, which is different from the snake case we're used to in Ruby.
00:32:02.240
The methods are direct translations from the Objective-C signature. Objective-C has a concept of pseudo-named parameters, which Ruby 2.0 would have with named parameters.
00:32:22.720
In this case, the cell for row at index path method has a colon for index path, representing one of those named parameters.
00:32:43.440
For most people, it might seem like you're overwriting the same method, but in reality, it’s a completely different signature, and the RubyMotion interpreter handles it.
00:33:04.520
So, within my to-do data source, there's an array called data. When the table views ask for data, it will return a UITableViewCell.
00:33:26.360
This all comes from exploration through the console. I’ll run the test to show you where we’re at.
00:33:50.240
Now we have a clean passing set of specs. If we run the application, it’s not very exciting right now, but I have a table of data, and believe me, when I got to this point, I felt accomplished.
00:34:07.760
I was thrilled to have built an iOS application, feeling like I was mastering my domain. Give me a couple of weeks, and I’ll be rich!
00:34:36.400
Now, let me show you something exciting. If I run the tests, RubyMotion executes them and creates an application that represents the state of those tests at that moment.
00:35:02.760
So, running this again, it executes one swift test. But, if you have a comprehensive suite, when you click this application, it allows you to save snapshots of it, producing artifacts of your test suite.
00:35:26.720
I think that’s pretty cool—it's kind of like seeing a capybara running through a website, clarifying the path of behavior in your application.
00:35:41.760
Now that you've seen the TDD cycle with iOS and RubyMotion, let’s fast forward to a few later commits.
00:35:55.800
I’ll show you my development with FormMotion. Let me go to commit number 26.
00:36:12.480
Unfortunately, things get compiled—we're talking about object linking and embedding, processes we forget about while using Ruby.
00:36:31.680
With this framework, I've fleshed out my test suite to be a bit more comprehensive, now featuring a to-do controller, and my model.
00:36:50.320
Initially, I wanted the simplest possible failure—ensuring the model exists in the first place. I'm also checking that it provides names, descriptions, due dates, and validates them.
00:37:08.480
Now I can check to see if the to-dos controller shows the correct label for a given to-do. Let me quickly show you how that test looks.
00:37:25.680
I heard clapping, which means time is up—but let me finish. Here’s my to-do spec, which outlines a very simple model object.
00:37:47.840
And here’s my extended to-do controller spec—let me run this so you can see what we have so far, and then I’ll move quickly to the final commit.
00:38:02.760
Notice that I've started learning various aspects of iOS from the warning messages that appear in the console.
00:38:19.040
Even though I have practically every iOS book in print, I’ve only read snippets of each. Yet, this environment has been the best for learning.
00:38:36.560
Now I have a model backed by local storage for all my to-dos, and I'm using a FormMotion to provide a simple form, shown here.
00:38:55.760
Now, let me fast forward to my last commit to show you how it ended up.
00:39:18.720
There are 34 commits in total. It has been a fantastic experience building this example for the presentation. I learned a lot, clarifying things I thought I knew.
00:39:35.760
Here’s the final specification set—it’s compiling now, and I can see that it's coming.
00:39:50.640
The only problem with RubyMotion during presentations is that compiling time can be considerable. But now I have a slightly more complex test suite.
00:40:10.680
Now let me run the final version of my 'Okona' application. Notice I added decorations and a new button for creating to-dos.
00:40:27.760
It’s now more complete than before. When I save my new to-do, it gets placed at the bottom of the list—perhaps I should adjust it to appear at the top instead.
00:40:45.640
If I modify an existing item, it should reflect that.
00:41:02.240
Oh, what happened? I broke it! Well, that’s the reality of live coding.
00:41:18.360
But again, iOS is no longer a frontier too far for a Rubyist to travel to. I feel pretty comfortable with the environment now.
00:41:36.760
The syntax might be different, but the moment I come across a class I don't understand, I see many examples written in Objective-C that now translate directly to RubyMotion after just a few months of work.
00:41:58.360
It’s a viable avenue for building cool iOS apps. Let me show you some resources.
00:42:16.920
There’s a plethora of wrappers available for nearly everything you need for RubyMotion and iOS development.
00:42:41.000
Basecamp for iPhone is built on RubyMotion, combining some Rails components and a RubyMotion app.
00:43:05.040
Furthermore, numerous beautiful applications are built using RubyMotion—like apps tailored for the London subway, helping users find where to go and when trains arrive.
00:43:25.720
I didn’t have time to polish my example, but that’s probably the next step for me. Send me an email, and Lauren will give you a 15% discount on RubyMotion.
00:43:52.600
So do the math—about $14.
00:44:06.680
All the sample code is on my GitHub repository. I’m also planning to gather my experiences into an ebook about building RubyMotion applications in various genres, including games.
00:44:25.680
Any questions? Alright, well, thank you very much!