ReactiveCocoa & Complex User Interfaces

“ReactiveCocoa”:https://github.com/ReactiveCocoa/ReactiveCocoa is an “FRP”:http://en.wikipedia.org/wiki/Functional_reactive_programming framework written for Objective-C that makes it easy to observe, compose, and transform values. Recently, I had an opportunity to truly leverage the power of ReactiveCocoa when I found myself facing a complex user interface that took a multitude of inputs from the user.

h2. The Problem

The UI we were developing had the following features:
* A table of rows containing values that represent primary and optional secondary values.
* Each row’s primary and secondary values represent user inputs from a corresponding form.
* When the user submits the _composite_ form the associated form’s data must be merged.

Below is a rough sketch of the UI:

ui_mockup

h2. The Toolbox

I tackled the above problem leveraging the following frameworks:

* “RubyMotion”:http://www.rubymotion.com – A toolchain that allows developers to create iOS applications using the Ruby language
* “ReactiveCocoa”:https://github.com/ReactiveCocoa/ReactiveCocoa
* “Formotion”:https://github.com/clayallsopp/formotion/ – A great library for creating iOS forms easily.
* “RubyMotion extensions”:https://github.com/kastiglione/RACSignupDemo-RubyMotion to ReactiveCocoa

h3. ReactiveCocoa’s Objective-C to Ruby Legend

For those of you that are familiar with Objective-C but not Ruby, you may find the legend below helpful.

* rac.property is analogous to RACObserve(self, property)
* rac.property = signal is analogous to RAC(self, property) = signal

h2. The Form

The center piece of the above mockup is the form (plus the user inputs and data returned by the form once it has been submitted). The first thing to do is track the form data as it changes, which is when the user submits the form.

class RACForm
  attr_reader :form, 
              :data_signal

  def initialize(form, primary_key, *secondary_keys)
    @form = form
    @data_signal = RACReplaySubject.replaySubjectWithCapacity RACReplaySubjectUnlimitedCapacity

    self.data_signal.sendNext @form.render

    @form.on_submit do |f|
      self.data_signal.sendNext @form.render
    end
  end
end