What is the state-of-the-art solution for concurrency when exposing KMM to iOS? I was thinking corou...
m
What is the state-of-the-art solution for concurrency when exposing KMM to iOS? I was thinking coroutines native-mt + some wrapper which can be transformed to Swift Combine but I'm not seeing a lot of this on github tbh and there's some boilerplate to glue it all together.
j
Decompose project has a Observable which is compatible, easily, with iOS
πŸ‘ 1
m
j
Yeah
I think Flow is not easy to use on iOS side yet. I don't know if they are waiting until Swift code generation which will not arrive with Kotlin 1.5.
j
It's just proof of concept level at this stage but fwiw I created Combine Publisher that wraps a Kotlin flow coming from shared kmp code....described in https://johnoreilly.dev/posts/kotlinmultiplatform-swift-combine_publisher-flow/
πŸ‘ 2
m
@Javier what do you mean by swift code generation? Are there any plans for kotlin native support for swift maybe? Because then we might build Combine wrappers directly in the Kotlin code and that would be awesome.
Not sure what this symbol means, but I guess it's paused? 😞
j
Currently kotlin is only generating objective c code
I think that paused item is related to an option to generate swift code
Generics with kotlin-obj-c are awful because you need to cast later in Swift
m
@John O'Reilly I use a similar approach but I'm having some second thoughts, mainly because I'm looking for an elegant yet flexible way of handling the CoroutineScope inside swift code. Also there's no way to make an extensions for a generic class, because of lack of swift interoperability.
☝️ 1
in yoiur example you are using MainScope() for iosScope, so you assume that you will always launch your coroutine from the main thread, right?
j
In this particular case I am.....with typically calling main-safe methods from ktor
but original example in https://dev.to/touchlab/working-with-kotlin-coroutines-and-rxswift-24fa @russhwolf uses background thread and shows various
freeze
methods that need to be called when doing that
m
it seems like that's gonna be the most usual case so I'm thinking of using the MainScope() by default to avoid explicitly accessing the scope from swift code.
the original article has been writter before coroutine native-mt I think
but I'm starting my work from main thread in swift and then making network calls on Dispatchers.Default with ktor without any issues.
r
My article does take native-mt into account, but it assumes you might switch threads from the Swift side and so does extra work to make sure that's safe. If your Swift code is all main-threaded it will simplify things.
πŸ‘ 2
n
@Michal Klimczak our approach was to leave the CoroutineScope handling to the Kotlin world (a simple
dispose()
) and for the generics part expose some convenience lambdas. It doesn’t scale as well, but we wanted something more seamless on the swift side. It might depend on your use-case/architecture as well, but you can find an example here
m
@russhwolf I used your approach and have all the freezes where you had them, but I tried launching from different threads in swift and it crashes (with the IncorectDereferrence of course) when I use a different thread on the swift side and on kotlin launch side. I.e. when I launch from
RunLoop.main
and then use
Dispatchers.Main
in SuspendWrapper it's all good. I also succeeded with using
Dispatchers.Default
and something specific for the Combine side, but can't remember. Will perform some tests and get back to you.
@nrobi this is a shared viewmodel approach which will not work for me, but it gives m another angle, thanks πŸ™‚
@russhwolf okay, so the problem is not with passing the data through the suspendwrapper, but the scope, I think...
Copy code
.receive(on: DispatchQueue.global())
            .flatMap { username in
                createPublisher(
                    scope: coroutineScope,
                    wrapper: loadUserUseCase.loadUser(username: username)
                )
When I do this I get
Copy code
Uncaught Kotlin exception: kotlin.native.IncorrectDereferenceException: illegal attempt to access non-shared kotlinx.coroutines.internal.ContextScope@4f9348 from other thread
I guess this is just not something that I should do - I mean the
createPublisher
has to be done on the original thread that touched the
coroutineScope
param, otherwise I'm in trouble, right? Or am I reading it all wrong?
r
That error is saying that your
coroutineScope
is being accessed across threads and needs to be frozen.
πŸ’― 1
a
You may also want to check an alternative library: https://github.com/badoo/Reaktive It brings absolute multi-threading freedom in Native. Except freezing is still a thing in some cases. There are thread pools for computation and IO tasks, you can freely jump/switch between threads, update UI without freezing it, etc. It can also be exposed to Swift with just
.wrap()
extension function.
πŸ‘ 1