How to Manually Retry a Call Using Kotlin Flow?
I know that you already faced this scenario:
The user opens a screen, and the application does a network call to retrieve some data to fill the screen. But wait… something weird happened and the call failed. I know that you are a good developer and treated this exception and in this case, the application doesn't crash, it just shows a beautiful error screen… “Ops… something went wrong”. You are using Kotlin flows and decided to put a “Try again” button on the error screen. But… how you can restart the flow?
First implementation: without retry
Your view model will look more or less like this:
Here I made 2 main things
- I created sealed classes to represent each screen state (you can read a little more about this approach here ).
- I exposed a Flow that fetch the store list and wrap it in a
UiState
class.
This approach is very good because the flow will start as soon as it gets an observer as you can see in the example below. The example is made with Jetpack Compose but it can also be used with the regular View framework.
However, this approach doesn't provide us with an easy way to restart the flow if something went wrong on the first try. The Flow framework has a way to set up a retry behaviour but this happens automatically, not by the user command. You can see more about the built-in approach here.
Giving our Flow the power to restart
I spent some time thinking about how to do it. Read some articles and reached on the following implementation:
The point of this implementation is to wrap the main flow on an external flow that starts with a MutableStateFlow
. This way, we can use the event of the state changing to start the main flow again.
To make it more convenient, I also create a class called RetryTrigger
to control when the flow should start again and hold the states: RETRYING
and IDLE
.
The state RETRYING
means that the flow should start. The initial state is RETRYING
because we need the first call to run normally. After the first call, the state is changed to IDLE
(line 12).
The state IDLE
means that the flow was already started and returned the first value (it could be a success or an error response). We are filtering the MutableStateFlow
to start the main flow only when the state is RETRYING
(line 10).
Last but not least we have the method retry()
on RetryTrigger
. It set the mutable state to RETRYING
again and the main flow is started again.
The final implementation
On this new implementation we have the following:
- I created an instance of
RetryTrigger
and passed it as the first argument on the retryable flow creation method. - The main flow was wrapped in a retryable flow. I decided to pass this flow inside a lambda. I could even make this method inline, but these are implementation decisions, and is up to you to decide what fits better to your project.
- Now there is a
tryAgain()
method that callsretryTrigger.retry()
. This method should be called when the user presses the “try again” button on the error screen.