Nowadays we can say that all mobile applications communicate with at least one server to gather data and show to the user. We need to create robust data repositories in order to provide fast and resilient apps that can overcome many bad situations like bad internet service, constant changes from mobile data to wifi networks, or saturated backends that may not respond in the expected time.
Resiliency is the capability to handle partial failures while continuing to execute and not crash. For example, applications that communicate over networks (like services talking to a database or an API) are subject to transient failures. These temporary faults cause networking issues, and other problems that come and go and are hard to reproduce.
The retry pattern is one of the easiest and effective mechanisms to deal with those transient problems that can affect our mobile applications. Building this type of error handling mechanisms can be difficult and bug-prone. Using RxSwift you have a method that automatically does that for you.
This method is based on the retry operator described in ReactiveX documentation: http://reactivex.io/documentation/operators/retry.html
“The Retry operator responds to an onError notification from the source Observable by not passing that call through to its observers, but instead by resubscribing to the source Observable and giving it another opportunity to complete its sequence without error. “
This method can be used on an ObservableType ( Single < T >, Observable < T >, and others…) and will immediately re-subscribe the source sequence if it receives an onError event triggering the sequence again. In the example, someApiCall() returns an observable sequence with the response of an API call.
This will allow us to retry the request if it fails, but as this will trigger the request as it fails, this may be not ideal if the device cannot get service quickly or the server is momentarily overloaded. In those situations, we would want to wait a few seconds after retrying the request.
Thanks to the RxSwift community we have a project called RxSwiftExt which has a lot of convenience methods that do this kind of task: https://github.com/RxSwiftCommunity/RxSwiftExt
This extension has several types of retry mechanism, it’s called RepeatBehaviour and has:
- .immediate : same as the one in RxSwift.
- .delayed : applies a delay between retries.
- .exponentialDelayed : same as delayed but the delay time will increment exponentially between retries.
- .customTimerDelayed : we can provide our custom delay timer.
self.apiClient.someApiCall() .retry(.exponentialDelayed(maxCount: 5, initial: 1.0, multiplier: 2.0))
So if we change the example to use RepeatBehaviour.exponentialDelayed the first retry will be delayed by 1 second, the second retry by 2 seconds, the third by 4 seconds, and so on. If we don’t receive any success event after five retries the retry operator will trigger an error event to the underlying sequence.
If for some reason we want to set a custom timeout for a given request we can make use of the timeout operator. This operator emits a timeout error event if the source sequence doesn’t emit any event in the given due time.
.timeout(dueTime: RxTimeInterval, scheduler: SchedulerType)
We can combine this operator with the retry as follows:
self.apiClient.someApiCall() .timeout(RxTimeInterval.seconds(5), scheduler: MainScheduler.instance) .retry(5)
As seen in this article, it is really easy to apply a retry strategy in your repositories by using the methods .retry() and .timeout(). This will make your app more resilient to those transient failures that are very common in mobile applications. We can apply these strategies to database queries, network requests, and all other kinds of repositories that your app may have to interact with other services. These convenience methods are present in almost all of the reactive libraries in mobile like Combine, RxSwift, RxJava so I encourage you to start using them to create better apps!