Lesson 09 Network Calls ‐ Error handling - FranGarc/LearningPath GitHub Wiki
API calls won't always be successful. User's internet access can be faulty. The backend could be down. We can't just code a happy path, if we just expect the list of pokemon to arrive and we get instead a 404 error code, or a 500 error code, the app will either have an erratic behaviour or will downright crash. Therefore, we need to articulate an error handling strategy, a way to let the user know there was some issue with the communication instead of... just let the app blank/crash.
We'll need to wrap the different responses in a way we can convey either the success or the failure with the approppriate information in each case.
Also, for the GUI portion, we'll need the stateflow to be able to inform not only of the retrieved data but also of the different states that may be happening (like Loading, Success, or Failure) and show the relevant information to the user.
Create a Wrapper for the responses. It will be a sealed class with two child data classes: one for failures which will have an optional message and an optional throwable; and one for success, that will take the response class as a generic type value parameter.
- In the data layer, we'll consider
- a success if the communication with the backend was successful (code 200/ isSuccessful), independent of the response payload.
- a failure if the communication ended otherwise (code 30x, 40x, 50x).
- a failure if an exception was thrown
- In the domain layer, we'll consider
- a success if the data layer returned a success result AND the payload is ok (it passes any checks like not being null, etc)
- a failure if the data layer returned a success result but the payload is NOT ok.
- a failure if the data layer returned a failure result.
Create a Wrapper for the states. It will be a sealed class with child classes for Loading, Success (which will take the relevant data as a generic type parameter), Error (with an error code or message), and an initial state (when it's doing none of the above).
This class will be used in the ViewModel and where the stateflow is collected.
- The initial state will be the default for the stateflow.
- When the data request starts, the state will become Loading ( Extra: Display a Spinner while the request is Loading, and hide it when it's a Success or an Error)
- When the data request finishes, the state will become Success or Error (have the UI show the user what's going on), depending on the Result.
- Success state will contain the corresponding data and show the "normal" UI
- Error state will show an UI that tells the user of the error. Options are Dialog, Bottomsheet
- After Success/Error have been dealt with, reset the state to initial
Add a "Loading Indicator" for the Loading states.
- First of all, set your testing device (or your emulator) in Airplane Mode to simulate a connectivity issue, and then use the app. You'll notice it crashes. Research your retrofit code and see if any of those methods may throw an exception of any kind. Then set up some logs so you learn what's going on: when the exception is being thrown, and how to catch it to avoid it crashing the whole app.
Wrapper result class example
sealed class Result<out T> {
data class Success<out S>(val value: S) : Result<S>()
data class Failure(
val message: String?,
val throwable: Throwable? = null
) : Result<Nothing>()
}
Wrapper example of usage:
val response: Response = api.getResponse()
// if the call was successful
val result: Result<Response> = Result.Success(value = response)
// if there was an issue with the call - i.e. code 200 (OK) but response null
val result: Result<Response> = Result.Failure(message = "response was null")
// if there was an exception
try{
val response: Response = api.getResponse()}
catch(e: Exception){
val result: Result<Response> = Result.Failure(message = "there was an exception", throwable = e)
}
Wrapper state class example
sealed class StateWrapper<out T> {
object Nothing : StateWrapper<kotlin.Nothing>()
object Loading : StateWrapper<kotlin.Nothing>()
data class Success<out S>(val value: S) : StateWrapper<S>()
data class Error(val message: String) : StateWrapper<kotlin.Nothing>()
}
- Retrofit
- Retrofit.Builder
- Retrofit Call: execute, enqueue, cancel
- GsonConverterFactory
- OkHttp
- Result