Skip to content

Instantly share code, notes, and snippets.

@G00fY2
Created November 3, 2022 13:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save G00fY2/b89d07963431e44edf1564b0edb55bea to your computer and use it in GitHub Desktop.
Save G00fY2/b89d07963431e44edf1564b0edb55bea to your computer and use it in GitHub Desktop.
CustomRxJava3CallAdapterFactory to transform HttpExceptions to custom error
import com.squareup.moshi.Moshi
import io.reactivex.rxjava3.core.Completable
import io.reactivex.rxjava3.core.Flowable
import io.reactivex.rxjava3.core.Maybe
import io.reactivex.rxjava3.core.Observable
import io.reactivex.rxjava3.core.Scheduler
import io.reactivex.rxjava3.core.Single
import io.reactivex.rxjava3.schedulers.Schedulers
import retrofit2.Call
import retrofit2.CallAdapter
import retrofit2.CallAdapter.Factory
import retrofit2.HttpException
import retrofit2.Response
import retrofit2.Retrofit
import retrofit2.adapter.rxjava3.RxJava3CallAdapterFactory
import timber.log.Timber
import java.lang.reflect.Type
class CustomRxJava3CallAdapterFactory(
scheduler: Scheduler = Schedulers.io(),
private val moshi: Moshi = Moshi.Builder().build()
) : Factory() {
private val original: RxJava3CallAdapterFactory = RxJava3CallAdapterFactory.createWithScheduler(scheduler)
override fun get(returnType: Type, annotations: Array<Annotation>, retrofit: Retrofit): CallAdapter<*, *> =
RxCallAdapterWrapper(original[returnType, annotations, retrofit]!!)
private inner class RxCallAdapterWrapper<R>(private val wrapped: CallAdapter<R, *>) : CallAdapter<R, Any> {
override fun responseType(): Type = wrapped.responseType()
override fun adapt(call: Call<R>): Any {
return when (val result = wrapped.adapt(call)) {
is Observable<*> -> result.onErrorResumeNext { Observable.error(transformException(it, call)) }
is Single<*> -> result.onErrorResumeNext { Single.error(transformException(it, call)) }
is Completable -> result.onErrorResumeNext { Completable.error(transformException(it, call)) }
is Maybe<*> -> result.onErrorResumeNext { Maybe.error(transformException(it, call)) }
is Flowable<*> -> result.onErrorResumeNext { Flowable.error(transformException(it, call)) }
else -> result
}
}
}
private fun transformException(throwable: Throwable, call: Call<*>): Throwable {
// We can also access the initial call.request().url() e.g. to detect which route caused an exception
if (throwable is HttpException) {
var apiError: MyCustomError? = null
val response: Response<*>? = throwable.response()
val errorBody = response?.errorBody()
if (errorBody != null) {
try {
apiError = moshi.adapter(MyCustomError::class.java).fromJson(errorBody.string())
} catch (e: Exception) {
Timber.e(e)
}
if (apiError != null) {
return apiError
}
}
}
return throwable
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment