Exception in flow is not caught

2.9k views Asked by At

I have a kotlin flow in which halfway an exception is thrown. No matter what I do, the exception is not caught.

The flow is like this: In the viewmodel I have value which needs to be reread from a database upon a change of date. I use a switchmap for this.

 val branches:LiveData<List<SCBranch>> = currentDay.switchMap {
    schooldataUseCases.getBranches(it)
            .catch{
                 exception ->withContext(Dispatchers.Main) {
                    Timber.d("catching exception in switchmap")
                    uncaughtException.value = exception.message
                }
            }
            .asLiveData

The useCase is as follows:

override fun getBranches(day:Day): Flow<List<SCBranch>> =
        schooldataRepository.getBranchesForSchoolPeriodFlow(schoolPeriodManager.getSchoolPeriodFor(day.startTime))

The schoolPeriodManager selects a schoolPeriod for the requested date. If there is no schoolPeriod defined for the requested date it throws an exception. I want to catch that exception and inform the users they selected an invalid date through another liveData 'uncaughtexception'.

Alas, my app ends with an Fatal exception which is indeed the exception thrown by the schoolPeriodManager. So the catch block in the switchmap does not catch the exception.

I tried to add a CoroutineExceptionHandler to the flow like this:

val branches:LiveData<List<SCBranch>> = currentDay.switchMap {
    schooldataUseCases.getBranches(it)
            .asLiveData( exceptionHandler)
}

The exceptionHandler does not catch the exception either. The app still ends with the same Fatal Exception

How should I implement the catch block to catch the raised exception?

2

There are 2 answers

1
Pedro Okawa On

I had the same issue, mate.

In order to handle the catch, you must emit the value, for example:

 val branches:LiveData<List<SCBranch>> = currentDay.switchMap {
    schooldataUseCases.getBranches(it)
            .catch { exception ->
                Timber.d("catching exception in switchmap")
                emit(exception.message)
            }
            .asLiveData()

But in your case, the value emitted on catch is not the same from the map, so maybe you need to create a wrapper class for this, such as a sealed class with content for success and Error on catch.

    sealed class BranchesState {
        data class Success(val branches: List<Int>) : BranchesState()
        data class Error(val message: String) : BranchesState()
        object Loading : BranchesState()
    }

    val branches: LiveData<BranchesState> = currentDay.switchMap {
        schooldataUseCases.getBranches(it)
            .map { BranchesState.Success(it) as BranchesState }
            .onStart { emit(BranchesState.Loading) }
            .catch { exception ->
                Timber.d("catching exception in switchmap")
                emit(BranchesState.Error(exception.message))
            }
            .asLiveData()

PS: The cast is needed on map because otherwise it will show an error that the liveData you're trying to use is the type of BranchesState.Success instead of BranchesState

0
Shogun Nassar On

Just add this "$it.message" to catch the message

.flowOn(Dispatchers.IO).catch{
        Log.d("$it.message")
     }