How to update UI in coroutines in Kotlin 1.3

If you're using coroutines-android you can use Dispatchers.Main
(gradle dependency is implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.0.0")

network.getRoute(request,
        success = {
            withContext(Dispatchers.Main) {
                // update UI here
            }
        },
        fail = {
            // handle the exception
        }) 

To answer your immediate question, you must simply launch the coroutine in the correct context:

val call = ApiClient.getInterface().getRoute(request.getURL())
GlobalScope.launch(Dispatchers.Main) {
    try {
        success?.invoke(call.await())
    } catch (t: Throwable) {
        fail?.invoke(t)
    }
}

However, this would be just the tip of the iceberg because your approach is the wrong way to use coroutines. Their key benefit is avoiding callbacks, but you're re-introducing them. You are also infringing on the structured concurrency best practice by using the GlobalScope which is not meant for production use.

Apparently you already have an async API that gives you a Deferred<RoutesResponse> that you can await on. The way to use it is as follows:

scope.launch {
    val resp = ApiClient.getInterface().getRoute(request.getURL()).await()
    updateGui(resp)
}

You may be distressed by the fact that I'm suggesting to have a launch block in every GUI callback where you must execute suspendable code, but that is actually the recommended way to use this feature. It is in a strict parallel to writing Thread { ... my code ... }.start() because the contents of your launch block will run concurrently to the code outside it.

The above syntax assumes you have a scope variable ready which implements CoroutineScope. For example, it can be your Activity:

class MyActivity : AppCompatActivity(), CoroutineScope by MainScope {

    override fun onDestroy() {
        super.onDestroy()
        cancel()
    }
}

The MainScope delegate sets the default coroutine dispatcher to Dispatchers.Main. This allows you to use the plain launch { ... } syntax.


private var viewModelJob = Job()
private val uiScope = CoroutineScope(Dispatchers.Main + viewModelJob)

uiScope.launch {
            withContext(Dispatchers.IO) {
                //Do background tasks...
                withContext(Dispatchers.Main){
                    //Update UI
                }
            }
        }