(Resolved :white_check_mark: ) Hi everyone! I have...
# compose-web
r
(Resolved ) Hi everyone! I have searched Slack to see if this question has already been answered, but I'm wondering if anyone has been able to achieve fully asynchronous full-duplex communication between JS and Kotlin - calling JS from Kotlin and vice versa, independently from where/by-whom/thread the entry point for the exchange/function-calls is established. I believe I have followed all the steps from https://kotlinlang.org/docs/wasm-js-interop.html#use-kotlin-code-in-javascript but my browser console output in Chrome is reporting that it cannot find 'modules.mjs' when I try to call Kotlin. The reason I am investigating these mechanisms is because I am porting a vanilla/non-Compose Android app to CMP, but the app makes use of a
GLSurfaceView
with custom fragment and vertex shaders, so I'm hoping to leverage WebGL for the WASM deployment. I would love to, if this venture proves successful, contribute to providing an OpenGL context in CMP, and if this is the wrong place to ask the above question, my apologies in advance! Update in comments
_Update_: I have been able to successfully call Kotlin from Javascript with the following scriptlet: `index.html`:
Copy code
[...]
const exports = import("./composeApp.mjs");
            exports.then((value) =>
                value.logHelloInKotlin()
            );
[...]
`main.kt`:
Copy code
[...]
external fun logHello()

@OptIn(ExperimentalJsExport::class)
@JsExport
fun logHelloInKotlin() {
    println("Hello from Kotlin!")
}
[...]
build.gradle.kts
at project-level:
Copy code
[...]
@OptIn(ExperimentalWasmDsl::class)
    wasmJs {
        moduleName = "composeApp"
        browser {
            val rootDirPath = project.rootDir.path
            val projectDirPath = project.projectDir.path
            commonWebpackConfig {
                outputFileName = "composeApp.js"
                devServer = (devServer ?: KotlinWebpackConfig.DevServer()).apply {
                    static = (static ?: mutableListOf()).apply {
                        // Serve sources to debug inside browser
                        add(rootDirPath)
                        add(projectDirPath)
                    }
                }
            }
        }
        binaries.executable()
        generateTypeScriptDefinitions()
    }
[...]
w
Did you end up shipping this? I'm trying to do the same in order to show a button to the user to activate a new service worker, but I'm getting an issue which I think is caused by invoking the Kotlin method before composeApp.js fully initializes. I'm not quite sure how to delay the method call until Compose is ready.
I couldn't figure this out and instead ended up using a global scoped object on the JS side which the Compose side passes a static callback to, which can be invoked when the JS side is ready to send the event. Not ideal, but it works.
r
Hi @Winson Chiu . Sorry for the delayed response. I was not quite able to ship this, because I noticed that when I call back into Kotlin from Javascript, the hashcodes of the objects I get called back "into" differ, and not quite sure yet how to solve that - perhaps I'll try to pass objects as parameters all the way down the call-stack... but that's so hacky...
Regarding the issue you mentioned, the way I call the
external
Kotlin method is this (in
main.kt
):
Copy code
[...]
@OptIn(ExperimentalComposeUiApi::class)
fun main() {
    logHello()
    ComposeViewport(document.body!!) {
        App()
    }
}
[...]
I'm interested to hear if you have a stacktrace, or are able print a stacktrace to the logs and post it here. I take it you are trying to interact with the DOM? The only thing I have managed to do so far is 1) log messages and 2) make an AJAX call (but still no solution to the problem I described regarding the hashcodes)
To sketch some context as to what I'm trying to do and why; I figured an HTTP client is something that a lot of apps will want, and since browsers already support AJAX, I consider it reasonable to try and use that instead of shipping every WASM app with its own HTTP logic, as it will also result in faster loading times because of smaller .wasm files
w
I actually had an issue with the other direction, the
logHelloIntoKotlin
in your example. I don't remember the exact error, but it was something along the lines of failure to import js-joda/core because of a misformatted path. I think that's because the composeApp.js defers the load of those modules, so even if you invoke that script before any script that calls the interop method, Compose/Kotlin isn't ready to receive it.