Start your adventure with Functional Reactive Programming and learn how to use RxSwift in your app development!

Edit 18.01.2017: This post was updated to Swift 3.0 and RxSwift 3.1

Swift is that kind of a language that feels good whatever you do with it. It connects good aspects of other languages, which makes Swift really flexible and relatively easy to understand by newcomers. That’s why you can find it being used with Object-Oriented Programming, but also with much more paradigms like the newest one Protocol-Oriented Programming, presented at WWDC’15.

You don’t have to search much more to find that you can also use Functional Programming and Reactive Programming in Swift. Today and for another few weeks, we will talk about the combination of the last ones, Functional Reactive Programming.

So what is that Functional Reactive Programming? In short, this is using Reactive Programming with Functional Programming blocks (filter, map, reduce etc.). Guess what? Swift has them built-in already! And about the Reactive part, RxSwift covers us up.

RxSwift is a Reactive Extensions version written in Swift.

ReactiveX is a combination of the best ideas from the Observer pattern, the Iterator pattern, and functional programming

Basically, you have to change your perspective from statically assigning a value to the variable, to observing something that can and probably will change in the future.

You may ask “Why would I ever want to use it?”. Well, the answer is simple. It just simplifies (sic!) your work. Instead of notifications, which are hard to test, we can use signals. Instead of delegates, which take a lot of place in the code, we can write blocks and remove multiple switches/ifs.

We also have KVO, IBActions, input filters, MVVM and many, many more which are handled smoothly by RxSwift. Remember, it’s not always the best way to solve a problem, but you have to know when to use it to its full potential. I will try to present you some examples that you can use in your application.

Definitions

First, we want to start off with some definitions. To better understand the logic we kinda have to go through the really basic stuff.

Your smartphone is observable. It emits signals like Facebook notifications, messages, Snapchat notifications and so on. You are naturally subscribed to it, so you get every notification in your home screen. You can now decide what to do with that signal. You are an observer.

Here we go, now you are fully prepared for the Example below?

Example

We will write City Searcher – type city in the search box and dynamically show us the list. When you write something in the search bar, we will dynamically try to fetch towns that start with given letters and display it in a table view. Pretty simple, right? When you try to build a dynamic search in your app, you always have to think about what can go wrong. For instance what if I will write really fast and will change my mind often? We would have many API requests which we have to filter. In a real app, you would have to cancel the previous request, wait sometime before sending another request, check for the phrase if it is the same as before and so on. Often times it creates huge logic, which looks pretty simple at the first sight. “It is just a dynamic search, what could go wrong?”. Of course, you could do it without using Rx, but let’s see how we can write that logic using little to no code.

First, we need to create a project (if you don’t know how to do it, you can check out our other tutorials, for example here. Then we need to install CocoaPods and RxSwift + RxCocoa. Example Podfile could look like this:

platform :ios, '8.0' use_frameworks! target 'RxSwiftExample' do pod "RxSwift" pod "RxCocoa" end 1 2 3 4 5 6 7 8 9 platform : ios , '8.0' use_frameworks ! target 'RxSwiftExample' do pod "RxSwift" pod "RxCocoa" end

If we have every tool ready, we can start writing some code!

We’re gonna create our simple UI which is UISearchBar + UITableView .

Then we will need some arrays to save our cities. To reduce the logic in our code we will avoid using API, which will be covered in the next tutorials, and instead we will use two arrays, one with all cities and second one with shown cities. This will effectively act as an API in our case.

var shownCities = [String]() // Data source for UITableView let allCities = ["New York", "London", "Oslo", "Warsaw", "Berlin", "Praga"] // Our mocked API data source 1 2 var shownCities = [ String ] ( ) // Data source for UITableView let allCities = [ "New York" , "London" , "Oslo" , "Warsaw" , "Berlin" , "Praga" ] // Our mocked API data source

Then we will setup UITableViewDataSource and connect it with our shownCities variable:

@IBOutlet weak var tableView: UITableView! @IBOutlet weak var searchBar: UISearchBar! override func viewDidLoad() { super.viewDidLoad() tableView.dataSource = self } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return shownCities.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "cityPrototypeCell", for: indexPath) cell.textLabel?.text = shownCities[indexPath.row] return cell } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @ IBOutlet weak var tableView : UITableView ! @ IBOutlet weak var searchBar : UISearchBar ! override func viewDidLoad ( ) { super . viewDidLoad ( ) tableView . dataSource = self } func tableView ( _ tableView : UITableView , numberOfRowsInSection section : Int ) - > Int { return shownCities . count } func tableView ( _ tableView : UITableView , cellForRowAt indexPath : IndexPath ) - > UITableViewCell { let cell = tableView . dequeueReusableCell ( withIdentifier : "cityPrototypeCell" , for : indexPath ) cell . textLabel ? . text = shownCities [ indexPath . row ] return cell }

