Is there an easy way in Swift to await an async op...
# multiplatform
j
Is there an easy way in Swift to await an async operation? So instead of relying in callback fashion we would suspend
Copy code
// This isLoggedInUseCase is a Kotlin suspend fun
isLoggedInUseCase.invoke { task, error in
    switch task {
    case let task as TaskResultSuccess<KotlinBoolean>:
        // Callback style result here with true|false
        break;
    case _ as TaskResultError<KotlinBoolean>:
        // Handle error
        break;
    default: break;
    }
}

// What I wanted is to await the result of the usecase exec and
// Only then continue execution of the 'main' func
j
j
Was reading your answer on twitter, thanks for the input gonna take a look. Ty once again John
Unfortunately this did not solve my issue 😞 as I can’t “return” the data and I need to always await its completion
r
Swift doesn't yet have async/await support (though it sounds like that will change soon) so you need some sort of callback rather than a direct return.
🙏 1
j
came across following just now that seems to indicate you can somewhat mimic await type functionality using Combine.....but haven't tried it yet https://medium.com/swift-sections/async-await-with-swift-combine-fdb4857b92ac
Swift code then of course needs to map somehow to methods invoked in shared code....and I think we're still limited there to either using Kotlin/Native 1.4 capability to call suspend functions or passing in lambda/closure (which we still would need to do for flows anyway)....and also then managing coroutine scope...I think parts at least of how that was handled in the original RxSwift article will still be needed
@Joaquim Ley are you using this in a SwiftUI based app?
If so, I think the setup should allow things to be generally more reactive anyway so there shouldn't be same dependency on making calls like above where you need to wait on a result
(not that this wasn't possible of course before SwiftUI but I think that does encourage that approach)
j
Yes I am 😉 — But the UI part is totally fine. For some context, the shared module goes up until the use cases (CLEAN) that are Kotlin suspend fun. My ideia here is having a 100% swift ViewModel that calls and these, and I can (callback-based) use its results to send state to the view, but I just came across a very specific case where I need to await the response of 2 of these use cases
I’m going to read this article 👌
r
It might be easier to handle on the Kotlin side and just expose the final result to Swift. That way you have access to Kotlin's async and await.
j
btw I'm looking here atm at applying approach @russhwolf wrote about for RxSwift but for Combine
r
I would love to hear how that goes
j
Not sure how I would do that tbh, if the function is async the caller will always have to await regardless of the point of “entry”, unless I’m missing something 😛 @John O'Reilly please let me know if you end up with any solution I would really love to hear what you come up with
r
You'd still need a callback, but you could do a callback with the combined result of the two things you're awaiting instead of them each being separate.
j
Using the async from the blog post I’m getting the thread crash
Copy code
Uncaught Kotlin exception: kotlin.native.IncorrectDereferenceException: illegal attempt to access non-shared
j
I have very basic Combine
Publlisher
now that maps to the Kotlin flow. Not directly related to that but re.
Copy code
val scope: CoroutineScope = object : CoroutineScope {
        override val coroutineContext: CoroutineContext
            get() = SupervisorJob() + Dispatchers.Default
    }
@russhwolf I'm using
Dispatchers.Main
here right now as I'm making main-safe
ktor
calls in my
flow
....that won't always be case but at least in this example that seems to mean that those various
freeze()
calls aren't needed?
following is what I have so far....still looking to confirm cancellation behaviour on iOS side of things (added specific button for now and some logging in kotlin to verify cancellation does work) - https://github.com/joreilly/PeopleInSpace/commit/c24bce7ca25355cba11013213eb0c36035d299ea
r
Yeah all the freeze stuff will only be needed if you're switching threads. Making sure you could do that safely was one of my goals in the original RxSwift post. If you want to verify cancellation you could try porting the RxSwift test cases I had at https://github.com/touchlab/SwiftCoroutines/blob/master/ios/SwiftCoroutinesTests/SwiftCoroutinesTests.swift. But I don't know what API Combine provides for writing that sort of test.
j
I'm taking approach here (and elsewhere) where the flow is happening on main thread but is invoking "main-safe" functions (as likes of Ktor for example provides). Are you seeing many cases where flow needs to be on background thread?
r
Database access is the usual example that comes to mind
j
those calls should typically be main-safe as well though? guess it depends on library used?
r
Though you could set up similar main-safe internals I guess
j
not useable in KMP of course but Room at least is "Suspend functions in Room are main-safe and run on a custom dispatcher" ....I think that should be case for SQLDelight as well....
r
There’s also cases where even for something like ktor, you want the serialization to happen off main
j
ok, yeah, that makes sense
r
and if you’re integrating into a legacy system then all bets are off as far as what your threading needs are
j
Created this to try and capture what I've done so far....any feedback warmly welcomed 🙂 https://johnoreilly.dev/posts/kotlinmultiplatform-swift-combine_publisher-flow/
🙏 1