RxSwift For Dummies š£ Part 1
Edit 02.02.2017: This post was updated to Swift 3.0 and RxSwift 3.2
RxSwift is one of those things you have to try yourself to really start appreciating. Itās the one piece of the puzzle that I was missing to glue all the patterns like MVVM, VIPER, Coordinators/Routing.
Itās hard to express what RxSwift really is, because it does a lot. In general it serves as a great observer pattern with a mix of functional and reactive programming. Itās important to say what it isnāt and it isnāt Functional Reactive Programming in itās original definition. Itās heavily inspired on FRP, so we can say that it contains Reactive and Functional features.
If you donāt know what FRP is, donāt worry for now - youāll be able to discover it yourself in this tutorial. Youāll gradually learn what Functional and Reactive mean in the FRP.
Digging through RxSwift made me feel enlightened and saved, but also massively confused. Trust me, youāll feel the same.
It takes a couple of hours to get used to the idea, but when you do, you donāt want to go back.
In this tutorial, Iāll try to save you these precious hours by explaining everything step by stepā¦ You know, like to a dummy š
You just need some decent knowledge of Swift and UIKit.
The Why?
UI programming is mostly about reacting to some asynchronous tasks. Weāre taught to implement that with observer patterns: Iām pretty sure youāre familiar with delegates by now. Delegating is a cool pattern, but it gets really tiringā¦
- Delegating is a lot of boilerplate code: creating a protocol, creating a delegate variable, implementing protocol, setting the delegate
- The boilerplate reptition often makes you forget things, like setting the delegate (
object.delegate = self
) - The cognitive load is quite high: it takes quite a lot of jumping through files to find out whatās what
RxSwift takes care of that and more! It enables you to create observer patterns in a declarative way (reduces cognitive load) and without any boilerplate code.
Iāve just started a project, I didnāt create one delegate.
Basic Example
Ok, enough talking, letās get to it, but letās start simple.
Hereās a basic example. We have a runExample
method that does something with RxSwift when called. Letās try to understand whatās happening here.
Observable š”
Letās start from the basic building block in RxSwift: the Observable
. Itās actually pretty simple: the Observable
does some work and observers can react to it.
Ok, we have an Observable
. This is a cold āļø observable: it will start executing only when an observer subscribes. A hot š„ observable executes even if it doesnāt have any observers.
Weāll cover the difference and examples in the next parts, so donāt worry, but for now you have to understand that: the Hello dummy š£
value will not be emitted just because you instantiated an āļøObservable
object. The āļøObservable
is frozen and will start executing only when you add an observer.
Letās analyze step by step whatās happening:
The Observable
executes code on the main thread (unless programmed otherwise) so letās use a simple DispatchQueue
to not block it. RxSwift has a mechanism called Schedulers that we could use instead, but letās leave that for later when youāre less of a dummy š.
An Observable
ās time of work is also called a sequence. Throughout itās sequence it can send an infinite number of elements and we use the onNext
method to emit these.
When itās finished it can send a Completed
or Error
event, after which it cannot produce more elements and it releases the closure along with itās references.
Each Observable
has to return a Disposable
.
Use Disposables.create()
if you donāt need to dispose of anything.
If you look into the NopDisposable
implementation it does completely nothing - just empty methods.
Disposable
The Disposable
that needs to be returned in the Observable
is used to clean up the Observable
if it doesnāt have a chance to complete the work normally. For example you can use the AnonymousDisposable
:
The Disposable
is called only when an Observer
is disposed of prematurely: when it gets deallocated, or dispose()
is called manually. Most of the times the dispose()
is called automatically thanks to Dispose Bags. A little lost? Donāt worry, youāll be able to implement that yourself on a more concrete example.
Observer šµ
Our Observable
is cold āļø. It wonāt start executing until we start observing it.
Thatās the way you subscribe. Subscription is created and a Disposable
(a record of that subscription) is returned by the subscribeNext
method.
The Observable
has started work and after 10 seconds youāll see this printed out:
subscribe(onNext:)
will only react to Next
events. You can also use subscribe(onCompleted:)
and subscribe(onError:)
.
Dispose Bag š
The only cryptic thing here is the addDisposableTo
method.
Dispose bags are used to return ARC like behavior to RX. When a DisposeBag is deallocated, it will call dispose on each of the added disposables.
You add the Disposable
s you create when you subscribe to the bag. When the bagās deinit
is called (for example when the ExampleClass
object gets deallocated) the Disposable
s (subscriptions) that didnāt finish will be disposed of.
Itās used to dispose of old references that you pass in the closure and resources that are not needed anymore: for example an open HTTP connection, a database connection or a cache.
If you donāt understand now, donāt worry, a better example is coming.
Observable operators
create
is just one of many ways to create an Observable
. Take a look into ReactiveX official documentation for a list of operators. Letās take a look at some of them.
Just
just
just creates an observable that emits one value once and thatās it. So the sequence in that example would be: .Next("Hello")
-> .Completed
Interval
interval
is a very specific operator that increments an Int
from 0 every 0.3
(in this example) seconds. The scheduler is used to define the threading/async behavior.
Repeat
repeat
repeats a given value infinitely. Again, you can control the threading behavior with a SchedulerType
.
As you probably noticed, these are not very exciting, but itās good to know that there are other operators. One more important thing to notice is that itās a start of the functional part of RxSwift.
Real life example
Ok, letās wrap this up and letās do a quick example. Our knowledge of RxSwift is quite limited, so letās use a simple MVC
case. Letās create a model that will create an Observable
that fetches data from google.com. Fun! š
Thatās pretty simple: the createGoogleDataObservable
creates an Observable
we can subscribe to. The Observable
creates a data task and fetches the google.com website.
The data task of URLSession
is executed on a background thread, so we need to update Observers on the UI queue. Remember that we could use schedulers, but Iāll cover that in a more advanced stage.
The Disposable
is a great mechanism: if the observer stops observing the data task will be cancelled.
Now the observer part:
Amazing, huh? No protocols, no delegates, just a declarative definition of what should happen when thereās a new event.
Donāt forget about [weak self]
or [unowned self]
in the closure to avoid retain cycles.
Thereās a more reactive way to implement setting the text thatās called binding, but weāre too š£ to get into that now.
Dispose Bag Example
As you mightāve noticed, the disposeBag
is an instance variable of our ViewController
:
When the view controller gets deinitialized, it will also release the disposeBag
.
If the disposeBag
is released, itās deinit
will be called and our AnonymousDisposable
(created using Disposables.create(with:)) will be called on our Observable
and the data task will be cancelled and the connection will be closed if it didnāt have a chance to finish!
I hope this clearly explains the mechanism of dispose bags.
Thatās it!
Hereās the code for the example project.
This wraps it up. Youāve learned how to create observables and observers, how disposing works and hopefully you can see how this is better than the usual observer patterns.
Part 2 is about the whole functional part in RxSwift - operators.
Update: This post was updated to Swift 3.0 & RxSwift 3.2 by Yogish