As of now, it should work as your normal UITableView so if we’d change the values of shownCities we should see it on the screen.

Right, now to the more interesting stuff. We will now observe the text in UISearchBar. It is really easy because RxCocoa (which is the extension of RxSwift) has this built in for us! UISearchBar and many more controls given by Cocoa frameworks has support from Rx team. In our case, with usage of UISearchBar , we can use it’s property rx.text , which emits signals once the text in the search bar change. Cool! So how to observe this thing? Pretty simple! First, we need to import RxCocoa & RxSwift.

import RxCocoa import RxSwift 1 2 import RxCocoa import RxSwift

Then onto the observing part! In viewDidLoad() we will add observing to the rx.text property of UISearchBar :

searchBar .rx.text // Observable property thanks to RxCocoa .orEmpty // Make it non-optional .subscribe(onNext: { [unowned self] query in // Here we will be notified of every new value self.shownCities = self.allCities.filter { $0.hasPrefix(query) } // We now do our "API Request" to find cities. self.tableView.reloadData() // And reload table view data. }) .addDisposableTo(disposeBag) 1 2 3 4 5 6 7 8 searchBar . rx . text // Observable property thanks to RxCocoa . orEmpty // Make it non-optional . subscribe ( onNext : { [ unowned self ] query in // Here we will be notified of every new value self . shownCities = self . allCities . filter { $ 0.hasPrefix ( query ) } // We now do our "API Request" to find cities. self . tableView . reloadData ( ) // And reload table view data. } ) . addDisposableTo ( disposeBag )

Note: As you can see in RxSwift 3.1 instead of prefix rx_something (which was in earlier versions and Swift 2.2) there is rx.something . Thanks to that it is more convenient to check what properties/methods exist for given objects. Additionally there is no subscribeNext etc. syntax. However you can use my pod RxShortcuts and get it back with other helpful functions.

Perfect! And our dynamic search is working like that! subscribeNext is probably quite understandable – we are subscribing to the observable property, which produces signals. It’s like you’re telling your phone “Alrighty, now every time you got something new, show it to me”. And it will show you everything new. In our case we need only new values, but subscribe has more wrappers with events like onError, onCompleted etc.

The more interesting thing is the last line. When you subscribe to observables, often times you want to unsubscribe from it when an object is being deallocated. In Rx we have something called DisposeBag which is normally used to keep all the things that you want to unsubscribe from in the deinit process. For some cases, it is not needed, but the general rule of thumb is to always create that bag and add disposables to it. Next time we will learn how we can use a small library to help us with that process, but for now, we have to create our bag in order to compile our project:

var shownCities = [String]() // Data source for UITableView let allCities = ["New York", "London", "Oslo", "Warsaw", "Berlin", "Praga"] // Our mocked API data source let disposeBag = DisposeBag() // Bag of disposables to release them when view is being deallocated 1 2 3 var shownCities = [ String ] ( ) // Data source for UITableView let allCities = [ "New York" , "London" , "Oslo" , "Warsaw" , "Berlin" , "Praga" ] // Our mocked API data source let disposeBag = DisposeBag ( ) // Bag of disposables to release them when view is being deallocated

So now, after compiling, we should have working application! After typing “O”, we should get our Oslo record in the table view. Excellent! But… what about all the things we were scared of? API spamming? Empty phrase? Delay? Right, we need to protect ourselves.

Let’s start with protecting our API backend. We need to add delay, that will fire up the request after X seconds after the typing, but only if the phrase didn’t change. Normally we could for instance use NSTimer fire it after a delay or invalidate it if the new phrase is given. Not that hard, but still there is a room for error.

LET’S TALK ABOUT YOUR APP We’re 100% office based team with 7-years’ experience

in mobile & web app development Estimate project

