Confused about error handling in network layer when implementing MVVM in android, How to notify user something is wrong?

Viewed 1455

I've been struggling with MVVM pattern and android-architecture-components for last couple of months. In my last project although I did try to decouple some of my app logic but it ended with a lot of code mess. Every fragment did lots of work, from handling UI to handling network requests and ... In this new App I followed best practices for android app architecture and till now it's going well. But the thing is, I don't know how to handle network errors, and I don't get it how should I notify user if some network calls fail.

After searching and reading some blog posts I ended up with the following code (SafeApiCall and SafeApiResutl) functions to handle network requests in one place, but the thing is, All my Network Requests are done using retrofit and a NetworkDataSource class, Then I pass The NetworkDataSource and Dao to the RepositoryImpl class which is an implementation of my Repository Interface. then I pass the Repository to the viewModel, So ViewModel knows nothing about network or Dao or what so ever. So here is the problem, How can I notify user in case of any network errors ? I thought about creating a LiveData<> and pass errors to it in network layer, but in this case, Repository Must observe this, and also let's say create a LiveData in repository so viewModel observe that and so on ... But this is too much chaining, I dont like the idea of doing that. I also did take a look at the GoogleSamples Todo-MVVM-live-kotlin project, but honestly I didn't understand what is going on.


    suspend fun <T : Any> safeApiCall(call: suspend () -> Response<BasicResponse<T>>, errorMessage: String): T? {
        return when (val result = safeApiResult(call)) {
            is NetworkResult.Success -> {
                Timber.tag("safeApiCall").d("data is ${result.serverResponse.data}")
                result.serverResponse.data
            }
            is NetworkResult.Error -> {
                Timber.tag("SafeApiCall").e("$errorMessage & Exception - ${result.exception}")
                null
            }
            else -> TODO()
        }
    }


    private suspend fun <T : Any> safeApiResult(
        call: suspend () -> Response<BasicResponse<T>>
    ): NetworkResult<T> {
        return try {
            val response = call.invoke()
            Timber.tag("SafeApiResult")
                .d("response code : ${response.code()}, server value : ${response.body()!!.status}, server message: ${response.body()!!.message}")
            if (response.isSuccessful) {
                return when (ServerResponseStatus.fromValue(response.body()!!.status)) {
                    ServerResponseStatus.SUCCESS -> NetworkResult.Success(response.body()!!)
                    ServerResponseStatus.FAILED -> TODO()
                    ServerResponseStatus.UNKNOWN -> TODO()
                }
            } else {
                TODO()
            }
        } catch (exception: Exception) {
            Timber.tag("SafeApiResultFailed").e(exception)
            NetworkResult.Error(exception)
        }

    }


    sealed class NetworkResult<out T : Any> {
        data class Success<out T : Any>(val serverResponse: BasicResponse<out T>) : NetworkResult<T>()
        data class Error(val exception: Exception) : NetworkResult<Nothing>()
    }

1 Answers

You are in the correct path. I would place both methods in the NetworkDataSource. All the calls executed should call those methods to handle the errors.

The NetworkDataSource will return the NetworkResult to the repository, and it will return the result to the ViewModel.

As you say, you can use a LiveData to notify the Activity/Fragment. You can create an error data class:

data class ErrorDialog(
   val title: String, 
   val message: String
) 

And declare a LiveData<ErrorDialog> that will be observed from your view. Then when you receive notifications in your view, you can implement logic in a BaseActivity/BaseFragment to show a Dialog or Toast or whatever type of view to indicate the error.

Related