Skip to content

Latest commit

 

History

History
136 lines (109 loc) · 3.42 KB

File metadata and controls

136 lines (109 loc) · 3.42 KB

Flows

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.

Explanations

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())
}

Cancellation

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

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.

Cancellation

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

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.

Cancellation

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.


Table of contents