Tryswift marin todorov facebook

I create iOS apps - is RxSwift for me?

It is difficult to make the jump from map and filter to presenting view controllers or search bars that need to call an API on the web and populate a table view. To be honest, at first it seems almost like functional or reactive programming has nothing to do with UIKit or NSURLSession.

In this try! Swift NYC talk, Marin Todorov shows how RxSwift (an async, event based framework) applies in few every day situations of the life of an iOS developer. If you like major pains being solved for you transparently at the price of a single dependence, this talk is for you.


Introduction (00:00)

The Reactive Extensions, Rx, is a library for composing asynchronous and event-based programs using observable sequences and link style query operators. Using Rx, developers represent asynchronous data streams.

(Marin, shredding his notes): I have read the theory many times, but that has never helped me understand what it does for me or in my daily work of developing iOS apps. With the risk of being completely and totally wrong, today I am just going to tell you how I see Rx working in iOS apps.

The Reactive Side of Rx (01:08)

First I am going to talk about the reactive side of Rx, or the ability of having your data pushed to you (instead of you pulling for changes).

Array < String > (01:22)

An array, a collection of strings (if it is an array of strings, of course), is handy because you can use a for loop (e.g., to do some work with each of the items, or you can use the more swifty approach by using the forEach method). But it is all the same: you provide a closure, some block of work, and it gets executed on each item separately, one after the other.

The problem with the arrays is that you do this work in a frozen moment in time. You cannot account for, for example, elements that are being added to your array later. Why would I want to work with things that are not there yet?

Let’s have a look at a table view controller. You have an array of things and you show them on screen in table view. That is great. However, as soon as the user taps on the plus button and adds a new item (i.e. a fourth item to your array), you have a problem, because your data model has four items, and your UI has three.

Instead of focusing on your business logic and handling your data, you have to think about notifications, synchronizing your model and UI, etc. It would be handy if there was a way to asynchronously work with older versions of your data, and not just the one in this very moment.

Get more development news like this

Observable < Array < String > > (03:22)

Rx wraps your data (in this case, an array) into an observable class. This observable class, simplistically, extends your data through time. It takes your data and adds a “time dimension.”

If we go back to our array example, the observable will define this block of data you want to do with each of the elements, i.e., printing them. It will get executed over all the initial data that you have (the three items that are in this array), but then, when you add a fourth one, this block will fire again. When you add a fifth element to this array, it will again execute. You move from defining how and when things should be happening, and you move (mentally) to defining behaviors. You always would have some data, and you would want to do something with it any time it changes or any time some event happens.

Things get very linear. This is one of the greatest things that Rx does (from my point of view): things get very simple. Instead of thinking: what do I have now? What will I have in the future? What did I have in the past? You only would define a simple behavior. In our example, I have a list of items, I want to see them on screen, in table view. That is it. Let RxSwift do the work every time that you add a new element. It is a very straight line from your data to your UI, in this case.

Observables can do great things. For example, if you have a text field, it is a very similar situation (even though it is a totally different company we are talking about). Your event, in this case, would be the user typed in a character, and the behavior “show this text in a label, or do something with this text and update the UI.” You define this behavior once and let Rx take care of every time the user types in the new character, just reapply this behavior for you. Your UI can never be out of sync with your data model.

Handy, simple, linear.

For a more esoteric example, let’s define a behavior: “the user is scrolling through a table view, and every time they scroll to the bottom of that table view, do a behavior.” This behavior in this case could be: “let’s load 20 more items from the server, and append them to the list that we already have onscreen” (simple, linear behavior). When the user does that, they will have more items when they scroll again to the bottom (and they reach the bottom of these new items being appended), this exact same behavior will get executed for you automatically (you will have even more items). It is a very simple way that you start thinking about your code.

The functional side of Rx (06:30)

Let’s have a look at the functional side of these, presumably, great observables.

In our three different examples, we had an observable type for each of them. For the text field, it has text in it, we used an Observable<String>. If you work with text, an observable string is if you want to have the string in all of its future versions. You wrap your data, the text, into an observable. In our table view controller example, that was an array of strings, Observable<Array<...>>. In our scrolling example, we did not have data; it is an Observable<Void> - we are just interested in the event that the user reached the bottom.

