From d406a0a5f315dff906f4bbfc1543114dc43a4ead Mon Sep 17 00:00:00 2001 From: Waheed Nazir Date: Fri, 21 Jan 2022 11:07:49 +0500 Subject: [PATCH] API Error Handling (Connection, Request Timeout and Cancel) at Network layer done. Propagated error status back to view via live data. --- app/src/main/AndroidManifest.xml | 2 ++ .../api/network/LiveDataCallAdapter.kt | 22 ++++++++++--------- .../api/network/NetworkAndDBBoundResource.kt | 16 +++++++++++--- .../api/network/NetworkBoundResource.kt | 7 +++++- .../mvvm/repository/api/network/Resource.kt | 9 ++++---- .../api/network/RetrofitExtensions.kt | 6 ++--- .../com/kotlin/mvvm/utils/ApiErrorHandling.kt | 2 +- 7 files changed, 42 insertions(+), 22 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index b159076..cd4aa02 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -18,6 +18,7 @@ tools:ignore="AllowBackup,GoogleAppIndexingWarning"> @@ -28,6 +29,7 @@ diff --git a/app/src/main/java/com/kotlin/mvvm/repository/api/network/LiveDataCallAdapter.kt b/app/src/main/java/com/kotlin/mvvm/repository/api/network/LiveDataCallAdapter.kt index 7127afa..d908113 100644 --- a/app/src/main/java/com/kotlin/mvvm/repository/api/network/LiveDataCallAdapter.kt +++ b/app/src/main/java/com/kotlin/mvvm/repository/api/network/LiveDataCallAdapter.kt @@ -2,10 +2,8 @@ package com.kotlin.mvvm.repository.api.network import androidx.lifecycle.LiveData -import retrofit2.Call -import retrofit2.CallAdapter -import retrofit2.Callback -import retrofit2.Response +import com.kotlin.mvvm.utils.ApiErrorHandling +import retrofit2.* import java.lang.reflect.Type import java.util.concurrent.atomic.AtomicBoolean @@ -21,14 +19,11 @@ import java.util.concurrent.atomic.AtomicBoolean class LiveDataCallAdapter(private val responseType: Type) : CallAdapter>> { - override fun responseType(): Type { - return responseType - } + override fun responseType(): Type = responseType override fun adapt(call: Call): LiveData> { return object : LiveData>() { - internal var started = AtomicBoolean(false) - + var started = AtomicBoolean(false) override fun onActive() { super.onActive() if (started.compareAndSet(false, true)) { @@ -38,7 +33,14 @@ class LiveDataCallAdapter(private val responseType: Type) : } override fun onFailure(call: Call, throwable: Throwable) { - postValue(Resource.error(throwable.message)) + postValue( + Resource.error( + ApiErrorHandling.error( + throwable, + call.isCanceled + ), if (throwable is HttpException) throwable.code() else 0 + ) + ) } }) } diff --git a/app/src/main/java/com/kotlin/mvvm/repository/api/network/NetworkAndDBBoundResource.kt b/app/src/main/java/com/kotlin/mvvm/repository/api/network/NetworkAndDBBoundResource.kt index 3052561..50edd34 100644 --- a/app/src/main/java/com/kotlin/mvvm/repository/api/network/NetworkAndDBBoundResource.kt +++ b/app/src/main/java/com/kotlin/mvvm/repository/api/network/NetworkAndDBBoundResource.kt @@ -40,7 +40,7 @@ constructor(private val appExecutors: AppExecutors) { shouldFetch(data) -> fetchFromNetwork(dbSource) else -> { result.addSource(dbSource) { newData -> - setValue(Resource.success(newData)) + setValue(Resource.success(newData,result.value?.retrofitAPICode ?: 0)) } } } @@ -75,14 +75,24 @@ constructor(private val appExecutors: AppExecutors) { // otherwise we will get immediately last cached value, // which may not be updated with latest results received from network. result.addSource(loadFromDb()) { newData -> - setValue(Resource.success(newData)) + setValue( + Resource.success( + newData, + result.value?.retrofitAPICode ?: 0 + ) + ) } } } } else -> { result.addSource(dbSource) { - result.setValue(Resource.error(errorMessage)) + result.setValue( + Resource.error( + errorMessage, + result.value?.retrofitAPICode ?: 0 + ) + ) } } } diff --git a/app/src/main/java/com/kotlin/mvvm/repository/api/network/NetworkBoundResource.kt b/app/src/main/java/com/kotlin/mvvm/repository/api/network/NetworkBoundResource.kt index a5ae0bf..3523209 100644 --- a/app/src/main/java/com/kotlin/mvvm/repository/api/network/NetworkBoundResource.kt +++ b/app/src/main/java/com/kotlin/mvvm/repository/api/network/NetworkBoundResource.kt @@ -43,7 +43,12 @@ abstract class NetworkResource @MainThread constructor() { response?.apply { when { status.isSuccessful() -> setValue(this) - else -> setValue(Resource.error(errorMessage)) + else -> setValue( + Resource.error( + errorMessage, + result.value?.retrofitAPICode ?: 0 + ) + ) } } } diff --git a/app/src/main/java/com/kotlin/mvvm/repository/api/network/Resource.kt b/app/src/main/java/com/kotlin/mvvm/repository/api/network/Resource.kt index 82cc743..e4bbfff 100644 --- a/app/src/main/java/com/kotlin/mvvm/repository/api/network/Resource.kt +++ b/app/src/main/java/com/kotlin/mvvm/repository/api/network/Resource.kt @@ -12,6 +12,7 @@ package com.kotlin.mvvm.repository.api.network data class Resource( var status: Status, var data: ResultType? = null, + var retrofitAPICode: Int = 0, var errorMessage: String? = null ) { @@ -22,8 +23,8 @@ data class Resource( * last value is null so passing it optionally * */ - fun success(data: ResultType): Resource = - Resource(Status.SUCCESS, data) + fun success(data: ResultType, retrofitAPICode: Int): Resource = + Resource(Status.SUCCESS, data, retrofitAPICode = retrofitAPICode) /** * Creates [Resource] object with `LOADING` status to notify @@ -37,8 +38,8 @@ data class Resource( * Creates [Resource] object with `ERROR` status and [message]. * Returning object of Resource(Status.ERROR, errorMessage = message) */ - fun error(message: String?): Resource = - Resource(Status.ERROR, errorMessage = message) + fun error(message: String?, retrofitAPICode: Int): Resource = + Resource(Status.ERROR, errorMessage = message, retrofitAPICode = retrofitAPICode) } } \ No newline at end of file diff --git a/app/src/main/java/com/kotlin/mvvm/repository/api/network/RetrofitExtensions.kt b/app/src/main/java/com/kotlin/mvvm/repository/api/network/RetrofitExtensions.kt index bc5b746..851cb8f 100644 --- a/app/src/main/java/com/kotlin/mvvm/repository/api/network/RetrofitExtensions.kt +++ b/app/src/main/java/com/kotlin/mvvm/repository/api/network/RetrofitExtensions.kt @@ -22,10 +22,10 @@ fun Response.toResource(): Resource { isSuccessful -> { val body = body() when { - body != null -> Resource.success(body) - else -> Resource.error(error) + body != null -> Resource.success(body, this.code()) + else -> Resource.error(error, this.code()) } } - else -> Resource.error(error) + else -> Resource.error(error, this.code()) } } \ No newline at end of file diff --git a/app/src/main/java/com/kotlin/mvvm/utils/ApiErrorHandling.kt b/app/src/main/java/com/kotlin/mvvm/utils/ApiErrorHandling.kt index fccd204..4760bed 100644 --- a/app/src/main/java/com/kotlin/mvvm/utils/ApiErrorHandling.kt +++ b/app/src/main/java/com/kotlin/mvvm/utils/ApiErrorHandling.kt @@ -6,7 +6,7 @@ import java.lang.Exception import java.net.SocketTimeoutException -object CallAdapterErrorHandling { +object ApiErrorHandling { private const val ERROR_SOMETHING_WENT_WRONG = "Something went wrong." private const val ERROR_REQUEST_CANCELLED = "Request cancelled."