1

I have a KMM app, and there is code:

fun getWeather(callback: (WeatherInfo) -> Unit) {
        println("Start loading")
        GlobalScope.launch(ApplicationDispatcher) {
            while (true) {
                val response = httpClient.get<String>(API_URL) {
                    url.parameters.apply {
                        set("q", "Moscow")
                        set("units", "metric")
                        set("appid", weatherApiKey())
                    }
                    println(url.build())
                }
                val result = Json {
                    ignoreUnknownKeys = true
                }.decodeFromString<WeatherApiResponse>(response).main
                callback(result)

                // because ApplicationDispatcher on IOS do not support delay
                withContext(Dispatchers.Default) { delay(DELAY_TIME) }
            }
        }
    }

And if I replace withContext(Dispatchers.Default) { delay(DELAY_TIME) } with delay(DELAY_TIME) execution is never returned to while cycle and it will have only one iteration.

And ApplicationDispatcher for IOS looks like:

internal actual val ApplicationDispatcher: CoroutineDispatcher = NsQueueDispatcher(dispatch_get_main_queue())

internal class NsQueueDispatcher(
    private val dispatchQueue: dispatch_queue_t
) : CoroutineDispatcher() {
    override fun dispatch(context: CoroutineContext, block: Runnable) {
        dispatch_async(dispatchQueue) {
            block.run()
        }
    }
}

And from delay source code I can guess, that DefaultDelay should be returned and there is should be similar behaviour with/without withContext(Dispatchers.Default)

/** Returns [Delay] implementation of the given context */
internal val CoroutineContext.delay: Delay get() = get(ContinuationInterceptor) as? Delay ?: DefaultDelay

Thanks!

P.S. I got ApplicationDispatcher from ktor-samples.

1 Answers1

1

Probably ApplicationDispatcher is some old stuff, you don't need to use it anymore:

CoroutineScope(Dispatchers.Default).launch {

}

or

MainScope().launch {

}

And don't forget to use -native-mt version of coroutines, more info in this issue

Philip
  • 2,018
  • 12
  • 22
  • Thanks! For me first variant throws exceptions from Android because I change UI in the callback. But `GlobalScope.launch(Dispatchers.Main)` works well for both Android and IOS – Alexandr Izmaylov Mar 30 '21 at 17:15
  • you use first variant to run on a background queue, and second one for main queue. I think `GlobalScope.launch(Dispatchers.Main)` should be equivalent to the second one. So if you're doing some huge calculations run those on the inside first one, and when you finish call ui updates inside second one, to come back to UI thread – Philip Mar 30 '21 at 20:53
  • Generally you should avoid GlobalScope, as you lose the benefits of structured concurrency. Alternatively you can save a reference to MainScope() in your ViewModel, or similar - https://kotlinlang.org/docs/coroutines-guide.html#additional-references – enyciaa Apr 04 '21 at 18:06
  • I found a lot of interesting in [structured-concurrency](https://kotlinlang.org/docs/coroutines-basics.html#structured-concurrency), thanks @enyciaa ! – Philip Apr 05 '21 at 08:28