RxSwift by Examples #1 – The Basics
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:
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.
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:
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.
1 2 | import RxCocoa import RxSwift |
Then onto the observing part! In viewDidLoad()
we will add observing to the rx.text
property of UISearchBar
:
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 isrx.something
. Thanks to that it is more convenient to check what properties/methods exist for given objects. Additionally there is nosubscribeNext
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:
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
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:
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:
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!
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!
You can find complete source code on Droids on Roids’s GitHub repository and here you can check other RxSwift examples!
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
About the author
Build a mobile app with experts with 12 years of experience
Our experts help with the whole process from idea to app release
Thank you so much for this tutorial. I’ve been looking all over for a very basic RxSwift tutorial. Not only there are only a handful of tutorials about RxSwift as a whole, most of them just get way too complicated way too fast. This is the best one I found.
Thank you so much for the kind words! ☺️
I’m trying to refresh my knowledge in RxSwift and also familiarize myself with the updated RxSwift library. Here searchBar.rx.text.subscribeNext, I’m getting an error saying it has no member called subscribeNext.
Can you please update the tutorial to the latest syntax if possible? Thank you.
” .addDisposableTo(disposeBag) // Don’t forget to add this to disposeBag to avoid retain cycle.”
Are you sure ? You’re using
[unowned self]
, so there’s no retain cycle.If you didn’t use
[unowned elf]
/[weak self]
it would cause a retain cycle EVEN with the call toaddDisposableTo
. As you mentioned, the disposable bag gets disposed ondeinit
, anddeinit
*won’t be called*, because there would be a strong reference to self in the closure.I think that the dispose bag’s main function is to dispose resources. Sure it clears references too, but it won’t fix retain cycles.
Or am I missing something here ?
Hey @swiftpearls, thanks for the comment. You are right, I’m not kinda sure why but the comment is mixed with retain cycle and disposing the subscription on release. For the “beginners” course I wanted to make sure people will remember that as a rule of thumb you should use disposeBag and only avoiding it when you specifically know why and how.
Nonetheless, thanks for the catch, will be fixed in both repo & post ?
Thanks a lot! I’ve read and watched tons of tutorials about starting with reactive, but it is the first when I could continue adding features after the end by myself.
I really appreciate that! That was one of my goals writing it, so I am really happy with the results ?
Any tip on how can I show all content when searchBar is empty within Rx ?
Got the following error message for latest version 3.0.alpha1 in Swift 3.0.
Value of type ‘UISearchBar’ has no member ‘rx_text’
Any idea ? Thanks
Hey! Depends what version of RxSwift are you using, but there will be many changes in the upcoming RxSwift for Swift 3.0 release. In the newest release there is a namespace “rx” so instead of using rx_something, you need to use rx.something, like: UISearchBar().rx.text. I’m not sure it is implemented yet, but should be.
Hope it helps! ?
Thanks for the tutorial. It saved me tons of time! I really appreciate it.
Hi, thanks for the tutorial.
I just wonder why don’t you use tableView bindings instead of using UITableViewDatasource methods.
tableView.rx.items...
It’s really a good and simple tutorial for beginners to begin with. Thank You Łukasz Mróz.
I have made some changes to this City Searcher app like using the bindings to bind the tableview with the shownCities instead of using the datasource methods. Also I have changed shownCities from [String] to Variable, so that we can observe any changes to shownCities and make the changes to tableview. You can find the source code here –
https://github.com/iamyogish/RxSwiftTableViewExample1
Hope this helps someone 🙂
This is really cool, thanks for sharing that with us! I might link it in the https://github.com/DroidsOnRoids/RxSwiftExamples repository later 🙂
Thank You 🙂
Awesome 🙂 thanks
Gonna check out your next tutorial I really liked your phone example. Super duper easy to understand the concept of whole RX 🙂
Thank you! I really appreciate that! And hope you like next ones as well 🙂
Amazing Tutorial bro. Thanks 🙂
Thanks your tutorial. I spend 4 hour for run tutorial because I write [let disposeBag = DisposeBag()] in func viewDidLoad