RubyMotion Tutorial for Beginners: Part 2

RubyMotion Tutorial for Beginners: Part 2
Get Started with Ruby Motion!

Get Started with Ruby Motion!

Welcome back to our RubyMotion Tutorial for Beginners series!

In the first part of the series, you learned the basics of getting started with RubyMotion, and created a view controller with a few styled views.

In this second and final part of the series, you will add the rest of the logic to this app, including making the label count down and getting the app fully wrapped up.

Let’s get back in motion! :]

Building a Countdown Timer

At this point, you have a label and a button, and the label has some static text on it. You want the label to count down from 25 minutes.

It’s clear there should be some sort of object responsible for handling the countdown, but you haven’t defined that object yet. While it’s tempting to add that functionality to MainViewController, that’s not really the controller’s responsibility. The controller’s job is to respond to events and direct what should happen next.

Run the following command in Terminal:

mkdir app/models

You’ll use the models directory to store the models containing the application logic of your app.

Run the following command in Terminal to create a new class that will serve as your countdown timer:

touch app/models/pomodoro_timer.rb

Open PomodoroTimer and add the following lines of code:

class PomodoroTimer

PomodoroTimer has no superclass; it’s just a plain-old-ruby-object, or PORO for short.

The first thing PomodoroTimer needs is an attribute to store its current value. To do this, add the following lines to app/models/pomodoro_timer.rb:

  attr_accessor :count

The attr_accessor macro declares a getter and setter for count. The equivalent to this in Objective-C is:

@interface PomodoroTimer : NSObject
@property NSInteger count;

Now, add the following method to app/models/pomodoro_timer.rb:

def initialize
  @count = Time.secsIn25Mins

By default count should be set to the number of seconds in 25 minutes, so you use the method you’ve just defined on NSDate to set this in initialize.

Weak References

PomodoroTimer also needs a delegate to report when certain events occur.

Add the following code just below the spot where you declared count:

  attr_reader :delegate

You’re using attr_reader here because the default setter for your delegate isn’t appropriate in this case. Using attr_accessor would create a setter that holds the delegate — in this case, an instance of MainViewController in a strongly referenced instance variable. But since you’re going to define PomodoroTimer as a property of MainViewController, using attr_accessor would create a circular dependency leading to memory leaks and crashes!

To avoid that mess, add the following method to pomodoro_timer.rb:

def delegate=(object)
  @delegate =

Here you define your own setter for delegate and set it as a weak reference. In Ruby, everything is an object, and weak references are no exception.

Add the following property to the PomodoroTimer class:

attr_accessor :ns_timer

This property, as the name suggests, will hold an NSTimer object that handles the countdown by firing once a second for 25 minutes.

Add the following method to the PomodoroTimer class next:

def start
  invalidate if ns_timer
  self.ns_timer = NSTimer.timerWithTimeInterval(1, target: self, 
    selector: 'decrement', userInfo: nil, repeats: true)
    forMode: NSDefaultRunLoopMode)
  delegate.pomodoro_timer_did_start(self) if delegate

This handles the creation of a new timer. Here’s what’s going on in the code above:

  1. If the PomodoroTimer already has an ns_timer instance, call invalidate.
  2. Set ns_timer to a new NSTimer instance that calls decrement once per second.
  3. Add the NSTimer to the current run loop, and if the delegate has been set then send pomodoro_timer_did_start to the delegate so it’s aware that the timer started.

You’ve yet to define PomodoroTimer#invalidate and PomodoroTimer#decrement.

Add the following below the start method you just wrote:

def invalidate
  delegate.pomodoro_timer_did_invalidate(self) if delegate

This method simply passes invalidate on to ns_timer and then notifies the delegate that the timer has been invalidated as long as a delegate has been set.

Finally, define the decrement method as follows:

def decrement
  self.count -= 1
  return if delegate.nil?
  if count > 0

This simple method decrements the value of count by 1 each time it’s called. If there’s a delegate present and the count is greater than 0, it notifies the delegate that pomodoro_timer_did_decrement. If the count is 0 then it notifies the delegate that pomodoro_timer_did_finish.

