How to recollect `SharedFlow` when the first coll...
# android
a
How to recollect
SharedFlow
when the first collect got exception? like I expose a
Flow
from repo to VM then convert it to
SharedFlow
by
shareIn()
then collect this SharedFlow from the fragment, now I got an exception while collecting the SharedFlow, so how to retry/recollect it again?
g
You should include this recollection logic before converting it to SharedFlow You can use
retry
for this: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/retry.html
But still, I would recommend to handle it on level of original Flow, because it knows better semantics of those errors and have more ways to handle it, so your flow wouldn’t propagate errors, instead ignore errors and retry or emit error state and retry after that So if you control code of original Flow, just handle it there, if not, you can use
retry
But don’t forget, you should use retry before creating StateFlow
a
I could use retry on the original Flow like:
Copy code
flow.retryWhen { cause, attempt -> 
    attempt < 3
}
But I have a button for retrying the API call when failed and, I couldn't handle the retry mechanism when the click event occurred.
g
BTW, flow.retry(3) will do the same
But user retry is different thing, and probably should be resolved on level of reactive stream, so when you have source (or multiple sources) of event to start loading, it mapped to your request
It hard to recommend particular solution without context
But this doesn’t look as work for retry() at all, but rather matter how you design your data flows
a
I receive the Flow from repo to VM like:
Copy code
val flow: Flow<Result<T>> = repo.getData()
And collect the flow in the fragment, now the retry button appears when the
result
failed, I searched for an elegant/clean way to recollect the flow on the click event, therefore, the API called again but I didn't found. I ended up with something like that:
Copy code
private val _sponsors = Channel<Unit>()
val sponsors = _sponsors.receiveAsFlow().flatMapLatest {
        repository.loadSponsors()
  }.myShareIn(viewModelScope, replay = 1)

fun loadSponsors() = viewModelScope.launch {
   _sponsors.send(Unit)
}

init { loadSponsors() }
In this case, just call `loadSponsors()`fun when the retry button clicked, what do you think?
g
so what is your problem with this code?
I’m not exactly sure that I see any problem So you should show error case and allow to reload
Just do not cache failed “repository.loadSponsors”
return some error state instead
like:
Copy code
val sponsors = _sponsors.receiveAsFlow().flatMapLatest {
        return runCatching {
            repository.loadSponsors())
         } 
  }.myShareIn(viewModelScope, replay = 1)
I use Kotlin Result here, but you can use any own state to propagate error
a
I see, thank you man, I just tried to apply the cleanest way.
g
Otherwise how you can handle error case?
a
The
sponsors
val is a flow containing custom Result class
Result<T>
, so when collecting it in the fragment I'm just checking if the result failed or not, therefore, I don't need any processing in the VM like
runCathing{}
because the catch applied already in the repo layer.
g
So if you already have this Result class, you do not throw exception and no need to restart, right?
a
Ok but if the result failed when collecting the flow, how to recall the API? I need to recollect the flow again, that's it.
g
What kind result?
If loadSponsors will return error?
But in this case you just restart chain by emitting to _sponsors
a
Yes in the final solution I retrigger the API call by emitting to _sponsors. But I was hoping to find something built-in like retry() in the Flow Apis to make my code more elegant.
g
retry exists, it just will not help in this case