Hi everyone, I'm trying to implement a DDP Client ...
# multiplatform
o
Hi everyone, I'm trying to implement a DDP Client using kmp for iOS and Android platforms. For side note DDP(Distributed Data Protocol) is a specialized protocol for meteor.js applications and it uses websockets to communicate with the server. I've used ktor websockets, kotlin flows to implement it. My problem is, my library behaves different on iOS and Android, probably because of wrong thread handling but I couldn't find out what it is. The server result is received in about 400ms in Android but it takes 4 seconds in iOS. The UI is unresponsive during this time. Can anyone help me with this? Details are in the thread.
It's very hard to create a reproducible code but I'll try to explain the flow first. I send message to the server with an ID and wait for the response with the same ID. First, Updated message is received with only an ID. After that result message is received with data. I collect and emit the data in the webSocket flow.
Copy code
val methodResultFlows: MutableMap<String, MutableSharedFlow<Incoming.MethodResponseMessage>> = mutableMapOf()

fun init() = flow {
        httpClient.webSocket(
            request = {
                url.protocol = protocol
            },
            block = {
                webSocketSession = this
                receiveAll {
                    handleIncoming(it)
                    emit(it)
                }
            },
        )
    }


private suspend fun DefaultClientWebSocketSession.handleIncoming(incoming: Incoming) {
    when (incoming) {
        is Incoming.Result -> {
            methodResultFlows[incoming.id]?.emit(incoming)
        }
        is Incoming.Updated -> {
            for (id in incoming.methods) {
                methodResultFlows[id]?.emit(incoming)
            }
        }
        else -> return
    }
}

inline fun <reified T : Any> call(method: String, params: JsonArray? = null) = channelFlow {
        val id = randomUUID()
        //I send a request to server here
        sendMessage(method, params)
        var last = Clock.System.now()

        val logTime: (String) -> Unit = { id ->
            val now = Clock.System.now()
            Logger.i("DDPClient") { "***time $id $method: ${now - last}" }
            last = now
        }
        send(MethodState.Loading)

        launch {
            methodResultFlows[id] = MutableSharedFlow()
            methodResultFlows[id]!!.collect { responseMessage ->
                when (responseMessage) {
                    is Incoming.Updated -> {
                        logTime("update")
                        if (!finished) {
                            send(MethodState.Updated)
                        }
                    }
                    is Incoming.Result -> {
                        logTime("result")
                        val resultObject = responseMessage.result?.let {
                            json.decodeFromJsonElement<T>(it)
                        }

                        send(MethodState.Success(resultObject))
                    }
                }
            }
        }

        awaitClose { methodResultFlows.remove(id) }
    }
The log is like this on iOS (DDPClient) ***time update (method name): 129ms (DDPClient) ***time result (method name): 4.267s And on Android: (DDPClient) ***time update (method name): 129ms (DDPClient) ***time result (method name): 467s
I also did some profiling on Xcode. But I think I couldn't read it.
turns out the problem was the deserialization step. some regular expression made the significant difference