Skip to content

Instantly share code, notes, and snippets.

@Groostav
Created January 16, 2019 19:38
Show Gist options
  • Save Groostav/4417c0bf400dadf70a5e5bb61b0dcb36 to your computer and use it in GitHub Desktop.
Save Groostav/4417c0bf400dadf70a5e5bb61b0dcb36 to your computer and use it in GitHub Desktop.
using coroutines instead of RX
Retrofit.addCallAdapterFactory(ErrorCheckingCallAdapterFactory())
class ErrorCheckingCallAdapterFactory() : CallAdapter.Factory() {
override fun get(returnType: Type, annotations: Array<Annotation>, retrofit: Retrofit): CallAdapter<*, *>? {
if (CallAdapter.Factory.getRawType(returnType) != Deferred::class.java) {
return null // Ignore ~~non-Observable~~ non-coroutine types.
}
val nextAdapter: CallAdapter<Deferred<???>, ???> = retrofit.nextCallAdapter(this, returnType, annotations) as Deferred<???>
return launchAsCallAdapter<???, ???> { next ->
val upstreamResult = nextAdapter.adapt(next).await()
if (upstreamResult is BaseResponse) {
if (!upstreamResult.isSuccessful()) {
throw MemErrorException(upstreamResult.result?.retCode, upstreamResult.result?.retMsg, upstreamResult)
}
}
upstreamResult
}
}
}
// I would hope somebody smarter with retrofit than me has already written launchAsCallAdaptor,
// and they probably gave it a better name (maybe just `adapt { next -> }`??), but heres my shot:
fun <TIn, TSth, TOut> CoroutineScope.launchAsCallAdapter(block: suspend (TIn) -> TOut): CallAdapter<TSth, TOut> {
val result = object: CallAdaptor<Deferred<TOut>, TIn> {
override fun adapt(call: Call<TIn>): Deferred<TOut> = async {
block(call.???)
// so, does retrofit have a special case for Single.error() such that it will unbox that into an error response for you?
// if thats the case, you'd need the same special case code for a failed deferred,
// if theres an interface that can be optionally implemented to supply that same metadata, you could implement that here.
// ill take a look a little later today...
}
}
}
Retrofit.addCallAdapterFactory(ErrorCheckingCallAdapterFactory())
class ErrorCheckingCallAdapterFactory() : CallAdapter.Factory() {
override fun get(returnType: Type, annotations: Array<Annotation>, retrofit: Retrofit): CallAdapter<*, *>? {
if (CallAdapter.Factory.getRawType(returnType) != Single::class.java) {
return null // Ignore non-Observable types.
}
// Look up the next call adapter which would otherwise be used if this one was not present.
val delegate = retrofit.nextCallAdapter(this, returnType,
annotations) as CallAdapter<Any, Single<*>>
return object : CallAdapter<Any, Any> {
override fun adapt(call: Call<Any>): Any {
// Delegate to get the normal Observable...
val startTime = System.currentTimeMillis()
val o: Single<*> = delegate.adapt(call)
// ...and change it to send notifications to the observer on the specified scheduler.
return o.compose(errorCheckTransformer<Any>(startTime))
}
override fun responseType(): Type {
return delegate.responseType()
}
}
}
fun <T> errorCheckTransformer(startTime: Long): SingleTransformer<T, T> {
return SingleTransformer { observable ->
observable.flatMap { response ->
if (response is BaseResponse) {
if (!response.isSuccessful()) {
return@flatMap Single.error<T>(MemErrorException(response.result?.retCode, response.result?.retMsg, response))
}
}
Single.just(response)
}
}
}
}
@Groostav
Copy link
Author

Sorry the types are hard to figure out without an IDE's assistence, and I'll look to see if i can find retrofit special-casing Single.error in a bit.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment