Similar to rx support I'll be adding kotlin support to allow defferred apollo operations
Hey @digitalbuddha, I'm curious about what this would look like for the most basic use-case. Would it be something like this?
suspend fun <T> ApolloCall<T>.await(): T? = suspendCancellableCoroutine { continuation ->
enqueue(
object : ApolloCall.Callback<T>() {
override fun onResponse(response: Response<T>) = continuation.resume(response.data())
override fun onCanceledError(ex: ApolloCanceledException) { continuation.cancel(ex) }
override fun onFailure(e: ApolloException) = continuation.resumeWithException(e)
override fun onNetworkError(e: ApolloNetworkException) = continuation.resumeWithException(e)
override fun onParseError(e: ApolloParseException) = continuation.resumeWithException(e)
override fun onHttpError(e: ApolloHttpException) = continuation.resumeWithException(e)
}
)
}
I would expect more something along Deferred<T> ?
Maybe
object CoroutineApollo {
fun from(ApolloCall<T> call): Deferred<T>
}
We actually need this as well, I might try to open a first PR in the coming days.
I'll be attempting something similar to what Martin suggested to keep consistent with current Rx wrappers
Actually I'm thinking something closer to call.toDeffered() suggestions for a name would be appreciated
Actually, since ApolloCall are more like Observables than Singles, I guess that translates in coroutines to Channels and not Deferred ?
A very naive first draft: => https://github.com/martinbonnin/apollo-android/commit/92c3cce56bafab8f6eb6b1631417e88df13bc469
As said above, this doesn't work as soon as the call emits multiple values.
Oh yeah that totally won't work. Channels it is.
First try at Channels:
fun <T> ApolloCall<T>.toChannel(): Channel<Response<T>> {
val channel = Channel<Response<T>>(Channel.UNLIMITED)
enqueue(object : ApolloCall.Callback<T>() {
override fun onResponse(response: Response<T>) {
channel.offer(response)
}
override fun onFailure(e: ApolloException) {
channel.close(e)
}
})
return channel
}
This seems to work pretty smoothly. I'm not 100% sure about error handling yet
Similar what to do with cancelations?
Right, cancellation is another thing.
I believe ApolloCall.enqueue cannot be canceled, is it ? That leaves us with Channel.cancel(). TBC if it has any guarantees in terms of Channel.receive() being properly canceled (sorry if not the appropriate terminology, pretty new at coroutines here and even more at channels!)
I do believe calls can be canceled...the callback has onCanceledError. Would that work?
onCanceledError defaults to onFailure so we could just forward the exception to channel.close. I just updated the code above to do this.
New version that closes the channel when the call is completed:
fun <T> ApolloCall<T>.toChannel(): Channel<Response<T>> {
val channel = Channel<Response<T>>(Channel.UNLIMITED)
enqueue(object : ApolloCall.Callback<T>() {
override fun onResponse(response: Response<T>) {
channel.offer(response)
}
override fun onFailure(e: ApolloException) {
channel.close(e)
}
override fun onStatusEvent(event: ApolloCall.StatusEvent) {
if (event == ApolloCall.StatusEvent.COMPLETED) {
channel.close()
}
}
})
return channel
}
looks good to me, want to implement with a few tests being carried over from the rx impl?
Right, I was looking for Rx tests in the rx modules but it looks like they are under apollo-integration. I'll look into that.
Damn, apollo-integration is pure Java. Is it ok to pull Kotlin there ? Or should I write tests in apollo-coroutines-support ?
Kotlin in integration is fine with me. That module doesn't ship
First drop there => https://github.com/apollographql/apollo-android/pull/1169/files
I'm not too happy with it though. As @Tyler-Wong-CK mentioned above, calls/watchers can be canceled (and sometimes must) so we would need a way to forward the channel cancellation to the call/watcher cancel().
As of the pull request, the channel and all the interceptor chain continue running. That means watchers will continue watching forever....
I asked the question on the kotlin slack and I was suggested to use channel.invokeOnClose which seems to do the job quite well. The pull request is considered working for me. Code reviews welcome :-)
Eagerly awaiting this :)
Will be in next release.
I see it's part of the latest release: https://github.com/apollographql/apollo-android/releases/tag/1.0.0-alpha5
Do we need to add a new dependency to Gradle in order to use it? Something like: implementation "com.apollographql.apollo:apollo-coroutines-support:$versions.apollo"
Edit:
Found the issue, the dependency should have been:
implementation "com.apollographql.apollo:apollo-coroutine-support:$versions.apollo"
Mmm, good point. Thanks for noticing. @sav007 if it's not too late, I made a pull request there to fix the name: https://github.com/apollographql/apollo-android/pull/1199
I went with the plural form, I guess it's more used.
What's the current status? I see PR 1199 has not been merged, but I cannot resolve either dependency atm
@timbotnik Sorry about that. The situation is a bit unstable right now. You can checkout the kotlin sample, which should work: https://github.com/martinbonnin/apollo-android/blob/3bb2e2fb07eb659c4189ebb4c66b857e6f7cd617/apollo-kotlin-sample/build.gradle#L42
What worked for me:
maven {
url 'http://dl.bintray.com/apollographql/android'
}
dependencies {
implementation "com.apollographql.apollo:apollo-coroutine-support:1.0.0-alpha5"
}
See https://github.com/apollographql/apollo-android/issues/1130 for some context about the bintray vs jcenter thing but that's still not 100% clear to me.
@martinbonnin ah, my mistake. I did not have the maven url added. Thanks
We should certainly make this more clear in the README.md or move to mavenCentral altogether
Most helpful comment
Will be in next release.