RxSwift Safety Manual š
RxSwift gives you a lot of useful tools to make your coding more pleasurable, but it also can bring you a lot of headaches andā¦ bugs š± After three months of using it actively I think I can give you some tips to avoid the problems I encountered.
Side Effects
Side Effect in computer science may be hard to understand, because itās a very broad term. I think this Stackoverflow thread does a good job of explaining it.
So basically, a function/closure/ā¦ is said to have a side effect if it changes the state of the app somewhere.
In the context of RxSwift:
Why is that important in RxSwift? Because a coldāļø signal (like the one we just created) starts new work every time itās observed !
Letās observe on observableWithSideEffect
twice:
We would expect it to print 2
and 2
, right? Wrong. It prints 2
, 3
, because each subscription creates a separate execution, so the code inside the closure is executed twice and the side effect(counter incrementation) happens twice.
It means that if you put a network request inside, it would execute two times!
How do we fix this? By turning the cold observable into a hot one š”! We do this using publish
, connect
/refCount
operators. Hereās a whole tutorial explaining this in detail.
Itāll print 2
, 2
.
Most of time time itās enough if you just use the more high level shareReplay
operator. It uses the refCount
operator and replay
. refCount
is like connect
, but itās managed automatically - it stars when thereās at least one observer. replay
is useful to emit some elements to observers ālate to the partyā.
Main Queue
When observing in the view controller and updating the UI you never know on which thread/queue does the subscription happen. You always have to make sure itās happening on the main queue, if youāre updating UI.
Error Events
If you have a stream of observables bound together and thereās an error event on the far end all the observables will stop observing! If on the first end thereās UI, it will just stop responding. You have to carefully plan your API and think of whatās going to happen when completed
and error
events are passed in your observables.
If importantText
sends an error
event for some reason, the binding/subscription will be disposed.
If you want to prevent that from happening you use catchErrorJustReturn
Driver
Driver
is an Observable
with observeOn
, catchErrorJustReturn
and shareReplay
operators already applied. If you want to expose a secure API in your view model itās a good idea to always use a Driver
!
Reference Cycles
Preventing memory leaks caused by reference cycles takes a lot of patience. When using any variable in the observe closure it gets captured as a strong reference.
The view controller has a strong reference to the view model. And now, the view model has a strong reference to the view controller because weāre using self
in the closure. Pretty much basic Swift stuff.
Hereās a small tip:
Use [unowned self]
without worrying it might be dangerous when adding disposable to self.disposeBag
. If self is nil
, then the dispose bag is nil
, so the closure will never be called when self
is nil - app will never crash and you donāt have to deal with optionals š¤
self
is not the only case you have to watch out for! You have to think of every variable that gets captured in the closure.
It might get very complex, thatās why itās a very good idea to keep closures short! If a closure is longer than 3-4 lines consider moving the logic to a separate method - youāll see all the ādependenciesā clearly and youāll be able to decide what you want to capture weakly/strongly.
Managing your subscriptions
Remember to always clear the subscriptions you donāt need anymore. I had an issue where I didnāt clear my subscriptions, the cells were reused, new subscriptions were created each time causing some very spectacular bugs.
RxSwift is very complex, but if you set your own rules in the project and adhere to them, you should be fine š Itās very important to be consistent in the RxSwift API youāre exposing for each layer - it helps with catching bugs.