Rx at Heaps

Jens Grud
Jens Grud
Nov 8 · 5 min read

February 2017 we decided to take the reactive programming wagon for a test drive on our native iOS and Android applications. The Rx community already outlines the Why’s nicely but our main goal at Heaps was to ease maintainability and testing by making the code more reusable, less stateful and in general more understandable and concise. The following outlines a few concrete examples of that.

Full disclosure and spoiler alert; opinions on the subject will vary but we were pretty exited with what we learned and now biased towards Rx.

Compositional disposal

Let us start out easy with the signup flow. I am being a little verbose here for the sake of example, but overall this is what we are trying to accomplish:

  • Login with OAuth provider
  • Create or re-create user
  • Authenticate user
  • Retry with exponential backoff
  • Get user data

Before Rx this is what that looked like in pseudo Swift code:

User signup before Rx

Totally not unreasonable and fairly alright to understand and maintain — but not pretty to look at and definitely not easy to fail gracefully or retry if one of the API calls fail.

After sprinkling with a little Rx dust, this is where we end up:

User signup after Rx

That’s it — we are achieving exactly the same in the two examples.

Note the observeOn(MainScheduler.instance) part — as we have the API returning on a background thread we want to make sure the binding happens on the UI/main thread.

Overall much more readable and concise by way of:

  • No need for nesting callbacks
  • Possibility of handling all failures in one single place
  • Support for retries without keeping count in additional variable

An other good use would be aggregating network requests with the zip operator in case you have two or more request and want to wait processing until all of them has completed.

A → B → A Passing information between entities

How about passing information between classes and entities? Imagine the pretty common scenario where we have view controller A and want change content here based on an option selected in a presented view controller B:

  • View controller A: Show list of all locations
  • View controller A: Push view controller B with a list of filtering options
  • View controller B: Select option and dismiss
  • View controller A: Refresh list based on selection in view controller B

A → B → A:

Browsing venues based on current or pre-selected location

It is a pretty common scenario but surprisingly tricky to implement in a clean and concise way with the standard delegates-notifications-and-closures-toolbox.

Until..

Introducing…

Wait for it…

🥁

Rx!

Observables everywhere

Think of view controller B as just another observable — it does not really matter if an observable comes from an API call or an entire view controller; it’s all just another observable.

Let’s elaborate on our location filtering example and introduce:

  • VenuesViewController for presenting the venues (A).
  • LocationPickerViewController for picking the location by which we want to sort the location (B).

We’ll start with the reactive extension to LocationPickerViewController and work our way backwards.

The Reactive Struct definition below puts it a little more concise but in layman terms you can extend any struct with your own custom binding using the Reactive<Base> .

/// A proxy which hosts reactive extensions of `Base`.

This is what that looks like for our LocationPickerViewController in question:

We just extended the view controller with a done property that will be “triggered” every time the user select an item in the table view allowing us to emit the selected location to whoever is subscribing to it. After emitting the location the .do(onNext:.. part will be triggered and in our case pop to the parent view controller.

Armed with that, looking at the VenuesViewController we now have:

And this is where we tie it all together:

  • Line 8: The LocationPickerViewController is pushed when user clicks the pickLocationButton, but since we are subscribing to the done extension we just outlined above, nothing will happen until we get to line 8 when the user actually makes a selection.
  • Line 15: To make sure we start showing some content before the user has selected a location herself, we introduce the startWith operator in line 15. This will emit a single pre-defined event and make sure we are presenting user with venues for the default filtering when presenning the view controller the first time.

A → B → A. Simple.

Do not worry about the `[weak self]` parts for now — it is related to how objects are managed and retained but will be the subject of a blog post in it self.

And the list goes on — but …

In fact, there is lots of ground to cover and areas to get into with compelling arguments for considering going Rx — ranging from managing state (another area you just have to worry about, either explicitly by making decisions and trade-offs or implicitly by fixing weird, hard-to-reproduce bugs), unit testing to memory management — but I’ll wrap it up for now with a final selling point:

… it is not all or nothing

While the list of operators is huge — overwhelming maybe even — it is totally possible to start out easy with the 5–10 most common and confine them to a single view model or view controller until you are comfortable they will cover your use cases and be a nice addition to your tool box. That way you can ease into it or revert it just as quickly as it got there. In any case, I am sure you will have learned something valuable about pros and cons of you current implementation in the process.

Thanks for reading so far! Be sure to reach out if you have any questions, comments or concerns.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade