RxSwift on iOS - Where to start the adventure

Reactive Extensions (Rx for short) is a collection of libraries that became popular on multiple platforms in the recent years. It introduces a new paradigm that enables writing concise and easily readable code, facilitate powerful architectures like MVVM and simplifies such complex problems as handling errors across multiple threads. The benefits are quite big, but one cannot forget about the downsides. There may be only one downside, but it is quite a big one: it's hard to learn.

This is undeniably a major setback for Rx, and also the reason why I decided to write about the topic. I already published a similar article about the Android counterpart of the library (I do recommend you to check it out to see how similar is the Java version of Rx), as one of the awesomeness of Reactive Extensions is that if you learn it, you can apply it easily on multiple platforms. This can be a great advantage for developers like me who develop for both iOS and Android. And for bigger teams an Rx-based architecture can be a common ground for developers on the different platforms.

For this post, I will go through the basics of Rx (and more specifically RxSwift), while also recommend some reading materials at the end to help you get started. I primarily meant this article for developers who only started to learn about Rx.

Availability

You might ask if Reactive Extensions on iOS is limited to Swift. After all, RxSwift has it in its name. The answer is twofold: the official implementation of Reactive Extensions is RxSwift, which is Swift-only, but there is an alternative library called ReactiveCococa. It was first developed in Objective-C and still available like that. Unfortunately, it also switched to Swift and only actively developed using that language.

There are a couple of differences between RxSwift and ReacitveCocoa, but for now, I will only write about RxSwift as it aligns with the rest of the Rx implementations. If you are only interested in iOS development, you may want to check out the ReactiveCocoa project, it has an API that is a bit more aligned with Cocoa APIs and maybe it is easier to grasp in some places, while a bit more restricted in my opinion.

The RxSwift library is also actively developed and has a vibrant developer community. It is still a bit low on documentation, even compared to other iterations of Rx, but it is constantly improving in that regard too.

Basics of Rx

The four fundamental concepts that Rx builds upon are:

  • Observable Pattern
  • Iterator Pattern
  • Reactive Programming
  • Functional Programming

To get a hold on Reactive Extensions, you have to understand these at least fundamentally.

Observable Pattern is all about subscribing to the changes of an object. The Observer is the object that is interested in the changes of another object: the Subject (sometimes called Observable). So the Observer subscribes to the change events and is notified when they happen (as long as it is subscribed), so they can somehow handle these events. It is important to remember that these constructs are called a bit differently in Rx: the Observable pattern's Subject is called Observable in Rx, and the Observer is called Subscriber. In Rx, the flow of data is abstracted as Observable sequences that are composable in the same fundamental way.

Iterator Pattern is so common in programming that it hardly needs explanation. Basically it is used every time when the code iterates through some kind of collection. It's Next event raised every time there is another element in that list. Rx also has similar events when a new element is available.

Reactive Programming is about asynchronous data streams, which can be listened to, and the listener can react accordingly. It is basically an evolution of the Observable Pattern.

Functional Programming is not as simple as the previous too, but luckily it is not really required to fully understand it to learn Rx. Rx is not really functional programming, but it uses fundamental aspects of it. As in Functional Programming, Rx uses functions (they are called Operators here) that can be chained together to apply different transformations on data. Every Operator's (function's) output can be the input of the next Operator in the chain. This is the fundamental behavior of Rx, but unlike real Functional Programming, Rx's functions can have side effects, although Rx operators are stateless by default. Chaining Rx Operators is the fundamental way in which Observable sequences can be composed.

To understand Rx, you have to think about everything as sequences. Almost every data-based program flow can be viewed as a sequence of data and some transformations on it. For example here is a finite sequence of numbers:

1-2-3-4-5-6  

You can simply create an Observable sequence like this in RxSwift:

[1,2,3,4,5,6].toObservable()

Let's say that you are interested in these numbers as they are emitted as you would like to act upon them. All you need is a Subscriber. A Subscriber has three events: Next,Completed and Error. Next means there is a new data coming through, and the other two signals the end of the sequence. Every finite sequence terminates in either one of these, Completed means that it terminated normally, and Error means there was some kind of error somewhere. It is important that every error and exception results in an Error event and when you chain operators you can be sure that as soon as an exception happens, it will raise the Error event and subsequent Operators won't execute with the faulty data (This is a huge win).

So back to the simple example, our Subscriber could look like this (notice the three closures that handle the three events I mentioned):

