By Casey Liss
RxSwift Primer: Part 1

When I got to my current job, my team was just starting to look into Reactive Programming as the basis for our clean-room rewrite of our main app. I had never really heard of this before, and I was immediately intrigued.

I set out to learn more about reactive programming, and never found one great guide to answer my questions. Over the next several posts, I’ll attempt to provide exactly that.

Today, we’ll begin with the basic questions. What is Reactive Programming? What does it bring to the table? Why should I care?

What is Rx?

I’d summarize reactive programming (Rx) as:

Modeling a system using a series of composable streams of data, and eliminating stored state wherever possible.

That may not be the most pure academic definition, but that’s how I think of it.

The key to Rx is to think of everything as a series of inputs and outputs; to think of as much as possible as being asynchronous. We can model most systems—most especially user interfaces and network requests—as chains of composable streams.

What Does Rx Bring?

  • Stateless programming
    Naturally every application has some state, but Rx tries very hard to eliminate state. Rather than storing state, Rx attempts wherever possible to simply react to a change from the outside world. These changes may be network callbacks or user actions such as tapping a button.

  • Fewer bugs
    By eliminating state, we’re eliminating avenues for bugs to crop up. Bugs are often caused by the stored state of the world not agreeing with the actual state of the world. Say, for example, a cloud-based app where the local copy of user data is not in sync with the server’s. Naturally, Rx can’t eliminate all of this, but it can get rid of much of it.

    Even something as simple as keeping a data model in sync with a UI control can sometimes be buggy. With Rx, there’s often nothing to store, and thus, nothing to keep in sync, which in turn means fewer bugs.

  • Better local reasoning
    Rx brings a bunch of tools to the table; some are the ones we already know. Some are totally new. Once you get to know the tools, it’s easy to reason about what is being done to a stream. For example:

    self.button.rx.tap
      .withLatestFrom(segmentedControl.rx.value)
      .map { selectedIndex in
          return "You've selected #\(selectedIndex)."
      }
      .subscribe { message in
          print(message)
      }
      .addDisposableTo(disposeBag)
    

    In the above, we’re taking a button, and upon it being tapped, start to do work. The first thing we do is read the current value of a UISegmentedControl. Once we have that value, we generate a message which we’re printing to the console.

    We’re composing two streams: the button tap and the segmented control’s value, and then mapping the result into a string. Finally, we use that string to do work.

    All of that is easily reasoned from just looking at the properties and methods called above: tap, withLatestFrom(), value, map, and subscribe. It’s all right there.

  • Improved flexibility and speed
    Since everything in your world is (or should be) exposed as a stream, it’s easy to extend what you’ve already written to add new features, or to react to new requirements. So many otherwise-challenging business requirements become quite simple when it’s just a matter of combining streams.

    Just the other day at work, we had a situation where our product owner wanted us to only enable a button when some search filters were selected. Since the filter values are just exposed as streams, it was as simple as a
    withLatestFrom(filters).map {} to make the button enable/disable as a reaction to the state of the filters. It took no time.

  • Cross-language Equivalency
    Most modern languages have a native implementation of Rx. In the case of my job, we’re using RxSwift for iOS and RxJava for Android. If your “native tongue” isn’t Swift, that’s okay. These same ideas and methods should be available in your platform of choice. Check out the ReactiveX site for details.

    The advantage for us is, if the Android team solves a tough problem, it’s likely that the iOS team can crib their solution, platform differences aside. That’s very powerful, as it leads to us all speaking a common language, even though our actual languages are very different.

Why Should I Care?

Having worked in RxSwift since February, I’ve come to really love it. It’s taken me a long time to un-learn all the ways I’ve traditionally thought about writing code. I’m still struggling, from time to time, to truly understand the right—reactive—way to approach a problem.

For all the reasons enumerated above, I can’t imagine writing code the traditional way anymore. It feels so heavy, and bloated, and just… old.

Rx is a completely different way of thinking, and I’ve yet to meet someone that took to it well. It took me a couple months to really get to the point where I could understand it well, and write it with a modicum of efficacy. Almost a year on, I absolutely love it.


Over the next several posts, I’ll be taking the world’s most boring and simple demo application and converting it from traditional, procedural code to use Rx. I’ll be going over the changes one very small step at a time.

The demo application is a single UIView that has a UIButton and a UILabel. As you tap the button, it tells you how many times you’ve done so in the label.

It’s boring and stupid, but by design: in moving from traditional code to the new hotness, I don’t want any other irrelevant stuff clouding the discussion.

Here’s a visual demo of the app:

Demo app animation

In part two, we’ll start by quickly examining the code, written using vanilla UIKit, and then taking the first step of the conversion to Rx.