Why ReactiveX Programming Will Improve Code Quality

What is reactive programming?

Reactive programming is about designing your program based on how inputs are received and turned into outputs. This thought process applies whether you’re talking about a user typing a search term in a textbox and you are displaying search results or a scheduled batch job to migrate data from one database to another, both have inputs and outputs.

We find polling for data incredibly annoying in real life, the kids in the back of the car continually asking “Are we there yet? Are we there yet? Are we there yet?” the answer is “No! I will tell you when we get there!”. This is the way we have become accustomed to handling certain types of interactions in our programs already. We do not constantly ask a button if it has been pressed, we give the button a place to tell us when it has been pressed.

You might be thinking isn’t this Event Driven design? Event driven design focuses on user-initiated events, but does not generally address data models being asynchronously changed and how those changes should be shared with a UI. Reactive programming events are triggered by change events both user driven and data driven. So reactive programming gives you a feedback loop for your UI to know when an underlying model has changed and your user should be updated.

Now let’s look at the definition: reactive programming is an asynchronous programming paradigm oriented around data streams and the propagation of change. So what does that actually mean? Well let’s break it down slowly, “asynchronous programming paradigm” so instead of having things happen in a sequence “I went for a walk and then I read a book”, you do them concurrently “I went for a walk and listened to an audio book at the same time”. Hopefully, this concept isn’t foreign to anyone at this stage. Now we get to the “Wut!?” portion: “oriented around data streams and the propagation of change”. What this portion of the definition is referring to is that a program is a dynamic thing that has input and output, if your program doesn’t have input then you might as well replace it with a constant, if it doesn’t have output then what’s the point of ever running it?

Designing around data streams and change propagation

One of the biggest challenges for people beginning to look at reactive programming is the required shift in mindset to thinking about streams of data. This thought process is actually something that most of us are already familiar with though because we have been dealing with callbacks. We don’t poll for changes anymore, we pass a callback function or delegate so that we can be updated when something happens and REACT to it.

“So why are you writing this intro like reactive programming is something different? You just said I’m already doing it.” The difference is in the interface, and for this part I will be referring to the implementation of Rx. Quick aside; I am a fan of Rx because there is a sizable community behind it and there is an implementation that is fairly uniform in most modern programming languages. This correlation between multiple languages actually helps developers from multiple teams discuss how to solve problems using a common API </aside>. Rx creates a generic interface for handling events, Observable streams. Let’s look at a swifty implementation:

Using target action


import UIKit
class ViewController: UIViewController {
    let label: UILabel = UILabel(frame: CGRect(x: 20, y: 150, width: 100, height: 50))
    var counter: Int = 0

    override func viewDidLoad() {
      super.viewDidLoad()
      view = UIView(frame: CGRect(x: 0, y: 0, width: 375, height: 667))
      
      let button: UIButton = UIButton(frame: CGRect(x: 20, y: 50, width: 100, height: 50))
      button.setTitle("Press Me", for: .normal)
      // Tell the button to call updateCount when the touchUpInside event fires
      button.addTarget(self, action: #selector(ViewController.updateCount), for: .touchUpInside)

      label.textColor = UIColor.white
      view.addSubview(button)
      view.addSubview(label)
    }

    func updateCount() {
      counter += 1 // update the counter
      label.text = "Count: \(counter)" // update the label
    }
}

Using Rx


import UIKit
import RxSwift
import RxCocoa
class ViewController: UIViewController {
    let disposeBag = DisposeBag()

    override func viewDidLoad() {
      super.viewDidLoad()
      view = UIView(frame: CGRect(x: 0, y: 0, width: 375, height: 667))

      let button: UIButton = UIButton(frame: CGRect(x: 20, y: 50, width: 100, height: 50))
      button.setTitle("Press Me", for: .normal)

      let label: UILabel = UILabel(frame: CGRect(x: 20, y: 150, width: 100, height: 50))
      label.textColor = UIColor.white

      button.rx.tap
        // keep a running count
        .scan(0){ (currentCount: Int, _) -> Int in
          return currentCount + 1
        // change the type of data in the stream from an Int to a String
        }.map { (updatedCount: Int) -> String in
          return "Count: \(updatedCount)"
        }.subscribe(label.rx.text)
        .addDisposableTo(disposeBag)

     view.addSubview(button)
     view.addSubview(label)
    }
}

This is an example of two different implementations of a simple view that has a button and a label that counts the number of times the button has been pressed.

The first implementation adds a callback with addTarget(_:,action:,for:) and in the implementation of updateCount() we update the counter and display the new value in the label. Even though this is a small program you already have symptoms of larger problems that go along with traditional event based target callbacks. The culprits? The variables counter and label. We are forced to have instance level properties and counter is only ever used in one place! (Yes, static function variables are a thing but they don’t exist in Swift and the work around is distracting.) The other property, label, is technically used in two places. It is initialized and added to its superview in viewDidLoad(), the text is then set in updateCount(), however as we see in the Rx example you don’t need to separate these things.

Let’s look at the second implementation and try to translate it into English. The chain of calls that starts with button.rx.tap and ends with addDisposableTo(disposeBag) translates as so: Listen to the taps on this button (button.rx.tap), when the button is tapped start with the value of 0 and run the closure that takes the previous value and add 1 to it (scan(0){…}), pass the new value along and save the new value for the next tap, next transform in Int stream type returned from the previous value update into a String (map{…}), now register the label text as the listener to this stream (subscribe(labe.rx.text)) and finally add this stream to the bag of things to throw away when the view controller is dismissed (addDisposableTo(disposeBag)).

This example shows the ability to take an input stream and transform it into what is needed for an output stream. There is no need for instance level variables so there is less of a chance that a teammate will misunderstand the purpose of the variable and use it where they shouldn’t. The local reasoning is also improved because you have one chain of functions for the stream going from input to output.

Observable streams

Ok so observable streams are streams of events or a series of events if that’s easier to think about. There are three types of events that an observable stream can provide onNext, onCompleted(or onDone), and onError. This makes sense if you think about a series of events taking place. Say you are at an all you can eat wing restaurant there are really three events you care about:

onNext – They bring you another basket of wings to devour.
onCompleted – Sorry we aren’t able to serve wings anymore because we ran out.
onError – Everybody run! The kitchen is on fire!

All observable streams have the same three event types that you as the consumer might need to think about. You can stop consuming the events (you get full and can’t eat any more delicious wings) at any point but that does not change the possible outcomes of the observable.

The call to the subscribe function adds on Observer to the Observable stream. An observer is exactly what it sounds like, something that observes an observable stream. An observer knows what to do with the different event types for an observable stream. In the code above I set the observer to the label text, but I could just have easily used a set of closure’s for each event type if the situation required it.

So why should I care?

Reactive programming will push some of the state management you generally have to deal with down into a library that is heavily tested and shared. In the simple examples shared here the management of state was not addressed, but a follow-up blog will show this powerful feature of reactive frameworks. If you choose the Rx framework you will get the benefit mentioned above with being able to collaborate with colleagues who use different languages to help each other create the best solutions possible.

Reactive programming will hopefully expose you to some common operators of functional programming. Starting you down that road will have you asking many more questions about how you design your code.

Next time we will go over more about observable streams, operators for mutating and combining streams as well as a more concrete example that will show how reactive can simplify a code base.