It's that time again, when a new version of Kotlin is released and a new promising Kotlin Multiplatform feature is announced. Much like my last past post I saw this new release of 1.4-M2 with a new feature and was curious how it would work in practice. This time it's the addition of:
Support for Kotlin’s suspending functions in Swift and Objective-C
Again, this is a great feature but there isn't a lot of information to go off of in the release notes. Sure it shows some sample code and mentions you can only run it in the main thread, but the implementation seems simplified and leaves out customization options. This is most likely because there are many useful features of coroutines (like Jobs and Dispatchers) that are part of kotlinx libraries and are not available in the stdlib. So with all that said, how do these suspending functions work in swift and objective-c? Will this be useful in your Kotlin Multiplatform project? Let's find out.
The Code
So first off, I've made a simple example of this new feature on Github, which can be found here. In this example there is a suspend function in the shared kmp library called helloWorld
in the class SuspendSampleHandler
. This function is called twice in the ViewController of the matching XCode project to showcase a success and failure. Let's jump into the details of how this function is called.
SuspendSampleHandler().helloWorld(showError: false, completionHandler:
{ result, error in
if let errorReal = error {
print(errorReal.localizedDescription)
}
if let resultReal = result {
print(resultReal)
}
})
Prior to 1.4-M2, suspend functions would not generate Objective-C output, and were not callable from iOS. In 1.4, suspend functions will generate Objective-C functions which take in a block with two arguments: a result and an NSError, both of which are Optional.
So in this case the result will be the successful result, and the error would be any errors or throwables that may have happened during the call. In the example code we have the function returning either a success or a failure based on the showError
boolean variable.
The Results
This method of calling suspend functions does work successfully, however a lot of this implementation still remains a mystery. Even after testing the sample and attempting to dig into the source code, I'm unable to clarify how the suspend function is called. I cannot find a way to pass in Scopes or Dispatchers, or having it return a Job. Another thing I couldn't get working are Flow
's. While they did compile successfully in XCode I was unable to get them to work during runtime. This could be a mistake on my part, but as far as I could tell there wasn't a successful way to use Flow
from iOS.
In short, I've come to these conclusions about this feature:
- The Scope and Dispatcher is unknown
- There is no way to supply a Context or Scope
- There is no way to cancel a coroutine in flight
- There is no real
Flow
support
Should you use it
The answer isn't a simple yes or no, it depends on your usage.
This new feature is a nice straight forward way to call suspend functions from iOS, so if you're going the simplified route then I'd recommend it.
On the other hand this feature is missing a lot of options for customization that you would get with kotlinx coroutines. Things like the ability to cancel jobs, set scopes, use Flows, and set contexts. With this new language-level style you don't get these features.
With all that said, I most likely won't be using this feature. We are able to create something similar to this approach while still having the options for customization and Flow
's. For an example you can look at how we implement suspend functions in our KaMPKit sample. This implementation supports a custom scope, Flows, and could easily be updated to support cancellation.
Our approach includes non-suspending functions which launches a predefined kotlinx.coroutine
scope. The results are then sent to a callback that is set when creating the object. If you want to avoid some boilerplate and have consistent interfaces, you could expose suspend functions to the compiler and wrap those calls in your own kotlinx.coroutines
scope once you’re on the Kotlin side.
Writing your own interface involves a little more work, but you gain much better control over the concurrency lifecycle.
Conclusion
It's nice that Kotlin added support for suspend functions in swift and objective-c. It provides a simplified and easy to use implementation for suspend functions, but as a trade-off you lose a lot of customization and control in the process. I personally would not use this new feature, and would instead choose a custom implementation in the common code.
Top comments (2)
The link was broken.
Please change
github.com/touchlab/KaMPKit/blob/m...
to
github.com/touchlab/KaMPKit/blob/m...
Is there any update to how these work? I've been searching around and can't find many examples on how to consume flows from Swift.