Introduction to Functional Reactive Programming and Table Views

Introduction

In the last few years, there has been a lot of talking at conferences, blog posts and tutorials about this awesome different approach to programming – Functional Reactive Programming (FRP). Facebook uses it as a way to handle the countless different news posts appearing on their mobile apps feeds without making large unmaintainable codebase (check this video for more details about their react inspired framework Component Kit). This same approach is at the core of their hybrid mobile platform React Native.

Elsewhere in the mobile world, a lot of iOS enthusiasts find this way of thinking combined with the MVVM pattern as a solution to their massive view controllers appearing everywhere. And this is true, I’ve seen a lot of view controllers going over 1000 lines of code, doing all kinds of stuff, like presentation logic, networking, business logic and much more. Some mask it by creating categories (or extensions in Swift), but that’s still the same class just split in several files.

State is also a big problem in mobile apps – there are a lot of things going on in several different threads which can access that state and modify it in an unexpected manner. The most tricky and annoying bugs appearing in mobile apps are because of state. One of the great benefits of FRP is that you don’t have to maintain state, because everything happens in data streams happening in a period of time.

Another problem is the testing – it’s really hard to test parts of a view controller, since you will need to instantiate the controller in the tests, call the system methods and so on. It’s one of the reasons why mobile projects usually have around 20% code coverage. Most of the others (with exceptions) that go past this number have all kinds of useless tests that don’t bring any value to the stability of the code.

Lastly, there is the fun factor. Most of the FRP protagonists claim they really enjoy coding this way, describing it as magic and art. I would also add it’s like writing poetry. And of course, the only way to do great work is love what you do.

Having all these benefits in mind, I’ve decided it was time to give it a go in my next project.

What is Functional Reactive Programming?

There are a lot of great resources and definitions about FRP on the internet explaining this paradigm. I prefer the one that FRP is combination of streams, transformations and bindings.

Streams can be imagined as pipes – throwing data at certain periods of time, transformations (like map, filter) do something with this data (convert it in something else) and all this can be bound to things that can display this altered data. All the transformations can be chained, which makes it even more powerful. I would not go in much details about FRP, since a lot of people have done that already in an excellent manner (see links above).

What is Model-View-ViewModel (MVVM)?

MVVM is an upgrade to the Model-View-Controller (MVC) that we currently use in most iOS projects. It tries to solve the problem of the massive view controllers described above. The biggest difference is that the presentation logic is not in the view controllers anymore, but in a different class/structure called ViewModel. The view controller calls methods of the view model when it needs details about the presentation logic and in an ideal case, most of the view controller methods would be the system ones like viewDidLoad. The view model is kept in the controller, but the view model doesn’t know about the controller – it can be plugged anywhere (test case classes as well). This architecture then requires some mechanism that will update the data of the view model when the model changes and this change needs to propagate to the user interface (the view). This is where the FRP libraries come into play.

FRP libraries

There are some really great FRP libraries for iOS, like Reactive Cocoa, RXSwift and ReactKit. I’ve decided to go with RXSwift, since it looked more Swifty and also because it is the Swift implementation of Reactive Extensions from .NET. The latter might be useful if you get stuck on something and there is no concrete solution for RXSwift – you can try to find it in another language. Another plus for RXSwift is that if you develop an Android version in parallel, there is RXJava library that you can use with the same architecture for Android. The other libraries also look pretty good and can be tried out in the future.

FRP in action – Table Views

One of the most used components in iOS development is definitely UITableView. When used in the traditional manner, you need to implement two delegate methods that will tell the table view how it should look like and how big it should be. The second one is boilerplate, since most of the time we need to have as many rows in the table as there are entries in the array that serves as data source. In RXSwift, all this can be resolved with one liner.

Imagine you have list of videos, where each row displays an image of the video, a sponsor logo and a share button, which when is clicked shares the video. You probably know how to do this in MVC, however here’s an RXSwift solution:

Screen Shot 2016-08-21 at 00.19.46

