swift 从iOS收听Kotlin协同程序流

ht4b089n  于 2023-03-22  发布在  Swift
关注(0)|答案(3)|浏览(127)

我已经设置了一个Kotlin多平台项目,并将SQLDelight数据库附加到它。它的所有设置和运行都正确,因为我已经使用以下代码在Android端测试了它:
commonMain:

val backgroundColorFlow: Flow<Color> =
            dbQuery.getColorWithId(BGColor.id)
                    .asFlow()
                    .mapToOneNotNull()

它在Android项目MainActivity.kt中使用以下命令触发:

database.backgroundColorFlow.onEach { setBackgroundColor(it.hex) }.launchIn(lifecycleScope)

但是当我试图在iOS项目的app delegate中访问相同的调用时,我得到了以下选项,我不确定如何使用它们或将它们转换为我的BGColor对象:

database.backgroundColorFlow.collect(collector: T##Kotlinx_coroutines_coreFlowCollector, completionHandler: (KotlinUnit?, Error?) -> Void)

有谁能告诉我怎么用这个吗?

wljmcqd8

wljmcqd81#

所以这个问题通过创建一个flow helper来解决:

import io.ktor.utils.io.core.Closeable
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*

fun <T> Flow<T>.asCommonFlow(): CommonFlow<T> = CommonFlow(this)
class CommonFlow<T>(private val origin: Flow<T>) : Flow<T> by origin {
    fun watch(block: (T) -> Unit): Closeable {
        val job = Job()

        onEach {
            block(it)
        }.launchIn(CoroutineScope(Dispatchers.Main + job))

        return object : Closeable {
            override fun close() {
                job.cancel()
            }
        }
    }
}

我的backgroundColorFlow var更新如下,以使用此帮助程序:

val backgroundColorFlow: CommonFlow<BGColor> =
            dbQuery.getColorWithId(BGColor.id)
                    .asFlow()
                    .mapToOneNotNull()
                    .map { BGColor(it.name) }
                    .asCommonFlow()

我的雨燕是这样工作的:

database.backgroundColorFlow.watch { color in
            guard let colorHex = color?.hex else {
                return
            }
            self.colorBehaviourSubject.onNext(colorHex)
        }

android是这样的:

database.backgroundColorFlow.watch { setBackgroundColor(it.hex) }

希望这对遇到这个问题的人有所帮助。我想将CommonFlow类转换为Flow的扩展,但我不知道如何使用atm,所以如果有可能,IMHO将是一个更好的解决方案

0pizxfdo

0pizxfdo2#

你可以在swift中使用上面提到的collect方法来完成,FlowCollector是一个可以实现的协议,用于收集Flow对象的数据。
通用示例实现可能如下所示:

class Collector<T>: FlowCollector {

    let callback:(T) -> Void

    init(callback: @escaping (T) -> Void) {
        self.callback = callback
    }

    func emit(value: Any?, completionHandler: @escaping (KotlinUnit?, Error?) -> Void) {
        // do whatever you what with the emitted value
        callback(value as! T)

        // after you finished your work you need to call completionHandler to 
        // tell that you consumed the value and the next value can be consumed, 
        // otherwise you will not receive the next value
        //
        // i think first parameter can be always nil or KotlinUnit()
        // second parameter is for an error which occurred while consuming the value
        // passing an error object will throw a NSGenericException in kotlin code, which can be handled or your app will crash
        completionHandler(KotlinUnit(), nil) 
    }
}

第二部分是调用Flow.collect函数

database.backgroundColorFlow.collect(collector: Collector<YourValueType> { yourValue in 
    // do what ever you want
}) { (unit, error) in 
    // code which is executed if the Flow object completed 
}

也许你也喜欢写一些扩展函数来增加可读性

hmmo2u0o

hmmo2u0o3#

我今天也必须处理这个问题,所以我复制了Kotlin NopCollector,并在Swift中创建了自己的 collect() 扩展函数:

import Foundation
import shared

/**
 * Copy of kotlinx.coroutines.flow.internal.NopCollector
 */
class NopCollector<T>: Kotlinx_coroutines_coreFlowCollector { 
    func emit(value: Any?) async throws {
        // does nothing
    }
}

/**
 * Copy of kotlinx.coroutines.flow.collect extension function
 */
extension Kotlinx_coroutines_coreFlow {
    func collect() async {
        try? await collect(collector: NopCollector<Any?>())
    }
}

这样你就可以在Swift中简单地收集你的流,如下所示:

let messageStream = myService.receiveMessageStream(listener: self)
        await messageStream.collect()

正如你在receiveMessageStream中看到的,它传递了listener(来自shared module的Kotlin接口),你可以在View Model(ObservableObject)中实现它作为经典的swift Protocol。这是为了接收kotlin流的所有事件。下面是在kotlin中的shared module中的实现方式:

fun receiveMessageStream(listener: IWebsocketApi.IListener): Flow<Message> {
        return flow {
            createSessionWithWebSocket(this, listener)
        }.onStart {
            listener.onWsStart()
        }.onEach {
            listener.onWsEach(it)
        }.catch {
            listener.onWsConnectionFailure(it)
        }.onCompletion {
            listener.onWsCompletion(it)
        }
    }

这就是你 * 从iOS* 收听Kotlin协程流的方式。

相关问题