ktorio / ktor

Framework for quickly creating connected applications in Kotlin with minimal effort
https://ktor.io
Apache License 2.0
12.97k stars 1.06k forks source link

Ktor-client forcing a dependency on coroutines in the multiplatform world #721

Closed ldavin closed 5 years ago

ldavin commented 5 years ago

Hi there,

My question is about the ktor-client libs, and especially the common multiplatform API. For some context I'm currently diving into the kotlin-multiplatform world, and I'm quite amazed that you already provide a multiplatform HTTP client.

My problem is that the client API, in its current form, leaks a dependency to kotlin coroutines. As it is not possible to cleanly launch a coroutine from the kotlin-multiplaform code (without messing with the iOS main thread for example) or to wrap it in a runBlocking block, I'm not able to "hide" the coroutines dependency in this common code. I thus end up exposing suspendable methods to my iOS and Android apps. Anecdotally it is not really a problem for the Android side, but it's not the same deal with the iOS side :)

I would like to have your intake on those three questions:

  1. A first, very prosaic question: Do you see a way to limit the coroutines related stuff to the method that makes the call to the HttpClient.call() (ie by making it a classic thread-blocking call) ? I'm not yet very fluent with coroutines and I may have missed a way.

  2. A second, very prosaic question: Would it be possible to expose at least one call method on the HttpClient API that would not be suspendable ? I'm not familiar with the internals of the lib, and I fear your answer might be no because you're opinionated :) or because you rely on it for some features (eg to control concurrent calls?).

  3. On a more conceptual approach: Don't you believe it's the "end-client" responsibility (meaning the jvm, ios, native or even js side) to choose how it wants to handle its "asynchronoucity" ? That would allow clients to call the shared-logic "black-box" as they see fit (for Android with executors, coroutines, RX or whatever; for iOS with grand-central dispatch…)

Thank you all for the great work that is put into this lib. Looking forward to reading your thoughts

e5l commented 5 years ago

Hi @ldavin. Thanks for the report.

1,2: Basically, the answer is no. You could use launch in common code, and we provide common dispatchers later(also with primitives to convert method in blocking).

For now, you could do it in your application API with presenters concept:

example:

interface SessionPresenter {
    fun onSessionUpdate(session: Session)
}

For more advanced example you could look here: https://github.com/JetBrains/kotlinconf-app More abstract explanation: https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93presenter

  1. It depends on the app architecture. With current API you could write sequential code without blocking the thread:
    val user = client.get<UserInfo>(...)
    val friends = client.get<List<Friend>>(..., body = user.friendIds)

And you have to use async or launch only when you don't need the result right now.

e5l commented 5 years ago

If you have any questions feel free to reopen the issue or find me in slack.kotlinlang.org in the #ktor channel.