[1,2,3,4,5,6].toObservable()
    .subscribe(
        onNext: { (intValue) -> Void in
            print("Pumped out the next Int")
        },
        onError: { (error) -> Void in
            print("Error occured")
        },
        onCompleted: { () -> Void in
            print("The sequence is finished")
        }
    ) { () -> Void in
        print("We disposed this subscription")
      }

When a Subscriber subscribes to an Observable, it creates a Subscription. The latter is not a concrete type in Rx, but it represents the connection between the Observable and the Subscriber. In fact, the subscribe* methods return an object called Disposable which needs to be disposed to avoid potential memory leaks. This why there is a final closure in the example where the Subscriber is notified about this happening. There is a dispose operator, but you rarely should call that manually, as there are more sophisticated solutions (like DiposeBags), but they are out of the scope of this post.

If you are only interested in the next events, you can subscribe to only that that too (although in most cases it is advisable to handle errors in some form):

[1,2,3,4,5,6]
  .toObservable()
  .subscribeNext {
    print($0)
  }

Ok, this may be pretty basic until now, but don't feel bad if you need to reread the previous sections, as it is wise to have a solid start.

Operators

What about those data transformations I wrote about? Staying with our basic sequence of numbers for now, let's say we want to print the even numbers. It can be achieved like this:

[1,2,3,4,5,6]
  .toObservable()
  .filter { $0 % 2 == 0 }
  .subscribeNext {
    print($0)
  }
// Prints 2, 4 and 6

Here we used one of the Operators, namely filter to - well - filter out even numbers. You can do all sorts of transformations with Operators like mapping elements to others with the Map operator (eg. multiply by two):

[1,2,3,4,5,6]
  .toObservable()
  .map { $0 * 2 }
  .subscribeNext {
    print($0)
  }
// Prints 2, 4, 6, 8, 10 and then 12

Or take only some (eg. first two) of the elements with the take operator:

[1,2,3,4,5,6]
  .toObservable()
  .take(2)
  .subscribeNext {
    print($0)
  }
// Prints only 1 and 2

Or even combine the latest emitted elements from multiple Observables with the combineLatest operator (in this basic example, the two obserbales emmit in a serial way, so that is why the first element in the first observable is never combined with the elements in the second observable).

Observable.combineLatest([1,3].toObservable(), [2,4].toObservable()) { "\($0)\($1)" }  
  .subscribeNext {
    print($0)
  }
// Prints 32, and 34
// as the first observable emmits 1 and 3
// and only then the second observable emmits 2 and 4

To help you better understand these operators, the Reactive Extensions documentation uses marble diagrams, which visualize sequences and the transformations that operators make on them. There are even interactive marble diagrams (you can find these here, but they are also being merged into the page about Operators in the docs).

An example marble diagram about the Combine Latest Operator. This is different from the previous simple example of the same operator as these events are emmitted asynchronously. Think of them like strings entered in an input field on the UI

If you are familiar with Functional programming with Swift, you know that some basic operators (like map and filter) are come with Swift by default. However, there are a lot more Operators in Rx and by using them, you can solve quite complex problems with ease. As Apple's old saying goes for apps, it can be stated with Rx: There's an Operator for that!.

Asynchronous calls and Thread handling

On iOS, there are quite a few ways already to handle asynchronous calls:

  • Delegate methods
  • Callback blocks
  • NSNotifications
  • Key-Value Observing
  • Dispatch Async
  • etc...

In fact, there may be too many of them. I already mentioned that Rx can simplify handling asynchronous calls, and introduced Rx operators. Rx is synchronous by default, but two of the Operators, namely observeOn and subscribeOn define where "threads" are switched. Well, not really threads but Schedulers.

What are Schedulers? - you might ask. They are the thread abstractions. They do what their name suggest: schedule the tasks they are given. They can be serial or concurrent and a single thread or a whole threadpool can be behind them. RxSwift has several built-in schedulers, but you can define new ones as well (like this).

Ok, back to the two operators: subscribeOn is used to describe the Scheduler where we subscribe to the Observable and ObserveOn is used to describe the scheduler where we observe the result. So subscribeOn defines where the subscription happens, no matter where you use it in the chain. observeOn on the other hand can be chained together with other operators, and by chaining several observeOn operators together, you can define a new scheduler that all the following Operators will run on (below the observeOn call). Let's look at some examples.

let bgSched1 = SerialDispatchQueueScheduler(queue: dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), internalSerialQueueName: "")  
Observable.just(1)  
    .doOn {
        print("subscribe on \(NSThread.currentThread())")
    }
    .subscribeOn(bgSched1)
    .observeOn(MainScheduler.instance)
    .subscribeNext {
        print("map on \(NSThread.currentThread())")
        print($0)
}

In this example where we state that the subscription should happen on a background scheduler, but the result should arrive in the Main scheduler. The Main scheduler is used everywhere below the observeOn call, until that the scheduler used for subscribing (defined by the subscribeOn) is used as there are no other observeOn call in the chain. The subscribeOn call could be put at any place in the chain it would still only define the scheduler used from the point to subscription to the point of the first observeOn call. You should only use one subscribeOn per subscription as only the first in the chain will have any effect (even if you add more). The doOn Operator is only used here to print out which scheduler is used when subscribing. It is not hard to imagine the source Observable as a resource intensive and/or blocking call (eg. a long running operation, a network call, or a database retrieval), and we want to display the result on the UI, hence we have to observe it on the Main scheduler (thread).

By the way, the just method of the Observable class is another Operator, which is used to create an Observable from a single object.

Here is a more complex example where we chain a map operator and multiple observeOn operators to switch to different schedulers:

let bgSched1 = SerialDispatchQueueScheduler(queue: dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), internalSerialQueueName: "")  
let bgSched2 = SerialDispatchQueueScheduler(queue: dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), internalSerialQueueName: "")  
Observable.just(1)  
    .doOn {
        print("subscribe on \(NSThread.currentThread())")
    }
    .observeOn(bgSched1)
    .map {
        print("map on \(NSThread.currentThread())")
        $0
    }
    .subscribeOn(bgSched2)
    .observeOn(MainScheduler.instance)
    .subscribeNext {
        print("observe on \(NSThread.currentThread())")
}

Again, the doOn operator is only used to print out which Scheduler is used when subscribing.

This can also be visualized by the below Marble diagram:
A marble diagram describing a similar situation to the example code above it. The color of the lines represent which scheduler it runs on.

Relation to built-in functional aspects of Swift

You may be a bit doubtful why you would need RxSwift as Swift has functional aspects built-in. It has operators like map and even SequenceType which seems similar to Rx Observables. The main difference between these two is that Rx Observables can receive events asynchronously, and Rx has way more Operators than Swift has by default.

Use cases for Rx

Reactive Extensions has a lot of use cases. The great thing is that you can start using it in different parts of a project like in the network layer or the UI layer, so you don't have to go all-in on it.

While using Rx can simplify complex parts of the code, it is not wise to use it for everything, so for this reason I would like to highlight some places where it could really make a difference. It is really suited for bigger projects, especially when it targets multiple platforms. The reason for this is it has a quite steep learning curve, but if you learn it once, you can apply your knowledge on different platforms easily.

Network layer

The network layer of an application is a great candidate for introducing Rx to the project. Big players in the industry, like AirBnb already did this in their flagship product. The typical challenges of network handling can be solved in a clean and straightforward way by taking advantage of some Rx Operators.

It is quite common for mobile applications that multiple network request need to be executed after each other. With composition and chaining of Rx Operators, these problems can be solved easily. Timeouts and exponential backoffs can be implemented in a breeze. And the best part is that any error/exception happening during these calls will be forwarded to the Subscriber at the very end (and we now that errors are not uncommon with network calls).

UI layer (Data Binding)