With this one line of code, we tell that the videos will be bound to the table view and in the callback we do what we usually do in the cellForRowAtIndexPath method. Let’s go into more details here:
– currentVideoFeed – it represents the view model that’s responsible for loading the videos from the network.
– videos – this is a Variable object, which can both observe and be observed from other objects. This means that the results of the network requests for the videos can be bound to this variable, but also the table view can observe for any changes of the videos. In the view model, here’s how the videos variable is filled with data:

Screen Shot 2016-08-21 at 00.31.25

In this code, after the request is finished, the result of it (which is a Response object) is converted (using flatMapLatest) into a driver of array of VideoItems, which is what the table view expects. But what is a Driver? A driver is something that can be observed only on the main thread. We use this because the changes to the videos will require changes to the table view, which have to be done on the main thread.

I will not go into much details for the flatMapLatest closure, since this is domain specific – it converts the response from the server to what you expect in the objects that observe this variable. Only one thing that is worth mentioning is the forward pipe operator |> from F#, which is very useful when you want to pass the results of one function (or tuple) to the other without nesting the functions.

Screen Shot 2016-08-21 at 18.23.07

About the dispose bags at the end of the last chunk of code, we will refer to the RXSwift documentation: Using dispose bags or takeUntil operator is a robust way of making sure resources are cleaned up.

With this setup, whenever the videos are updated through a network request, the videos variable is updated, which updates the table view – without the need of manually calling reload data.

Now that we know about the videos variable, let’s go back to the cell rendering method in the binding closure above:

Screen Shot 2016-08-21 at 00.19.46

Here we are sending drivers for the thumbnail image and the sponsor logo. These drivers are created in the view model (currentVideoFeed):

Screen Shot 2016-08-21 at 00.33.53

 

This driver will return UIImage when the request finishes. The driver is passed to the cell, which will then subscribe to it, so whenever the image arrives, the image view that needs to display the image, will update itself:

Screen Shot 2016-08-21 at 00.34.52

This is similar to the table view binding to the videos variable, the only difference here being that we also implement the doOnError method, which is our chance to provide some placeholder image if the request fails. Apart from that, in the bindNext method, we are just setting the right image to the image view, depending on the type of the driver (sponsor logo or thumbnail).

The last part of the cell rendering closure that we haven’t covered is the onShareDelegate callback:

onShareDelegate: { (content: String) in
self.displayShareSheet(content)
}

The displayShareSheet is just a method in the controller that shows the native sharing sheet, but how is this handled on the cell’s side? The traditional way of doing this is to tie an IBAction to the share button in the cell and then call the delegate’s method with a parameter identifying the cell. In RXSwift, this is done like this:

Screen Shot 2016-08-21 at 00.36.14

With rx_tap, we are subscribing to all button clicks (events) and then we just call the function which we pass from the controller.

Now let’s say we want to actually show the video when the cell is tapped. The normal way to do this is to implement the didSelectRowAtIndexPath method, where based on the indexPath, you show the selected video. The same thing done in RXSwift:

Screen Shot 2016-08-21 at 00.37.30

Here, the rx_modelSelected is RXSwift’s equivalent to the didSelectRowAtIndexPath method.

And what if you want to animate the cells, you’ll need to subscribe to the rx_willDisplayCell observable:

tableView.rx_willDisplayCell.subscribeNext{ (cell, indexPath) in
self.animateCells(cell, cellIndex: indexPath.indexAtPosition(1))
}.addDisposableTo(disposeBag)

Conclusion

This was just a short intro to what can be done with FRP and RXSwift, especially with the table views. All of the things that we do in the standard iOS way, can be done with RXSwift as well, while also reducing the boilerplate code. Also, this approach doesn’t require to care about explicitly calling the updates on the UI elements, everything is updated by itself after the binding is setup. Also, the video feed model can be easily unit tested, independently of the view controller.

If we don’t want to make our tests dependent on the network, we can also create Test subclass of the view model, where the videos are retrieved locally from a file. Or even better, we can define VideoFeedProtocol, which both the test and real view model will implement. More about this in a future post about Protocol Oriented Programming in Swift.

I hope you will like FRP, RXSwift and MVVM as much as I do – as mentioned at the beginning I’m really enjoying when I’m working on these things. And when I go back to a standard MVC project (if on top of that is Objective-C), it looks quite boring 🙂

5 Comments

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s