In all of these examples you are working with the same class. This allows you to use the same set of methods on this class (in all possible situations!). You can move away from the data type (i.e., an array, a string, a number or anything else), and you can think of defining the behavior in whole workflows, based on what you can do with observables. Your logic can be easily transferred between projects by copying and pasting files, or of course, extracting those into frameworks, and you can focus on the specific datatype you want to work with in your specific project.

Let’s have a look at a quick classic example that every first-time Rx presenter has to go through: an application that allows you to enter a query in a text field, and search GitHub for a matching repo.

Let’s have a look at how we would build this using those, presumably, great observables. There is my text field on the screen. It gives me Observable<String>; it gives me a string every time that the user types a new character in there. There is a method on the observable class called “filter” that allows me to maybe discard some of those values. It filters out things that I do not like - e.g., I do not like any search queries less than three characters, because they will produce too may irrelevant results. It is a method on the observable. The good thing about it is it also returns an observable. I can get the result of filter and call another method of the observable class on the result of that method. I can chain them, one after the other (which is great): I am going to call it debounce.

Debounce is a method on the observable that allows me to detect too many events close together, and take the last one. If the user knows part of the name of the repo and types that quickly, I do not want to fire a network request for each of the characters. I want to wait a bit, and take the last one. I am chaining those, because I can call them one after the other.

I am going to call map on my observable. Map allows me to convert the datatype that I am wrapping into something else. In this case, it is simple: I am taking a string (the search query), and I build a URL, and I build a map NSURLRequest(...). For each character that I type that already matches all of the previous, I would have a request ready to be fired off to the network.

FlatMap will allow me to make a network request, wait until it completes and gives back the result. Then I can continue working. That will give me the NSData back, and I can again use map on that to convert the NSData, by using NSJSONSerialization to Array<AnyObject>. Then, one more time, map to convert those any objects to Repo(...).

This is the workflow of my application (see slide 14 above), from a keystroke in the text field through the validation of that input, the networking, the conversion of data, until I have a list of repos that I can bind to a table view, maybe store on disk on Realm (or in any other way). It is good because it is very linear: one thing falls from the other, and I never get confused in what sequence these things happen. I do not have a data source protocol. I do not have a delegate method of protocol. Things are happening sequentially, one after the other, and it is very linear.

We are going to start with my outlet to my query text field, and Rx will be the observable that gives me the string.

query.rx_text

  .filter {string in
      return string.characters.count > 3
  }

  .debounce(0.51, scheduler: MainScheduler.instance)

  .map {string in
      let apiURL = NSURL(string: "https://api.github.com/q?=" + string)!
      return NSURLRequest(URL: apiURL)
  }

  .flatMapLatest { request in
      return NSURLSession.sharedSession().rx_data(request)
  }

  .map { data —> Array<AnyObject> in
      let json = try NSJSONSerialization.JSONObjectWithData(data, options: [])
      return json as! Array<AnyObject>
  }

  .map {object in
      return Repo(object: object)
  }

  .bindTo(tableView.rx_itemsWithCellIdentifier("Cell"))

We call filter and we supply the code to filter out the bad strings that we do not want. Then we are going to call debounce, and mention in what time interval we want to group those events together. We are going to call our map, taking the string and building a URL, and NS Serial request out of it. And then we are going to call FlatMap, use an URL session to do our request and get data back. Another map to transfer the NSData via NSJASONSerialization (as usual). Another map to take each of those AnyObject and convert them to a repo object that I have defined. In the very last call of this chain, I am going to call .bindTo and say that this list of repos should be bound to my table view, using a CellIdentifier to do all the work.

(As you can see, this is bad code, simple and short. However, we can consider a few aspects of it): you all had 15 minutes of experience with Rx (maybe some have more). You can already see what this code does. It is very sequential, and you can never get confused about the order in which these are going to be executed. If you are a new member on the team, you will understand what this code does. If I have a look at this code six months from now, I am going to understand what it does.

The best part is that I am not calling methods on an object that do not have anything to do with each other, but I am calling them in a chain. Each of them expects some input and produces some other output. And they are holding hands tight together, once you get it to compile, which is the trick; it is very difficult to introduce any variance into the code. Because once the compiler tells you “this is okay,” then things are running smoothly.

F&R App Architecture (14:44)

How any of this is relevant to my iOS applications?

Let’s take a look at a more elaborate example of presenting a view controller (inherently non-reactive, non-functional, and non-nothing). We could have a navigation controller with a list of repos, in a table view, and a model view controller which allows the user add the repo manually. They enter all the data via the keyboard, press done, and it should go in.

The normal way would be to implement a delegate protocol. You will define a protocol with some methods that one of the controllers is going to call on the other one. If you need those to talk back, that gets a little more complicated… but let’s forget all about this.

We have a universal class, a datatype that allows us to do communication universally between all classes. This is the observable. If your Add Repo view controller has an observable that would emit a value whenever the user is done… that would make things very simple.

Let’s have a look at the source code. Let’s start with the bar item (the + button on the top right). In Rx, tap is an observable that, basically, fires every time that the user taps on the + button.

addBarItem.rx_tap

    .debounce(0.5, scheduler: MainScheduler.instance)

    .flatMapFirst {[weak self] _ —> Observable<Repo> in
        let addVC = AddRepoViewController()
        self?.presentViewController(addVC, animated: true, completion: nil)
        return addVC.newRepo.asObservable()
    }

    .doOn {_ in
        self.dismissViewControllerAnimated(true, completion: nil)
    }

    .subscribeNext {repo in
        repos.value.append(repo)
    }

repos.asObservable()
    .bindTo(tableView.rx_itemsWithCellIdentifier("Cell"))

I am going to use debounce again, which prevents double taps. If the user quickly taps a few times on the + button, instead of opening a few view controllers on the screen, Rx will open it just once.

Then we are going to do a FlatMap. In the previous example, it allowed us to do a bit of work, and then wait until it completes. This is exactly what I want to do with our view controller that is present modally: present the view controller, and wait until its exposed observable via property would emit back a repo. Next, I want to dismiss the view controller. My final call of the chain: do something with the data. In this case, I am going to take the repo that the observable gave me back and add it to a list of repos. To complete the whole logic, I am going to bind my repos list to the Table View.

Quickly, run through NVVM (there was a discussion earlier about what it stands for. Forget about it). You are going to have some view controllers, you will have some models to drive their data, and this is where you will put all your Rx code (all the logic, all the calls). In these last lines in the chain - “since I finally have my data, I want to do something in the UI” - you will make it clear-cut and put it in the view controllers.

It is easy to separate the logic from bindings to our UI. Since the only logic will be in your view models, you will write your tests to test the view models, and you will never have to instantiate view controls or other craziness in your tests. It is very easy to come to a point where you feel things are separate.

RxSwift (18:56)

  1. RxSwift is a synchronous-like asynchronous framework.
  2. It has a functional aspect: it allows you to process this asynchronous event (convert things, do things with them).
  3. And it encourages good architecture.

Very relevant to iOS development (yes it is!).

Further Reading & Credits (19:46)

  • Since Rx is an API that is implemented in various languages, on different platforms: ReactiveX.io will give you instructions how to use the API on with Swift, Java, JS, Skala.
  • RXSwift.org.
  • rx-marin.com, where you could find some articles written by me that would help you start with RX.

In chronological order: thanks to Ash Furrow, for being extreme inspiration. Jens Ravens, who explained me some of the basics. Florent Pillet, who fixed some of my early code. Junior Bontognali, who is just a friend, amazing, supportive (much Rx in there!). And Krunoslav Zaher, who launched RxSwift.

I am here thanks to Natasha, who invited me to New York; Realm sent me here ❤️. If you are interested in any of these jobs - we are hiring!.

Next Up: RxSwift from Start to Finish #3: Reactive Programming with RxSwift

General link arrow white

About the content

This talk was delivered live in September 2016 at try! Swift NYC. The video was recorded, produced, and transcribed by Realm, and is published here with the permission of the conference organizers.

Marin Todorov

Marin Todorov is an independent iOS consultant and publisher. He’s co-author on the book “RxSwift: Reactive programming with Swift” the author of “iOS Animations by Tutorials”. He’s part of Realm and raywenderlich.com. Besides crafting code, Marin also enjoys blogging, writing books, teaching, and speaking. He sometimes open sources his code. He walked the way to Santiago.

4 design patterns for a RESTless mobile integration »

close