In more complex iOS application, managing the UI of an application is no easy feat. The dreaded Massive View Controller (when a ViewController's code become too long and overly complex) appears in many articles and talks about iOS development and while there are several other effective way of avoiding it, Data Binding is an outstanding candidate.

Data Binding is when you connect properties from the code to UI elements and functions to UI events in a way that they are automatically synchronized when a change happens in one of them. In many other platforms (like .NET and Android) Data Binding has built-in support, but on iOS there is none of that. Well, with Rx you can enable Data Binding which means you can use the Model-View-ViewModel (MVVM for short) pattern in your UI layer.

MVVM is a pattern derived from MVC (Model-View-Controller) pattern that iOS developers are quite familiar with, and it really is an evolution of it. Instead of a Controller object, which conducts the View and manages the model, MVVM introduces a so-called ViewModel which is a representation of the Model, made especially for the View. With Data Binding, the UI elements from the View can be bound to the properties of the ViewModel and be updated when changes occur.

RxSwift has a companion library called RxCocoa which contains Rx-ifyed extensions for common Cocoa UI elements, so important aspects - like the text of a TextField - can be converted to an Observable and Subscribers in the ViewModel can subscribe to it. It works on the other way around also, thus enabling Data Binding.

Domain layer

The domain layer of an application - where the business logic resides - also can also be a great place to use Rx. It is especially useful when you have complex domain layer and if you would like to do all of that on a separate thread from the UI thread.

I already introduced how Rx can be used for asynchronous operations. By making the different sources of the data (in the Data layer) Rx Observables and consumers of it (in the View layer) Subscribers, you can easily control the flow of data and which thread is used where.

Resource intensive operations like network calls and database operations can happen on a background thread and the result can be showed on the UI, using the Main thread (the code for this can be something very similar to the examples in the Asynchronous calls and Thread handling section of this post). The business logic itself can be implemented with Operators, switching threads where necessary and even combine data from multiple sources.

Conclusions

I highly encourage you - the Reader - to explore Rx more. I will also continue to learn it and write more articles about it for both Swift (iOS) and Java (Android). Until that, I collected some other articles and talks about the subject that you can read and watch.

Learning Rx is not a simple task, but it has many advantages that make it worth doing. If you would like to learn Rx, I encourage you to read some of the resources and then start coding as soon as you grasped the basics. After a while, things will start to click as hands-on coding experience is the best way to understand it. If you want to use Rx in a project but you or your colleagues does not have experience with Rx, introduce it selectively first (eg. in the Network layer) and then work from there if it serves you well.

Extra: Try out RxSwift in Playgrounds

RxSwift is available via CocoaPods but creating a new XCode project just to try some basic examples out can be a bit of a hassle. Luckily, there is a great tool that enables you to try out any Pod in XCode Playgrounds. You can find it here and if you install it, all you have to do to create an Rx-enabled pod in the current folder is to type the following to the Terminal:

pod playgrounds RxSwift,RxCocoa  

It is a great way to experiment with Rx, in fact, you can start immediately by copying the examples in this post to an Rx-enabled Playground (I highly recommend it).

Further reading

Conal Elliot's answer on What's Functional Reactive Programming
https://stackoverflow.com/questions/1028250/what-is-functional-reactive-programming

Reactive Extensions Documentation
http://reactivex.io/

RxSwift Documentation
https://github.com/ReactiveX/RxSwift/tree/master/Documentation

RxSwift Documentation - Getting Started
https://github.com/ReactiveX/RxSwift/blob/master/Documentation/GettingStarted.md

RxSwift Documentation - Examples
https://github.com/ReactiveX/RxSwift/blob/master/Documentation/Examples.md

RxSwift Community Projects (lots of examples)
http://community.rxswift.org/

Interactive Marble Diagrams (Operator visualizations)
http://rxmarbles.com/

RxSwift for Dummies series (ongoing)
http://swiftpearls.com/RxSwift-for-dummies-1-Observables.html
http://swiftpearls.com/RxSwift-for-dummies-2-Operators.html

RxSwift by Examples series
http://www.thedroidsonroids.com/blog/ios/rxswift-by-examples-1-the-basics/
http://www.thedroidsonroids.com/blog/ios/rxswift-by-examples-2-observable-and-the-bind/
http://www.thedroidsonroids.com/blog/ios/rxswift-examples-3-networking/
http://www.thedroidsonroids.com/blog/ios/rxswift-examples-4-multithreading/

RxSwiftExamples (the Github repo accompanying the above article series)
https://github.com/DroidsOnRoids/RxSwiftExamples

Functional Reactive Programming with RxSwift (an informative talk given by Max Alexander)
https://realm.io/news/slug-max-alexander-functional-reactive-rxswift/

Reactive Programming with RxSwift (a fairly detailed talk from AltConf 2016 by Scott Gardner)
https://realm.io/news/altconf-scott-gardner-reactive-programming-with-rxswift/

Make UITableView more Reactive with RxSwift (a great tutorial by Yannick Loriot)
http://yannickloriot.com/2016/01/make-uitableview-reactive-with-rxswift/

ReactiveCocoa vs. RxSwift on Raywenderlich.com (with a good section about the basics of Reactive programming)
https://www.raywenderlich.com/126522/reactivecocoa-vs-rxswift

ReactiveCocoa vs. RxSwift (a good summarizing answer on Stack Overflow)
http://stackoverflow.com/questions/32542846/reactivecocoa-vs-rxswift-pros-and-cons

Frp iOS Learning Resources (lots of articles and tutorials)
https://gist.github.com/JaviLorbada/4a7bd6129275ebefd5a6