Functional Reactive Programming on Mobile: A Rosetta Stone
My name is Nick Tinsley and I'm a software developer on the Mobile team here at Squarespace. We make the apps you love, like Squarespace Note, Squarespace Blog, Squarespace Commerce, as well as our newly launched Squarespace Start. One of the newer programming paradigms we have been enthusiastic to adopt is functional reactive programming. In this blog post, I'm going to talk about ReactiveX, ReactiveCocoa, and MobX.
Functional Reactive Programming: What is it?
Functional Reactive Programming, often known by its initials "FRP," is a programming paradigm that combines functional programming and reactive programming. Like most abstract concepts, it is important to understand the parents if you want to understand the child. The key points are that in functional programming, data is not kept in a global state and changed in steps, but instead passed through a succession of functions. Reactive programming describes a paradigm where data is fed in continuously, and the program reacts to changes in the data as they happen.
The classic example of reactive programming is a spreadsheet program like Microsoft Excel. If you have cells whose values are determined by other cells, a change made to one cell propagates to the other cells and updates all values automatically. There's no polling; it's a push-based system where the other cells react to new events. To better illustrate what I mean, we're going to implement trivial apps for both iOS and Android, using ReactiveCocoa and RxJava. Then we will write an app in React Native that will run on both platforms.
The core concept we're going to cover here is Observable
s (also known as Signal
s), which emit Event
s through a Stream
that we then subscribe
to. These subscriptions allow us to react to the events as they occur.
These events closely model the real world, where programs have to react to ever-changing and entirely unpredictable interactions. The strength of Functional Reactive Programming is that it lets the input drive the execution.
ReactiveCocoa
ReactiveCocoa follows the pattern outlined above. I'm using ReactiveCocoa 4.2.2, which targets Swift 2.2.x. I've found ReactiveCocoa to be most useful in pushing data all across an app, allowing many parts of the app to see a single source of truth and react accordingly.
To set the scene, I built the simplest interface in the world (a UITextField
and a UILabel
) in Interface Builder and linked them up to the default view controller. Now, like our Excel spreadsheet, we want a change in one cell (the UITextField
) to be updated in another cell (the UILabel
). Here's the view controller that makes that happen.
This is a standard Xcode project, with the addition of ReactiveCocoa that I installed using Carthage.
So what's happening here?
- ReactiveCocoa automatically extends our
UITextField
with aSignal
that emits each event. We access the Signal with the method provided by ReactiveCocoa. - We then subscribe to that signal, meaning every time an event is emitted we operate on that event.
- The closure we run casts the event as a
String
(events are passed around asAnyObject
s) and sets that string as the text of our label.
Again, all of this happens automatically as the user enters text. To continue the Excel analogy, it's like every cell has a hidden signal that you can subscribe to by referring to that cell in another cell's formula. This formula then operates on the events, much like our closure here.
Imagine the code it would take to update that label every time the user types a letter! You can imagine this code as not just a simple string replacement, but as something much more robust. Perhaps we want don't want users typing out the word "evil," so if we detect that we'll stop updating. For more fun, we can transform their text to be in the capitalization style of RiFF RAFF, a rapper from Houston who capitalizes every letter except "i." Here's how we do that:
Much better! We are now exploiting the "functional" part of FRP. Using common functions like filter
and map
, we can manipulate the data in interesting ways. We're using relatively trivial transformations here, but you can imagine something like a live Markdown preview based on the same principles.
But the benefits are greater than instant feedback. It also provides an easy way to send data anywhere in the app or beyond! Imagine that we need to send the data the user is typing to our server as they type it. (Facebook and Twitter do this for a few reasons, mostly for data analytics but also for "draft" functionality.) We simply subscribe again with a different closure.
I've added SwiftHTTP, again with Carthage, to avoid boilerplate code.
As you can see, we simply added a function to send along the partial drafts to our server, and subscribed that function to the original signal to send along the untouched values instead of the RiFF RAFF version. Easy! If you want to extend the concept yourself, try this: add a subscription so that a new post request also gets sent to a special endpoint if the text field contains the 🔥 emoji. This will use the filter
function as well as the additional subscription.
These examples are all in the same file, but these signals can be accessed across the entire app using normal visibility patterns, allowing data to be easily pushed anywhere it needs to be.
RxAndroid (+ RxJava + RxBindings)
RxJava allows for many of the same patterns. Let's rewrite our wonderfully reactive iOS app, this time for Android. I've set up the default empty project with a TextView
and an EditText
. Here's the code to constantly update the one label:
This uses RxJava, RxAndroid, and Jake Wharton's excellent RxBindings.
An initial difference you may notice right away is how Java's lack of lambdas makes this more annoying, but Java 8 and the third-party library Retrolambda can alleviate these nitpicks. RxJava also works with Kotlin, the new JVM language from JetBrains, but that's very new and perhaps not worth the headache. Another difference is there are three packages instead of one! Don't worry, the reasons are more political than technical. Looking past that, we see a lot of the same things:
- a text-entry field emitting a string of the characters in some sort of signal (here called an
Observable
) - a subscription to this stream with a function to update the label
Of course, the strength of streams is transformation. Let's filter and map, shall we?
Again, what we see here is almost exactly the same as what we saw in ReactiveCocoa. Working around Java's limitations with the Func1
and Action1
classes is a bit annoying, but that's about the worst one can say about it. In Java's defense, its rigid type system makes the casts unnecessary and the types unambiguous. Even so, the common ancestry is clear. Let's bring in the last example to show just how similar these two platforms are:
I've added Ion by Koushik Dutta, for the same readability reasons as SwiftHTTP above.
By now you can see the clear parallels between RxJava and ReactiveCocoa. These common concepts will carry you well throughout reactive programming in general.
MobX + React Native
Finally, we can see this new paradigm invade JavaScript with the rise of MobX, formerly known as Mobservable. Compared to RxJava and ReactiveCocoa, MobX has a slightly different goal: specifically, MobX seeks to derive the state of the app using functional reactive programming. As a result, instead of signals and streams, it merely tracks things as they change and reflects that in the rest of the state.
Here, we implement parts of that same app in the React Native starter project. We have a Text
object that is changed every time you type in the TextInput
element.
I've added MobX and MobX-React, as well as two Babel plugins to the dev dependencies.1 Testing was done in the Android emulator.
Given the architecture of applications based on React, which are based around this idea of "state," MobX is the perfect complement. React Native is an ambitious, and frankly quite impressive, project to bring React to our world of mobile apps. The future for React Native, MobX, and FRP is very bright.
FRP in an increasingly mobile world
I think the greatest benefit of functional reactive programming is the ability for far-reaching pieces of the app to communicate in an intuitive way. Just like how hundreds of cells in an Excel spreadsheet can be updated at once by changing one cell, any code in the app can react to events emitted from anywhere else in the app. Combined with the effortless propagation, it allows the entire app to react to new events as they occur.
Mobile is a world where the computer is always reacting to something; gone are the days when computer programs were left to run for days after being fed an initial data set with some imperative instructions. Today's smartphones have to react to the constant yet unpredictable stream of changing network conditions, changing hardware availability, changing memory resources, and, of course, user input. In this world, functional reactive programming takes center stage, ushered in by ReactiveCocoa and RxJava.
1: Here is the package.json for my React Native project: