Updating Views asynchronously

After calling books = task.await() you are outside UI thread. It is because you use CoroutineScope(Dispatchers.Default). Change it to Dispatchers.Main:

fun loadData() = CoroutineScope(Dispatchers.Main).launch {
        loadingIndicator.visibility = View.VISIBLE
        val task = async(Dispatchers.IO) {
            get_top_books()
        }
        books = task.await()
        viewAdapter.notifyDataSetChanged()
        loadingIndicator.visibility = View.INVISIBLE
    }

After calling books = task.await() you are outside UI thread. You should run all UI related code in the main thread. To do this you can use Dispatchers.Main.

CoroutineScope(Dispatchers.Main).launch {
    viewAdapter.notifyDataSetChanged()
    loadingIndicator.visibility = View.INVISIBLE
}

Or using Handler

Handler(Looper.getMainLooper()).post { 
    viewAdapter.notifyDataSetChanged()
    loadingIndicator.visibility = View.INVISIBLE
}

Or you can use Activty instance to call runOnUiThread method.

activity!!.runOnUiThread {
    viewAdapter.notifyDataSetChanged()
    loadingIndicator.visibility = View.INVISIBLE
}

Changing the Dispatchers.Default to Dispatchers.Main and upgrading my version of kotlinx-coroutines-android to 1.1.1 did the trick.

Changing

val task = async(Dispatchers.IO) {
    get_top_books()
}
books = task.await()

to

books = withContext(Dispatchers.IO) {
    get_top_books()
}

is also a bit more elegant. Thanks to everyone who responded especially @DominicFischer who had the idea to check my dependencies.