Idiomatic way to define a loading indicator API for coroutines


#1

I created a utility function that provides means to wrap some coroutine code, and shows a loading indicator while the wrapped code is running. The function can be used as in the following example:

launch (UI) {
    withLoadingIndicator(LoadingDialog("I am loading stuff")) {
        val result = someService.getResult().await()
        updateView(result)
    }
}

After having read more about coroutines I am now wondering whether there is a more idiomatic way of defining this API by making use of a custom coroutine context. What I have in mind would be used as follows:

launch (UI + LoadingDialog("I am loading stuff")) {
    val result = someService.getResult().await()
    updateView(result)
}

Would something like this be possible, and if so is it a feasible use case for a custom coroutine context?

A third option would be to define my own coroutine builder, which I would then use as follows:

launch (UI, loadingIndicator = LoadingDialog("I am loading stuff")) {
    val result = someService.getResult().await()
    updateView(result)
}

Which is the most idiomatic approach?


#2

I am not sure that there is some “true” way to do things with kotlin. The language was created to be practical. So the most convenient way is the most “idiomatic”.
In my opinion, the third way is the most readable. If you are sure that you have limited number of possible parameters, then there are not reasons to introduce builder pattern. Overriding operators should be limited wherever it is possible (it is known since C++) and used only in cases where operator meaning is obvious. Otherwise, it produces a lot of confusion.


#3

As far as I understand, the + operator is the established way of combining coroutine contexts. I did not define this operator, it is already available. However, I am not sure whether the second approach could be seen as a misuse of coroutine contexts.

Also note that the first approach does not utilize the builder pattern. The withLoadingIndicator function does not have a receiver. It is just a plain function that accepts 2 arguments, a loading indicator and a block of code. Its implementation is very plain, too.

Currently I am liking the third approach the most, too. I might need to combine it with the first approach, whenever the loading indicator needs to wrap only some fragment of the coroutine code.