Note the private directive above; since decrement should only be used internally within the class itself, you make this method private by adding the directive above the class definition.

Run rake to build and launch your app; you can now play around with the new class you defined above. To do this, execute the following command in Terminal (with rake and the Simulator active) to initialize a new PomodoroTimer and assign it to a local variable:

p =

Inspect the value of p.count using the commands below:


The value should be 10 as expected:

# => 10

Call start on p to start the countdown sequence as follows:


To see the countdown timer working, evaluate p.count repeatedly — but don’t wait, you only have 10 seconds! :]

# => 8
# => 6
# => 2

Now that you know your timer is working, you can use it in your app.

Adding a PomodoroTimer to MainViewController

Open main_view_controller.rb and declare the following property on MainViewController:

class MainViewController < UIViewController
  attr_accessor :pomodoro_timer
  # ...

This holds the timer instance for this controller.

In the first part of this tutorial series, you added nextResponder to MainView as the target for touch actions, with the action name of timer_button_tapped. It’s finally time to define that method.

Still in main_view_controller.rb, add the following code below loadView:

def timer_button_tapped(sender)
  if pomodoro_timer && pomodoro_timer.valid?

You call the above action when the user taps the timer_button. If pomodoro_timer has a value — i.e. is not nil — and it references a valid PomodoroTimer, then invalidate the PomodoroTimer. Otherwise, create a new PomodoroTimer instance.

Add the private directive just below the method you just added as shown below:

# ...
def timer_button_tapped(sender)
  if pomodoro_timer && pomodoro_timer.valid?
# ...

This separates the public and private methods.

Finally, add the following method after the private directive:

def start_new_pomodoro_timer
  self.pomodoro_timer =
  pomodoro_timer.delegate = self

start_new_pomodoro_timer assigns a new PomodoroTimer instance to pomodoro_timer, sets its delegate to self, and then starts the timer. Remember, tapping the button calls this method to you need to start the countdown as well.

Run rake to build and launch your app, then tap the Start Timer button to see what happens:

2014-09-11 16:40:58.276 Pomotion[17757:70b] *** Terminating app due to uncaught exception 'NoMethodError', reason: 'pomodoro_timer.rb:22:in `start': undefined method `pomodoro_timer_did_start' for #<MainViewController:0x93780a0> (NoMethodError)

Hmm, something’s wrong with your app. Can you guess what the problem is?

When you start pomodoro_timer, it calls delegate methods on MainViewController — but those methods don’t yet exist. In Ruby, this results in a NoMethodError exception.

Add the following delegate methods above the private keyword in main_view_controller.rb:

def pomodoro_timer_did_start(pomodoro_timer)
def pomodoro_timer_did_invalidate(pomodoro_timer)
def pomodoro_timer_did_decrement(pomodoro_timer)
def pomodoro_timer_did_finish(pomodoro_timer)

The NSLog statements will print out a line to the console, just to show you that the methods are in fact being called.

Run rake once again and tap Start Timer; you should see the NSLog statements written out to the console as they’re called:

     Build ./build/iPhoneSimulator-8.1-Development
     Build vendor/PixateFreestyle.framework
     Build vendor/NSDate+SecsIn25Mins
   Compile ./app/controllers/main_view_controller.rb
      Link ./build/iPhoneSimulator-8.1-Development/
    Create ./build/iPhoneSimulator-8.1-Development/
(main)> 2014-11-13 13:52:44.778 Pomotion[9078:381797] pomodoro_timer_did_start
2014-11-13 13:52:45.779 Pomotion[9078:381797] pomodoro_timer_did_decrement
2014-11-13 13:52:46.779 Pomotion[9078:381797] pomodoro_timer_did_decrement
2014-11-13 13:52:47.779 Pomotion[9078:381797] pomodoro_timer_did_decrement
2014-11-13 13:52:48.779 Pomotion[9078:381797] pomodoro_timer_did_decrement
(nil)? 2014-11-13 13:52:49.778 Pomotion[9078:381797] pomodoro_timer_did_decrement
2014-11-13 13:52:50.778 Pomotion[9078:381797] pomodoro_timer_did_decrement
(nil)? 2014-11-13 13:52:51.778 Pomotion[9078:381797] pomodoro_timer_did_decrement

If you still get an exception, make sure you followed the instructions above about pasting the methods before the private keyword.

There’s just one more bit of housekeeping before moving on. In timer_button_tapped you ask if pomodoro_timer is valid?, but you haven’t yet defined a valid? method on PomodoroTimer; if you tap the button twice RubyMotion will throw a NoMethodError.

Add the following code just beneath start in pomodoro_timer.rb:

def valid?
  ns_timer && ns_timer.valid?

In this case, a valid result means that the PomodoroTimer has an NSTimer and that the timer is valid. Ensure you’ve added this method above the private directive, so that you can call this method on any instance of PomodoroTimer from within other objects.

Updating the Button

In MainView you specified that timer_button should have a different title if the state is UIControlStateSelected, but you aren’t setting that state yet. The button should become selected when the timer starts, and go back to the normal state when the timer is stopped or invalidated for any reason.

Add the following method to main_view_controller.rb:

def timer_button

This is just a helpful wrapper method that calls timer_button on the view. While it may seem arbitrary to do so, this actually helps the code adhere to The Law Of Demeter.

Replace the pomodoro_timer_did_start and pomodoro_timer_did_invalidate delegate methods with the following:

def pomodoro_timer_did_start(pomodoro_timer)
  timer_button.selected = true
def pomodoro_timer_did_invalidate(pomodoro_timer)
  timer_button.selected = false  

Run rake, then tap Start Timer; you’ll see the button’s title change as shown below:

Interrupt Button

Updating the Label

The countdown timer isn’t terribly useful at present since the on-screen timer doesn’t count down yet. Fortunately, that’s your very next task!! :]

Add the following method to app/controllers/main_view_controller.rb:

def timer_label

This updates timer_label in MainView should be updated with the current count of pomodoro_timer each time the timer decrements.

Still in main_view_controller.rb, modify pomodoro_timer_did_decrement to look like the code below:

def pomodoro_timer_did_decrement(pomodoro_timer)
  mins = pomodoro_timer.count / 60
  secs = pomodoro_timer.count % 60
  timer_label.text = "%02d:%02d" % [mins, secs]

Here you take the value of pomodoro_timer.count and break it down into separate minutes and seconds values. You then set the text of timer_label to a formatted string so that the minutes and seconds values will always appear as double digits.

Run rake again and tap Start Timer; you should see the timer count down from 00:10 to 00:00 as shown below:


It looks good, but watch the console carefully and you’ll see the timer continues to decrement past zero and pomodoro_timer_did_finish executes multiple times.

(main)> 2014-11-13 13:57:37.038 Pomotion[9408:386412] pomodoro_timer_did_start
2014-11-13 13:57:47.039 Pomotion[9408:386412] pomodoro_timer_did_finish
2014-11-13 13:57:48.038 Pomotion[9408:386412] pomodoro_timer_did_finish
2014-11-13 13:57:49.038 Pomotion[9408:386412] pomodoro_timer_did_finish

Ah — you’re not invalidating the timer. To fix this, modify pomodoro_timer_did_finish like so:

def pomodoro_timer_did_finish(pomodoro_timer)

Now when the timer reaches zero, you’ll invalidate it from within MainViewController.

Run rake and try the above scenario again; verify that the counter stops decrementing at zero and pomodoro_timer_did_finish is called just once.

Another small issue with the timer right now is that timer_label only changes when the timer has counted down from 10 to 9; the user doesn’t get to see the initial value of 10 seconds.

To solve that, you’ll rewrite a bit of code to set the label as soon as the controller receives pomodoro_timer_did_start.

Still in main_view_controller.rb define a new private method named update_timer_label and move the code from pomodoro_timer_did_decrement into the new method as follows:

def pomodoro_timer_did_decrement(pomodoro_timer)
# ...
def update_timer_label
  mins = pomodoro_timer.count / 60
  secs = pomodoro_timer.count % 60
  timer_label.text = "%02d:%02d" % [mins, secs]
# ...

Now edit pomodoro_timer_did_decrement, pomodoro_timer_did_invalidate, and pomodoro_timer_did_start so they call update_timer_label as shown below:

# ...
def pomodoro_timer_did_start(pomodoro_timer)
  timer_button.selected = true
def pomodoro_timer_did_invalidate(pomodoro_timer)
  timer_button.selected = false
def pomodoro_timer_did_decrement(pomodoro_timer)
# ...

Run rake then tap Start Timer to see that the timer now starts from 00:10:


Making it Look the Part

The timer is working well, but it could do with a bit of dressing up.

Add the following code to app/controllers/main_view_controller.rb, just below loadView:

def viewDidLoad
  self.title = "Pomotion"

Run rake and you should see the title appear at the top of the screen:


That adds a bit of life to the app, but the navigation bar could use a face lift.

Add the following CSS to resources/default.css to brighten up the navigation bar:

navigation-bar {
  background-color: #7F0000;
navigation-bar title {
  color: white;

These two CSS values give the navigation bar a red tint,and color the title bar text white.

Run rake to see the results:


Extending Core Classes

You can give your app a professional touch by gradually changing the color of the timer’s label from green to red as the timer counts down to 00:00.

To achieve this, you’ll need to create a method that will take two colors and mix them based on a specified proportion. Since this behavior is relevant to UIColor, you should add your new method as a class method there. However, this time around you can extend the class the Ruby way! :]

First, create a new directory under app called core_extensions:

mkdir app/core_extensions

This is a sensible place to define all of the extensions you add to the core iOS and Ruby classes.

Create a file in that directory named ui_color.rb:

touch app/core_extensions/ui_color.rb

The basic design of this effect is that timer_label can have one of four color combinations: all red, all green, a red-green mix, or grey (which will be used when the timer is inactive).

Add the following helper class methods to the UIColor class in app/core_extensions/ui_color.rb:

class UIColor
  def self.pomo_grey_color
    @pomo_grey_color ||= UIColor.colorWithRed(0.5, green: 0.5, blue: 0.5, alpha: 1.0)
  def self.pomo_green_color
    @pomo_green_color ||= UIColor.colorWithRed(0.0, green: 0.666, blue: 0.0, alpha: 1.0)
  def self.pomo_red_color
    @pomo_red_color ||= UIColor.colorWithRed(0.666, green: 0.0, blue: 0.0, alpha: 1.0)

This makes it easier to reference the custom red, green and grey colors in Pomotion’s color scheme.

Now you need to define a class method that will mix red and green proportionally to return a new UIColor value.

First, define the following method below self.pomo_red_color:

def self.new_from_two_colors(color_1, color_2, proportion)
  # 1
  color_1_r =
  color_1_g =
  color_1_b =
  color_1_a =
  # 2
  color_1.getRed(color_1_r, green: color_1_g, blue: color_1_b, alpha: color_1_a)
  # 3
  color_2_r =
  color_2_g =
  color_2_b =
  color_2_a =
  color_2.getRed(color_2_r, green: color_2_g, blue: color_2_b, alpha: color_2_a)
  # 4
  new_red   = color_1_r.value + (color_2_r.value - color_1_r.value) * proportion
  # 5
  new_green = color_1_g.value + (color_2_g.value - color_1_g.value) * proportion
  new_blue  = color_1_b.value + (color_2_b.value - color_1_b.value) * proportion
  new_alpha = color_1_a.value + (color_2_a.value - color_1_a.value) * proportion
  # 6
  UIColor.colorWithRed(new_red, green: new_green, blue: new_blue, alpha: new_alpha)

Taking each commented section in turn, you’ll see the following:

  1. First, you initialize four Pointer objects. Pointers are handled quite differently in RubyMotion than in Objective-C — in Ruby, everything is an object and pointers are no exception. To create a new Pointer, simply initialize a new instance of the Pointer class and pass in the type as a parameter. The type can be either an Objective-C Runtime Type, or a Ruby symbol with the type name. To access the pointer’s value you simply call the value property.
  2. Next, you call the UIColor method getRed:green:blue:alpha to pluck the RGBA values for color_1 and then assign them to their respective pointers.
  3. Now do the same for color_2.
  4. The proportion parameter is a float value between 0 and 1. You can think of this as “What percentage of color_2 do we mix into color_1?”

    The following calculation determines how much red the new color should contain:

    color_1_r.value + (color_2_r.value - color_1_r.value) * proportion

    Here’s some real numbers to help make sense of the calculation above. If color_1 is bright red (RGBA: 255, 0, 0, 1) and color_2 is pure white (RGBA: 0, 0, 0, 1), then color_1_r and color_2_r will be 255 and 0 respectively. The color that’s a 50% blend of color_1 and color_2 will be 255 + (0 - 255) * 0.5 = 127.5.

  5. Perform the same calculation for the green, blue and alpha values.
  6. Finally, return a new UIColor object with the new, proportional values.

Run rake and test your methods from Terminal as below:

# => #<UIDeviceRGBColor:0x9099e10>
# => #<UIDeviceRGBColor:0x97b59c0>
# => #<UIDeviceRGBColor:0x94ed360>

That looks good — but what about new_from_two_colors?

UIColor.new_from_two_colors(UIColor.pomo_red_color, UIColor.pomo_green_color, 0.5)
# => #<UIDeviceRGBColor:0x909a510>

If you can stand to do the hexadecimal math yourself, you’ll see that the numbers above work out correctly. :]

Back in main_view_controller.rb, add the following lines to the bottom of update_timer_label to change the text color of timer_label as the timer decrements:

if pomodoro_timer.count > 0
  proportion = pomodoro_timer.count / Time.secsIn25Mins.to_f
  color = UIColor.new_from_two_colors(UIColor.pomo_red_color, UIColor.pomo_green_color, 
  color = UIColor.pomo_grey_color
timer_label.textColor = color

To determine the color above, you first check that pomodoro_timer‘s count is greater than zero; if so, then calculate the color proportion as the result of the time remaining divided by the total starting time.

Then assign color, which is a proportionate mixture of UIColor.pomo_red_color, and UIColor.pomo_green_color, to a new instance ofUIColor.

Finally, set textColor of timer_label to the new color value.

Note: When doing integer division in Ruby, the result is always rounded to the nearest full integer. So 4 / 5 would return 0, while 6 / 5 would return 1. To perform float division instead, simply convert the denominator to a float.

Run rake and tap the Start Timer button to see how your effect looks:



Hey — that adds a lot of polish to your app! :] However, update_timer_label is starting to get a little messy and, strictly speaking, it shouldn’t be up to the controller to calculate the text color.

You can make this code a lot neater, and more MVC compliant, by extending the behavior of timer_label through defining a subclass of UILabel.

Create a new file in app/views and name it timer_label.rb using Terminal:

touch app/views/timer_label.rb

Open timer_label.rb and declare a new class TimerLabelas a subclass of UILabel:

class TimerLabel < UILabel

Your goal is to extract all of the logic from MainViewController that updates the timer_label, and instead place it in a custom method in TimerLabel. This makes the controller code much easier to follow, easier to maintain — and easier to test.

Still in app/views/timer_label.rb, add the following code to the class implementation:

def update_for_count(count)
def update_text_for_count(count)
  mins = count / 60
  secs = count % 60
  self.text = "%02d:%02d" % [mins, secs]
def update_color_for_count(count)
  if count > 0
    proportion = count / Time.secsIn25Mins.to_f
    color = UIColor.new_from_two_colors(UIColor.pomo_red_color, UIColor.pomo_green_color, proportion)
    color = UIColor.pomo_grey_color
  self.color = color

This defines a new public method for timer_labelupdate_for_count — which takes the count value from a PomodoroTimer object and calls two private methods: update_text_for_count and update_color_for_count.

This is the exact same update_timer_label code from MainViewController; the only difference is that you set the properties on self, instead of on update_timer_label.

This should help you appreciate how much simpler Ruby code can be, when compared to Objective-C.

Modify the implementation of main_view.rb to use your new TimerLabel class instead of UILabel as shown below:

class MainView < UIView
  # ...
  def timer_label
    @timer_label ||= TimerLabel.alloc.initWithFrame(CGRectZero).tap do |label|
      label.styleId = 'timer_label'
      label.text    = '00:00'
  # ...

Next, update update_timer_label in main_view_controller.rb to use the update_for_count method you defined above:

def update_timer_label

Run rake to build and launch your app; test your app to make sure it works as before. The changes you’ve made shouldn’t have any impact on the appearance or performance of your app, but should make the code easier to maintain in the future!

Updating the Start Timer Button

The title of timer_button changes depending on the state of the button. It would be nice to polish your app a little further and change the background color of the button when it’s selected.

To add this custom behavior, you’ll create a new subclass of UIButton that can indicate a selected state. Can you guess where this new custom view class should go?

If you said “the app/views directory”, you’d be right!

Create the following file using Terminal:

touch app/views/selectable_button.rb

Open selectable_button.rb add the following code:

class SelectableButton < UIButton
  def selected=(bool)
    super.tap do
      self.backgroundColor = bool ? UIColor.pomo_red_color : UIColor.pomo_green_color

In the above code, selected= takes a boolean parameter, calls super's selected= method, then sets backgroundColor to red if bool is true, and to green if it’s false.

Note: You’re calling super here, but there’s no method named selected= defined in UIButton. What’s going on?

Naming setters as set[Something] is a C-style idiom. In Ruby, the preference is to name setters like [something]= instead. RubyMotion cleverly offers support for both idioms and aliases them as well so you can choose to use one or the other as you see fit.

Ruby also offers method aliases in the form of [something]? for methods named is[Something].

Modify app/views/main_view.rb to use your new SelectableButton class instead of UIButton as shown below:

def timer_button
  @timer_button ||= SelectableButton.buttonWithType(UIButtonTypeCustom).tap do |button|
    button.styleId = 'timer_button'
    button.setTitle('Start Timer', forState: UIControlStateNormal)
    button.setTitle("Interrupt!" , forState: UIControlStateSelected)
    button.addTarget(nextResponder, action: 'timer_button_tapped:',
      forControlEvents: UIControlEventTouchUpInside)

Run rake to build and launch your app; tap Start Timer and you’ll see the button background turn to red like so:


Once the timer reaches zero, the button background returns to green.

Alerting the User

Your app is really starting to take shape — but there’s a little more functionality to implement. One of the key principles of the Pomodoro Technique is that you should take a five minute break after your 25 minute block of work is over. You should suggest this to your user so they don’t slip back into their old habits. UIAlertController is the perfect tool for this.

Add the following property method to main_view_controller.rb:

def alert_controller
  @alert_controller ||= UIAlertController.alertControllerWithTitle("Pomodoro Complete!",
    message: "Time to take a short break.",
    preferredStyle: UIAlertControllerStyleAlert).tap do |alert|
      ok_action = UIAlertAction.actionWithTitle("OK",
        handler: nil)

You’ll want to show this alert once the timer has finished counting down to zero, so pomodoro_timer_did_finish would be a perfect place to call this method.

Add the following line to pomodoro_timer_did_finish:

self.presentViewController(alert_controller, animated: TRUE, completion: nil)

Run rake to build and launch your app, then tap Start Timer; wait ten seconds and you should see your new alert controller make an appearance like so:


And that’s it — you’ve completed your first RubyMotion application!

Where to Go From Here?

Hopefully you enjoyed working through this tutorial, and can appreciate the value RubyMotion provides, not just in time saved, but also in the emotional and mental energy you save by working with a friendlier, easier to use syntax.

If you would like to view the completed project, you can download a copy of it here.

To build and launch your RubyMotion app on a physical device, you can use the rake device command which you can learn more about here.

There are loads of great RubyMotion resources online; I’d recommend you check out RubyMotion Dispatch – A weekly newsletter as well as the developer documentation on the RubyMotion website.

If you have any questions or comments, feel free to join the discussion below!

RubyMotion Tutorial for Beginners: Part 2 is a post from: Ray Wenderlich

The post RubyMotion Tutorial for Beginners: Part 2 appeared first on Ray Wenderlich.



Write a comment