Reactive Programming with RxSwift

Darren Sapalo

May 13th, 2016

In a previous article, Building an iPhone app Using Swift, Ryan Loomba showed us how to build iOS apps using Swift, starting from a new project (Building an iPhone app Using Swift Part 1), and how to create lists using a table view and present a map using map view (Building an iPhone app Using Swift Part 2). In this article, we’ll discuss what RxSwift is and how it can be used to improve our Swift code.

The Github repository for Rx.NET defines Reactive Extensions (Rx) as “a library for composing asynchronous and event-based programs using observable sequences and LINQ-style query operators.” Initially, Netflix developed this library to improve the way their API backend handles streams of data, but there were uses for the library even on the frontend to achieve a responsive user interface. The above links provide a better explanation of what the library is and the rationale for why they developed it. This article will focus on staying as simple as possible and explain how Rx can help Swift developers with the problems that they might encounter with regards to mobile development.

Mobile Development and Background Threads

If you’ve worked on mobile apps requiring Internet access, you’ll realize that there are things that should be done on the main thread (UI manipulation and accessing context-related resources) and things that should be done on a background thread (network queries and code that takes some time to perform). This is because you shouldn’t block the main thread with long-running code, such as performing a network query to get some JSON data from a server, or else your user interface will appear to be hanging!

The Non-Rx Approach

For our example, let’s say you need to query some JSON data from a server and display it on the screen. On your AppDelegate class, you could possibly have some queue setup for network requests.

AppDelegate.swift

static var networkQueue = dispatch_queue_create("com.appName.networkQueue", DISPATCH_QUEUE_CONCURRENT)

We normally dispatch code to be run on a different thread by writing the code below:

dispatch_async(AppDelegate.networkQueue) {
    // Query some server to get some json data
}

Let’s say that the best-case scenario will always happen and your network call will be successful. You have Internet access, the server was alive and responded, you have proper authorization to access the data you are requesting, and you successfully retrieve the data. I have enumerated these because I want to emphasize that there are so many things that can go wrong and prevent you from a successful network query.

With the best-case scenario, you have your parsed data and you’re ready to display it on your UILabel. However, you’re currently on a background thread, which means that you should switch back to the main thread to manipulate the UI. This means your code will look something like this:

dispatch_async(AppDelegate.networkQueue) {
  let url = "http://myapi.myserver.com/users"
  let request = NSMutableURLRequest(URL: NSURL(string: url)!)
  
  let task = session.dataTaskWithRequest(request, completionHandler: { data, response, error -> Void in
      let json = try NSJSONSerialization.JSONObjectWithData(data!, options: .MutableLeaves) as? NSDictionary


      dispatch_async(dispatch_get_main_queue()) {
         self.label.text = json.valueForKey("result")
         // Query some server to get some json data
      }
  }
  task.resume()
}

There are two things I want to point out here. Firstly, there are two calls to a global method called “dispatch_async” to run code on a specified queue, and they are nested inside each other. Another thing is that we’re expecting this code to run perfectly at all times; there are no error checking of whether the request was successful, whether data or response was nil, or whether error has some value or not. As mentioned above, there are many things that can go wrong when performing network queries, and your code needs to handle it elegantly.

The RxSwift Approach

With RxSwift, network queries and code that takes some time to perform are converted into Observables, which emit some data with its type specified. Views and controllers subscribe to them as Observers.

Observables

The network query can have three possible states:

  • Currently emitting a new value (onNext), which can occur repeatedly or not at all
  • An has error occurred and the stream has stopped completely (onError)
  • The stream has ended (onComplete)

For instance, the above example of a network query returning a JSON value could be defined as an observable that emits an NSDictionary, because that’s exactly the type of result we’re expecting:

func rxGetUsers() -> Observable<NSDictionary> {
  return Observable.create { observer in 
    let url = "http://myapi.myserver.com/users"
    let request = NSMutableURLRequest(URL: NSURL(string: url)!)
    
    let task = session.dataTaskWithRequest(request, 
	  completionHandler: { data, response, error -> Void in
	    if (error != nil) {
		  // an error occured
	      observer.onError(NSError(domain: "Getting user data", code: 1, userInfo: nil)) 
        } else if (data == nil){
		  // No data response
  	    observer.onError(NSError(domain: "Getting user data", code: 2, userInfo: nil)) 
        }
  	  
	    // other error checking
        let json = try NSJSONSerialization.JSONObjectWithData(data!, options: .MutableLeaves) as? NSDictionary
  
	    if (json == nil) {
		  // No json data found
  	      observer.onError(NSError(domain: "Getting user data", code: 3, userInfo: nil))  
		  return
	    }
  
	      observer.onNext(json)
		  observer.onComplete()
      }
  	  task.resume()
	return NopDisposable.instance
  }
}

With the rxGetUsers function defined above, it is easier to see what the code does: when an error occurs, observer.onError is called and the management of the error is deferred to the observer (a ViewController, for example) instead of the observable (the network query). Only when the error checking for the network is done is the observer.onNext method called, and the stream is finished with the observer.onComplete method call.

Observers

With the network query encapsulated in a single function and returned as an Observable instance, we can proceed to use this query by subscribing an observer (see the subscribe method). The Rx library provides options on what threads the code will run on (observeOn and subscribeOn), a way for you to handle the result or errors with direct access to the ViewController’s properties such as UI references (onNext and onError), a way for you to be informed when the observable stream is finished (onComplete), and a way for you to disregard the results of a network query (via disposing of the subscription variable). The relationship between an Observer that observes an Observable is called a subscription. You might need to do the last one I mentioned if your user suddenly chooses to press home and leaves your app, and you lose access to your context and resources to interact with.

let subscription = rxGetUsers()
   // When finished, return to main thread to update UI
   .observeOn(MainScheduler.instance)
   
   // Perform parallel work on separate thread
   .subscribeOn(ConcurrentDispatchQueueScheduler.init(queue: AppDelegate.networkQueue))

   .subscribe {
 
     // What to do on each emission
     onNext: (dict: NSDictionary) in {
       self.label.text = dict.valueForKey(“result”) as? String
     },

     // What to do when an error occurs
     onError: (error) in {
       print(error)
       // or you could display an alert!
     },

     // What to do when the stream is finished
     onCompleted: {
       print(“Done with the network request!”)
       // or perform another network query here!
     }
   }

Summary

Once you understand the basics of Rx, I am sure that you will come to appreciate its great use of the Observer pattern and move past the annoyance and difficulty in handling network requests that respect the life cycle of a mobile app, or the confusing callback-ception/hell required when multiple queries need to be put together. In the next article, we’ll show the simple usage of operators and data binding provided by Rx.

About the author

Darren Sapalo is a software developer, an advocate for UX, and a student taking up his Master's degree in Computer Science. He enjoyed developing games on his free time when he was twelve. Finally finished with his undergraduate thesis on computer vision, he took up some industry work with Apollo Technologies Inc. developing for both the Android and iOS platforms.