Reputation: 1246
Update: It works if I first execute a coroutine without timeout and then withTimeout. But If I execute a coroutine withTimeout first then it gives me an error. same goes for Async as well.
I am creating a demo kotlin multiplatform application where I am executing an API call with ktor. I want to have a configurable timeout function on ktor request so I am using withTimeout at coroutine level.
Here is my function call with network API.
suspend fun <T> onNetworkWithTimeOut(
url: String,
timeoutInMillis: Long,
block: suspend CoroutineScope.() -> Any): T {
return withTimeout(timeoutInMillis) {
withContext(dispatchers.io, block)
} as T
}
suspend fun <T> onNetworkWithoutTimeOut(url: String, block: suspend CoroutineScope.() -> Any): T {
return withContext(dispatchers.io, block) as T
}
Here is my AppDispatcher class for the iOSMain module.
@InternalCoroutinesApi
actual class AppDispatchersImpl : AppDispatchers {
@SharedImmutable
override val main: CoroutineDispatcher =
NsQueueDispatcher(dispatch_get_main_queue())
@SharedImmutable
override val io: CoroutineDispatcher =
NsQueueDispatcher(dispatch_get_main_queue())
internal class NsQueueDispatcher(
@SharedImmutable private val dispatchQueue: dispatch_queue_t
) : CoroutineDispatcher() {
override fun dispatch(context: CoroutineContext, block: Runnable) {
NSRunLoop.mainRunLoop().performBlock {
block.run()
}
}
}
}
so the function with the timeout gives me the following error in iOS client.
kotlin.IllegalStateException: There is no event loop. Use runBlocking { ... } to start one.
I am using 1.3.2-native-mt-1 version of the kotlin-coroutine-native. I have created a sample demo application at the following URL. https://github.com/dudhatparesh/kotlin-multiplat-platform-example
Upvotes: 19
Views: 2786
Reputation: 1332
If you want to use [withTimeout]
functions in coroutines you have to modify your Dispatcher
to implement Delay
interface. Here is an example of how this can be achieved:
@UseExperimental(InternalCoroutinesApi::class)
class UI : CoroutineDispatcher(), Delay {
override fun dispatch(context: CoroutineContext, block: Runnable) {
dispatch_async(dispatch_get_main_queue()) {
try {
block.run()
} catch (err: Throwable) {
throw err
}
}
}
@InternalCoroutinesApi
override fun scheduleResumeAfterDelay(timeMillis: Long, continuation: CancellableContinuation<Unit>) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, timeMillis * 1_000_000), dispatch_get_main_queue()) {
try {
with(continuation) {
resumeUndispatched(Unit)
}
} catch (err: Throwable) {
throw err
}
}
}
@InternalCoroutinesApi
override fun invokeOnTimeout(timeMillis: Long, block: Runnable): DisposableHandle {
val handle = object : DisposableHandle {
var disposed = false
private set
override fun dispose() {
disposed = true
}
}
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, timeMillis * 1_000_000), dispatch_get_main_queue()) {
try {
if (!handle.disposed) {
block.run()
}
} catch (err: Throwable) {
throw err
}
}
return handle
}
}
This solution can be easily modified for your needs.
More information can be found in this thread.
UPDATE
At the moment there is a version 1.3.9-native-mt
of kotlinx:kotlinx-coroutines-core
artifact which gives the ability to use Dispatchers.Main
on ios platform (it supports delay
out of the box). It even supports Dispatchers.Default
which is used for background work. You can read docs in native-mt branch. Worth noting that the version for ios should be set strictly:
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9-native-mt") {
version {
strictly('1.3.9-native-mt')
}
}
Upvotes: 4
Reputation: 490
Sometimes ios app has a different async requirement with an android app. Use this code for temporary dispatch problem
object MainLoopDispatcher: CoroutineDispatcher() {
override fun dispatch(context: CoroutineContext, block: Runnable) {
NSRunLoop.mainRunLoop().performBlock {
block.run()
}
}
}
Please see the forum for this issue: https://github.com/Kotlin/kotlinx.coroutines/issues/470
Upvotes: 0
Reputation: 10350
So, as mentioned in comment above I had similar issue but turned out that it wasn't picking up the native-mt
version due to transitive dependencies in other libraries. Added following and it's resolving now.
implementation('org.jetbrains.kotlinx:kotlinx-coroutines-core-native')
{
version {
strictly '1.3.3-native-mt'
}
}
Also note guidance in https://github.com/Kotlin/kotlinx.coroutines/blob/native-mt/kotlin-native-sharing.md
Starting to make use of this in https://github.com/joreilly/PeopleInSpace
Upvotes: 9