What if we type for instance “O”, the results appear, we then change our mind and type “Oc”, but instantly go back to “O”, just before the delay and the API request fires up. In that case, we have 2 exactly the same requests to API. In some cases, we want that behavior, because maybe the database refreshes really fast. But normally it is not needed to fire up two the same requests in the span of let’s say 0.5 seconds.

To do that without Rx we would add some flag/last searched query and compare it with the new. Again not too many lines of code, but the logic grows and grows. In RxSwift we can do this with 2 lines of code. debounce() makes the delay effect on given scheduler, and distinctUntilChanged() protects us from the same values. If we connect it with the previous version it should look like this:

searchBar .rx.text // Observable property thanks to RxCocoa .orEmpty // Make it non-optional .debounce(0.5, scheduler: MainScheduler.instance) // Wait 0.5 for changes. .distinctUntilChanged() // If they didn't occur, check if the new value is the same as old. .subscribe(onNext: { [unowned self] query in // Here we subscribe to every new value self.shownCities = self.allCities.filter { $0.hasPrefix(query) } // We now do our "API Request" to find cities. self.tableView.reloadData() // And reload table view data. }) .addDisposableTo(disposeBag) 1 2 3 4 5 6 7 8 9 10 searchBar . rx . text // Observable property thanks to RxCocoa . orEmpty // Make it non-optional . debounce ( 0.5 , scheduler : MainScheduler . instance ) // Wait 0.5 for changes. . distinctUntilChanged ( ) // If they didn't occur, check if the new value is the same as old. . subscribe ( onNext : { [ unowned self ] query in // Here we subscribe to every new value self . shownCities = self . allCities . filter { $ 0.hasPrefix ( query ) } // We now do our "API Request" to find cities. self . tableView . reloadData ( ) // And reload table view data. } ) . addDisposableTo ( disposeBag )

Glorious! But… We forgot about something. What if the user typed something, refreshed the table view, and then deleted his phrasemaking new value that is empty? Yeah, we will send a query with empty parameter… In our case, we don’t want to do it so we have to somehow protect us against it. How? With the usage of filter() . You might know it already as it is built in Swift. But it raises the question: “Why do I have to use a filter on one value? filter() works on collections!!!”. And this is a really good question!

But don’t think about Observable as a value/object. It’s a stream of values, that will happen eventually. And therefore you will easily understand the usage of functional blocks. To filter our values we will do it as you would do it with an array of strings. Simply:

.filter { !$0.isEmpty } // Filter for non-empty query. 1 . filter { ! $ 0.isEmpty } // Filter for non-empty query.

And that’s it! The complete code, which covers really not-that-easy logic, consists of 9 lines. Magic!

searchBar .rx.text // Observable property thanks to RxCocoa .orEmpty // Make it non-optional .debounce(0.5, scheduler: MainScheduler.instance) // Wait 0.5 for changes. .distinctUntilChanged() // If they didn't occur, check if the new value is the same as old. .filter { !$0.isEmpty } // If the new value is really new, filter for non-empty query. .subscribe(onNext: { [unowned self] query in // Here we subscribe to every new value, that is not empty (thanks to filter above). self.shownCities = self.allCities.filter { $0.hasPrefix(query) } // We now do our "API Request" to find cities. self.tableView.reloadData() // And reload table view data. }) .addDisposableTo(disposeBag) 1 2 3 4 5 6 7 8 9 10 11 searchBar . rx . text // Observable property thanks to RxCocoa . orEmpty // Make it non-optional . debounce ( 0.5 , scheduler : MainScheduler . instance ) // Wait 0.5 for changes. . distinctUntilChanged ( ) // If they didn't occur, check if the new value is the same as old. . filter { ! $ 0.isEmpty } // If the new value is really new, filter for non-empty query. . subscribe ( onNext : { [ unowned self ] query in // Here we subscribe to every new value, that is not empty (thanks to filter above). self . shownCities = self . allCities . filter { $ 0.hasPrefix ( query ) } // We now do our "API Request" to find cities. self . tableView . reloadData ( ) // And reload table view data. } ) . addDisposableTo ( disposeBag )

And that’s it for today! Pretty fun if you ask me! As always the full project is available on our github!

In the repository there are more example projects: some of them are already commented, some of them are not, but you can check them out to prepare for the next tutorial! Cheers!

Read more articles about RxSwift

RxSwift by Examples #1 – The Basics

RxSwift by Examples #2 – Observable and the Bind

RxSwift by Examples #3 – Networking

RxSwift by Examples #4 – Multithreading