In recent days MVVM become very popular architecture design for iOS apps. Especially when RxSwift starts to gain more and more popularity. In RxMVVM most of properties are expressed by Observables.
However, Observables terminate whenever they receive
completed events. Termination means an Observable subscription won’t receive any new message. When I started to learn Rx I didn’t realize the consequences of this rule.
Do you have problems with errors handling? Did your Observable terminate unexpectedly and your button stopped sending tap events? This is what the article is about. Enjoy reading 📚💪
Errors – The example
Mobile applications usually do some API requests when a user taps a button. Our example will cover such a case:
success invokes fake API request with success answer. In the same way, tapping on
failure fakes the error. Tapping on the buttons should increase the count number.
This is what I want to achieve represented by this diagram:
successTap -s-----s--s-----s----------> failureTap ----f---f-----f----f-------> buttonTaps<Bool> -T--F--TF-F---F-T--F-------> response --V--E--VE-V---E-V--E------> (using flatMap) where: 's' and 'f' - success or failure button tap 'T' and 'F' - true or false 'V' - success response 'E' - failure response (error)
Coding time – the #1 attempt
Let’s write some code. You need to
map() tapping on the success button as
true event and map tapping on the failure button as
false. Next, you have to
merge() them into single Observable:
flatMap()seems strange, read Thinking in RxSwift first. I’m describing there how to think in Reactive way and how the basic operators works
Honestly speaking, tapping on
success will indeed increase the success count. However, as soon as you tap the
failure button the whole Observable chain will dispose itself. Since now, tapping on
success won’t increase the success count anymore 🙀.
Why? you ask.
performAPICall fails it returns an error event (the same as a real API call does). Since we use
flatMap all the
errors from the inner Observable are passed into the main sequence.
As a result, the main Observable sequence receives an
error event and it also terminates 💀⚰.
RxSwift & errors – How to handle them?
Sometimes errors are what you expect to happen. Take a login form as an example. You expect the server to return an error if a password doesn’t match a given e-mail. It’s an expected error, and god, this is good the error comes! 👌
If an error isn’t the exception it shouldn’t end the Observable sequence. To make that happen, your API calls should returns
Observable<Result<T>>. Having a
next event won’t terminate the main Observable sequence.
Thanks for those two observables it is possible to handle API errors as you would like to:
😱 – the performAPICall() is called twice
Above solution works as we expect, however, there is one bug inside. Whenever you press any of the buttons, the
performAPICall() is called twice. You would have to put a breakpoint in
performAPICall() to notice that.
You may say it is not a big deal in our sample, but in real life invoking one method 2 times would send 2 requests to the server which is bad. To fix that you need to use
share() operator in the
result Observable. The rest is unchanged:
Where to go from here?
When you use RxSwift extensions to feed the UI, handling errors is not as simple task as you may first think of.
Error event breaks the Observable, even if the
error comes from an inner
Usually, you want to notify a user about errors. To do so, you have to treat them as something expected to happen, not like an exception. I recommend to use materialize() from RxSwiftExt. However, don’t forget to use
share() 🙂. You don’t want to send 2 requests to the API 😉
You can find the project sample here.
If you want to read more about
share() operator there is a great article about it.
One of the GitHub issues says more about errors and the idea there is no such thing as universal error. The talk is eye-opening. I think it is worth to read id.
Do you like the article? Please share it by clicking on buttons below. Cheers!