Hi! :wave: A bit surprised to learn that Coroutin...
# multiplatform
g
Hi! đź‘‹ A bit surprised to learn that Coroutines (suspend methods) are only half-integrated in KMP. Looks like natives have a completionHandler but nothing to handle the cancellation, so objectiveC code cannot be cooperative to cancellation without writing our own code? Same for Js/Typescript where the usage of Promise don't allow cancellation afaik. The Kotlin code in common looks like if coroutines were ready but actually cancelling only works for JVM/Android ? Am I missing something here? Or is it something planned in a next release?
n
Hi! While the current translation of coroutine functions to Objective-C is a bit lacking in places, and I don’t know whether support for these kinds of things is planned to be added to Kotlin/Native itself, the community has attempted to tackle this issue in a number of ways, which might interest you. I have written an article on how we do it with
Flow<T>
where I work, titled Using Kotlin Flow in Swift, but the same idea can trivially be modified (and so have we) to support
suspend
functions. Additionally, there’s rickclephas/KMP-NativeCoroutines, which uses a Kotlin Compiler Plugin to generate these
Native
wrappers, and includes a library to trivially consume them in Swift, regardless of if you’re using
async/await
, Combine or RxSwift.
👍 1
👍🏻 1
t
Cancelations are technically an error - so I would expect that to come though on the error side of the completion
I would expect ditto for JS/Promises
g
Thanks @Nicklas Jensen for the links. Glad you're agreeing the support "is a bit lacking in places". I'm currently focused on
suspend
method as I'm not sure how to export Channel/Flow to JS yet, also I'm trying to provide a nice XCFramework without adding ObjectiveC code myself, and this may be the bad approach for this problem. My team exclusively consumes ObjectiveC so not sure if I could do that as nicely as you do on Swift. I'm evaluating KMP-NativeCoroutines, but looks a bit overkill to just handle cancellation on suspend method (at least, I was not expecting to add such a nice tool for this basic need).
👍🏻 1
Cancelations are technically an error - so I would expect that to come though on the error side of the completion
@Tim Oltjenbruns My problem is to cancel JS or ObjC code from Kotlin. So I need to provide them something that informs the non-coroutines code when it should cancel its work. I don't think Error/Exception can help in this case. Also CancellationException is quite unique for coroutine scope as it can have an impact on other jobs and it's currently not in Typescript signatures. I can add it myself but it means that in Kotlin code I'll have to try/catch all calls, searching for my own TS CancellationException, and map that back to Coroutines ones. That's just error-prone and easy to forget for what I consider basic coroutine contract, am I wrong?
t
The promise standard does not have a cancel option, and ObjC completions typically only have success/error. What would your ideal cancelation look like from the ObjC or JS side?
Also, I’m not sure I understand whether you need a coroutine to tell JS/ObjC to cancel? Or do you need JS/ObjC to tell a coroutine to cancel?
It sounds like the former, but the solution you’re running into with the try/catch sounds like the latter.
g
The promise standard does not have a cancel option, and ObjC completions typically only have success/error.
What would your ideal cancelation look like from the ObjC or JS side?
It could look like Kotlin actually. The big difference is that inside a suspend function in Kotlin, I can get coroutineContext.ensureActive() or isActive.
Copy code
suspend fun foo() = repeat(10000) { i ->
  longWorkAtIndex(i)
  coroutineContext.ensureActive()
}
In this Kotlin code, the ensureActive will stop the repeat by throwing a CancellationException. Since KMP only generates completionHandler, I need to pass myself an object that will allow me to change the isActive boolean on this object or throw an exception to define a similar code in objC/typescript (given the objC/typescript use this object to ensure cooperative cancellation).
Also, I’m not sure I understand whether you need a coroutine to tell JS/ObjC to cancel? Or do you need JS/ObjC to tell a coroutine to cancel?
To be honest, I'll need both. Right now it looks like cancelling a Promise written on Typescript from Kotlin code requires this additional object. I feel like the opposite direction will be very similar (passing an object that will provide a cancel() method). How do you deal with cooperative cancellation of typescript code?