-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ktor client iOS hangs forever in runBlocking #678
Comments
Why do you use |
Read Guide to UI programming with coroutines first |
Unfortunately |
I want to block the main loop. I am using this in the context of a testing library that's run in a separate process from the application. Specifically this is an iOS XCUITest. My confusion is that the code doesn't work. I am not finding any possible way to fix (even using a background thread). |
I tried that solution as well, using the custom |
Well, running coroutines on background threads are not yet supported. Using runBlocking is quite dangerous. And for sure you can't use runBlocking with MainLoopDispatcher and you are already on main loop. Are you sure you actually need blocking? |
Yeah, I'm replacing an existing blocking networking client (based on gRPC Swift). This is all test code that runs in a different process from the app. It's not related to production at all. |
UPD: looks like ios client is running on unconfined dispatcher |
I pushed some fixes: anything else obviously wrong? |
However it makes no difference. ios client is configured to schedule callback to the main loop that is blocked. This is why it doesn't work. So there is no way to use runBlocking with ktor client. |
Why can't you simply make |
Your fix looks too verbose, you can use
|
Thanks for clarification.
How do I call a Kotlin suspend function in a blocking way from Swift? The beauty of runBlocking is the calls are synchronous from the consumer perspective. |
When defining suspend fun getGitHub(): HttpClientCall {
return HttpClient().use { it.call("https://www.github.com") }
} I guess iOS is not working yet? |
|
Yes, for now there is no way to call suspend functions from swift. However you can invoke swift functions from kotlin coroutines. ktor client works on ios but you can't mix it with runBlocking and all coroutines need to be launched on a customized coroutine dispatcher that dispatch everything on the main loop The simplified example: common.kt interface MyAppView {
fun onDataLoadComplete(text: String)
}
class MyPresenter(private val view: MyAppView) {
fun load() {
launch {
val result = client.get<String>("http://....")
view.onDataLoadComplete(result)
}
}
} MyAppViewIos.swift class MyAppViewIos: .... , MyAppView {
lazy var presenter: MyPresenter = {
MyPresenter(view: self)
}
func somethingClicked() {
presenter.load()
}
func onDataLoadComplete(text: String) { // invoked from presenter's coroutine
// show result
}
} See complete example here: https://github.com/JetBrains/kotlinconf-app Relevant files are: view interface (Kotlin): https://github.com/JetBrains/kotlinconf-app/blob/master/common/src/commonMain/kotlin/org/jetbrains/kotlinconf/presentation/SessionListView.kt presenter (Kotlin) view implementation (Swift) UI dispatcher implementation |
I think a synchronous REST API defined in Kotlin and called by Swift is blocked then. Probably this will be possible once multithreaded support lands. The callback approach is interesting. I see that as a good fit for writing apps. Thanks for all the info. |
The async callback code works. With the callback API, it doesn't seem possible to wait for operations to finish. I tried using an operation queue and that crashed. |
I defined a custom bootstraponline/run_blocking@a795319#diff-c1a933ca71f0c706f65401b240f8c806 // Expectation.kt
package example
import platform.Foundation.NSDate
import platform.Foundation.NSRunLoop
import platform.Foundation.addTimeInterval
import platform.Foundation.runUntilDate
class Expectation<T> {
private var waiting = true
private var result: T? = null
fun fulfill(result: T?) {
waiting = false
this.result = result
}
fun wait(): T? {
while (waiting) {
advanceRunLoop()
}
return result
}
}
private fun advanceRunLoop() {
val date = NSDate().addTimeInterval(1.0) as NSDate
NSRunLoop.mainRunLoop.runUntilDate(date)
} // RunBlocking.kt
package example
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.Runnable
import kotlinx.coroutines.launch
import platform.Foundation.NSRunLoop
import platform.Foundation.performBlock
import kotlin.coroutines.CoroutineContext
actual fun <T> runBlocking(block: suspend () -> T): T {
val expectation = Expectation<T>()
GlobalScope.launch(MainRunLoopDispatcher) {
expectation.fulfill(block.invoke())
}
return expectation.wait() ?: throw RuntimeException("runBlocking failed")
}
private object MainRunLoopDispatcher : CoroutineDispatcher() {
override fun dispatch(context: CoroutineContext, block: Runnable) {
NSRunLoop.mainRunLoop().performBlock {
block.run()
}
}
} |
Thanks for the idea :) |
Freezes anyway as described here ktorio/ktor#678 (comment) * Upgrade kotlin coroutines to 1.3.3 * Remove default comments from iOS sources * Fix package name clash between common and android
Freezes anyway as described here ktorio/ktor#678 (comment) * Upgrade kotlin coroutines to 1.3.3 * Remove default comments from iOS sources * Fix package name clash between common and android
commonMain
iosMain
Xcode hangs forever when invoking the method:
If I delete
HttpClient
inrunBlocking
then the method successfully returns a value on iOS.Versions:
Is it a known issue that ktor iOS doesn't work in runBlocking? Is this even a ktor issue or a coroutines issue? Kotlin/kotlinx.coroutines#462
The text was updated successfully, but these errors were encountered: