We're hiring!

We're actively seeking designers and developers for our all three of our locations.

ReactiveCocoa & RubyMotion: The Setup

RubyMotion promises to bring the clarity and concise syntax of Ruby to iOS and OS X development. ReactiveCocoa’s aim is to help reduce complexity by deriving state instead of declaring it. Sounds like a great combination, right?

There are some complications. First, the documentation and examples almost all use the RAC and RACAble macros provided by ReactiveCocoa, something we can’t use from RubyMotion. Bummer. And RubyMotion’s bridgesupport doesn’t allow us to call Objective-C methods that take blocks typed as id. Rats. Fortunately these can be overcome, thanks to some helpful folks on the internet, so let’s get started.

1. Install ReactiveCocoa

ReactiveCocoa is available through CocoaPods. Update your RubyMotion project’s Rakefile to include it:

Motion::Project::App.setup do |app|
  ...
  app.pods do
    pod 'ReactiveCocoa'
  end
  ...
end

The next time you build your project, CocoaPods will retrieve and build the ReactiveCocoa library. There are RAC extensions for many popular libraries. I recommend checking out AFNetworking-RACExtensions in particular.

2. Set Up a Shim for RubyMotion

We ran across an example project from Dave Lee. He sets up a convenient shim that solves both of the complications I mentioned in the opening. There are two primary pieces.

On line 84 of IMMViewController.rb a new class method, reduceLatest, is added to the RACSignal class. This method wraps the existing combineLatest to resolve the problem of passing blocks as type id. It works in conjunction with an Objective-C shim and .bridgesupport file that enumerate methods taking blocks of specific arity (1-5 in this case, add more if you need them).

The other piece, starting at line 159 of IMMViewController.rb, is a replacement for the convenient RAC and RACAble macros. Keep in mind that objects passed to the rac method need to be KVO-compliant.

Overall, lines 77 – 219 of IMMViewController.rb and the contents of the vendor/ReactiveMotion make a pretty good shim.

3. Read the Docs

ReactiveCocoa’s paradigms can be challenging to get a good grasp on. Fortunately the team has provided good documentation. Read it. In particular, I’d recommend starting with:

Justin DeWind has a nice collection of resources in his post, ReactiveCocoa: The Future of Cocoa Programming

I’ve also had a great experience diving into ReactiveCocoa’s source code.

Example: Triggering Authentication Using a Command

This is an example of using a command to start the authentication process.

class LoginViewController < UIViewController
  attr_accessor :credentials
  def viewDidLoad
    super
    @credentials = RACSignal.reduceLatest(usernameField.rac_textSignal, passwordField.rac_textSignal) do |user, pass|
      @_credentials = [user, pass]
    end
  end
 
  # Wired up to a button in our storyboard
  def signInClicked
    @command.execute(@_credentials)
  end
end
 
# elsewhere, in a class that has a reference to the
# LoginViewController (@view) and user model (@user)
class LoginManager
  attr_accessor :credentialsAreNotEmpty
 
  ...
 
  # Gets triggered after the view for the view controller above is loaded
  def configure
    @credentialsAreNotEmpty = @view.credentials.map -> (creds) do
      creds.length == 2 and !creds.any? {|c| c.nil? or c.empty? }
    end
 
    @authCommand = RACCommand.commandWithCanExecuteSignal credentialsAreNotEmpty
    @authCommand.allowsConcurrentExecution = false
 
    # The command's `executing` will remain YES until the
    # signal returned by the block completes or errors.
    @authCommand.addSignalBlock ->(credentials) do
      # User#authenticate uses AFNetworking-RACExtensions under the hood and 
      # returns a signal that completes when the auth request is complete.
      @user.authenticate credentials
    end
  end
end

One thing I’d love to find a better way to accomplish is aggregating the command execution with the stream of current credentials. Keeping the added state @_credentials is gross. I tried a few variations of reduceLatest but didn’t get the behavior I was looking for.
 

Jason Porritt (39 Posts)

As a software developer at Atomic Object, Jason has used .NET, RubyMotion, JavaScript, and everything in between to build a wide variety of web a mobile applications.

This entry was posted in Ruby Motion and tagged , , . Bookmark the permalink. Both comments and trackbacks are currently closed.

One Comment

  1. Dave Lee
    Posted June 20, 2013 at 2:47 pm

    Great post, RubyMotion and ReactiveCocoa are both awesome.

    Glad you were able to get some use out of that demo code, but I must point out I was very new to RAC at the time and looking at it now, a bunch of it is not so good.

    I’m putting together the beginnings of an actual gem that uses the good parts of that demo code: https://github.com/kastiglione/rmrac

    The idea is that will be a lower level gem, which can then be built on to do Ruby DSL’s etc.

    Also, for getting rid of the @_credentials state, checkout RACSignal#executeCommand


    @credentials = RACSignal.reduceLatest(usernameField.rac_textSignal, passwordField.rac_textSignal) do |user, pass|
    [user, pass]
    end

    @credentials.executeCommand(@authCommand)