How can I run Kotlin suspend fun on @ReactMethod (ReactNative)

Viewed 536

I need to implement a custom gRPC on Kotlin native side. @ReactMethod can't be suspend func. How can I run it?

@ReactMethod
fun connect(ipAddress: String, port: Int) {
    try {
        channel = ManagedChannelBuilder.forAddress(ipAddress, port).usePlaintext().build()

        var guidKey = Metadata.Key.of("GUID", Metadata.ASCII_STRING_MARSHALLER)
        metadata.put(guidKey, GUID)

        val stub = DBServiceGrpcKt.DBServiceCoroutineStub(channel!!)

        var request = GrpcDBService.SignInRequest.newBuilder()
            .setUserName("user")
            .setPassword("11111")
            .build()

        try {
            //******* this part *****
            suspend fun coroutine() {
                var response = stub.trySignIn(request,metadata)
            }
        } catch (e: Exception) {
            Log.d("grpcConnect", e.localizedMessage)
        }
    } catch (e: Error) {
        Log.d("grpcConnect ", e.localizedMessage)
    }
    finally {
        channel?.shutdown()
    }
}
1 Answers

You need to create a coroutine, for example, define a scope (somewhere in your class) and use it with launch:

myPluginScope.launch {
   val response = stub.trySignIn(request,metadata)
   // Return, the result
}

Creating the scope is easy, the tricky part is to find where to cancel it. Check the documentation on React native modules to find a good place to call cancel on your scope:

val myPluginScope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)

Now, is important to decide whether the connect function should behave as an asynchronous function or not. For example, if the sign in operation takes time or uses the network, connect should probably return the result through a Promise or Callback, so the Javascript side is not blocked:

@ReactMethod
fun connect(ipAddress: String, port: Int, promise: Promise) {
    myPluginScope.launch {
        try {
            // Make the sign in happen in a separate thread:
            val response = withContext(context = Dispatchers.IO) {
                stub.trySignIn(request, metadata)
            }
            // Resolve the promise in the calling thread (The UI thread)
            promise.resolve(response.hypotheticalCode)
        } catch (e: Exception) {
            promise.reject("Sign in error!", e)
        }
    }
}

See: https://reactnative.dev/docs/native-modules-android#promises
https://kotlinlang.org/docs/async-programming.html#coroutines

Related