Translated into callback, experimentally - into async / await. Generic type arguments are lost. Libraries like SKIE and KMP-NativeCoroutines can be used to improve the interop and provide cancellation support.
Let's describe a class that creates a Kotlin Flow
:
class NumberFlowRepository {
fun getNumbers(): Flow<Int> = flow {
for (i in 1..10) {
emit(i)
delay(1.seconds)
}
}
}
On the Swift side, it's necessary to create a Flow
collector, but the generic type argument of the Flow
is lost and instead the Any?
type must be used.
class AnyCollector : Kotlinx_coroutines_coreFlowCollector {
func emit(value: Any?) async throws {
print("Got number: \(value!)")
}
}
Task {
try await NumberFlowRepository().getNumbers().collect(collector: AnyCollector())
}
However, this approach does not have cancellation support. Even if the Task is cancelled, nothing happens. The flow will continue emitting elements.
Task {
try await NumberFlowRepository().getNumbers().collect(collector: AnyCollector())
}.cancel()
KMP-NativeCoroutines is a library that can improve the interop and provides cancellation support. It is compatible with async/await, Combine, and RxSwift approaches to concurrency on iOS.
In Kotlin it is:
class NumberFlowRepository {
@NativeCoroutines
fun getNumbers(): Flow<Int> = flow {
for (i in 1..10) {
emit(i)
delay(1.seconds)
}
}
}
In Swift:
Task {
do {
let sequence = asyncSequence(for: NumberFlowRepository().getNumbers())
for try await number in sequence {
print("Got number: \(number)")
}
} catch {
print("Failed with error: \(error)")
}
}
Please follow the setup instructions in the KMP-NativeCoroutines documentation for the Gradle setup instructions.
In Swift:
Task {
do {
let sequence = asyncSequence(for: NumberFlowRepository().getNumbers())
for try await number in sequence {
print("Got number: \(number)")
}
} catch {
print("Failed with error: \(error)")
}
}.cancel()
This will cause the Flow to stop emitting values but not give an error.
SKIE is also improves the interop and provides cancellation support. SKIE is directly compatible with async/await, and with Combine and RxSwift using adapters.
In Kotlin it is the same as the original:
class NumberFlowRepository {
fun getNumbers(): Flow<Int> = flow {
for (i in 1..10) {
emit(i)
delay(1.seconds)
}
}
}
SKIE converts the flow to an iterable Swift AsyncSequence
:
Task {
for await it in NumberFlowRepository().getNumbers() {
print("Got number: \(it)")
}
}
Please follow the setup instructions in the SKIE documentation for the Gradle setup instructions.
In Swift:
Task {
for await it in NumberFlowRepository().getNumbers() {
print("Got number: \(it)")
}
}.cancel()
Cancellation will cause the processing of the AsyncSequence
to stop immediately.