'KMM on iOS: There is no event loop. Use runBlocking { ... } to start one
I'm trying to use coroutines
in a Kotlin Multiplatform
project. I'm not experienced in either.
I'm trying to call this function
fun startFlow {
coroutineScope.launch {
withContext(defaultDispatcher) {
myFlow.collect { next -> onNext(next) }
}
}
}
coroutineScope
on iOS
is this
val defaultScope: CoroutineScope = object : CoroutineScope {
override val coroutineContext: CoroutineContext
get() = SupervisorJob() + Dispatchers.Default
}
This is not the only call that gives me this problem, in fact all calls to coroutines
seem to fail with this error:
kotlin.IllegalStateException: There is no event loop. Use runBlocking { ... } to start one.
This is how I import the library
val commonMain by getting {
dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.3")
}
}
I'm using Kotlin
1.4.31. This problem is only present in iOS
, Android
works flawlessly.
I don't understand if I'm missing something.
Solution 1:[1]
New native concurrency model available for preview. Check out New memory model migration guide. native-mt
suffix described below will no longer be needed after the release of this functionality along with Kotlin 1.7.0.
for iOS you need to use coroutines with suffix "native-mt", more info here
so replace your import with
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2-native-mt")
Also note, that as per documentation:
When using other libraries that also depend on
kotlinx.coroutines
, such as Ktor, make sure to specify the multithreaded version ofkotlinx-coroutines
. You can do this withstrictly
:
implementation ("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2-native-mt"){
version {
strictly("1.5.2-native-mt")
}
}
Solution 2:[2]
You can also use the regular coroutines
library, but then you need to create a custom CoroutineDispatcher that posts the task on the mainRunLoop, e.g.:
object NSLooperDispatcher: CoroutineDispatcher() {
override fun dispatch(context: CoroutineContext, block: Runnable) {
NSRunLoop.mainRunLoop.performBlock {
block.run()
}
}
}
// use custom dispatcher
withContext(NSLooperDispatcher) {
myFlow.collect { next -> onNext(next) }
}
There are problems with switching threads which the native-mt
branch of coroutines is explicit about, so it may still be a good idea to use it. Otherwise, you are confined on one the main thread.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | |
Solution 2 | Stefan |