Writing a capacitor PluginMethod for Android annotated with returnType=PluginMethod.RETURN_NONE

The documentation for writing Capacitor plugins mentions that for Android, if your plugin method does not return any data, then you can annotate your plugin method with “returnType=PluginMethod.RETURN_NONE”. Capacitor documentation link to this scenario is here: Capacitor - build cross platform apps with the web

To summarize, the Java code in Android to define the method looks like this:

@PluginMethod(returnType = PluginMethod.RETURN_NONE)
public void method1(PluginCall call) {
android.util.Log.i(“method1”,“Inside method1 java code”);
call.resolve();
}

And back in TypeScript/JavaScript land, the code to define this same method looks like this:

export interface MyPlugin {
method1(): Promise<void>;
}

So based on the capacitor documentation, for a capacitor plugin method which does not return any data, that method returns a Promise which you should be able to “await” in TypeScript/JavaScript.

The problem is that the promise/await doesn’t work for the above sample code. For example, the following TypeScript/JavaScript code does not work as expected:

async myfunction() {
console.log(“About to call plugin method1”)
await MyPlugin.method1()
console.log(“Finished call to plugin method1”)
}

Given the above Android Java code and the above TypeScript/JavaScript, you would expected the log output to be:

About to call plugin method1
Inside method1 java code
Finished call to plugin method1

However, this is not what happens. Instead you get this:

About to call plugin method1
Finished call to plugin method1
Inside method1 java code

This indicates that the “await” in the TypeScript/JavaScript invocation of the plugin method is not really await-ing the completion of the promise returned by the plugin method. So it seems like this plugin method is not really returning a promise that can be await-ed.

You can solve the above problem by removing the “returnType=PluginMethod.RETURN_NONE” from the declaration of method1 in the Java code. If you remove that annotation and re-run the above example code, then the output is as expected:

About to call plugin method1
Inside method1 java code
Finished call to plugin method1

But this contradicts what the capacitor documentation says in the link at the beginning of my post here. The capacitor documentation claims that for PluginMethod.RETURN_NONE, you still get a JavaScript promise, which should be await-able.

So is the capacitor documentation incorrect, or is capacitor itself not working correctly for me?

The documentation is not incorrect, but your understanding is a bit off. On the JavaScript bridge for connecting web and native, we have two main, internal functions:

  • Capacitor.nativeCallback()
  • Capacitor.nativePromise().

In the documentation for the public APIs for building plugins, we have three return types

  • Void Return
  • Value Return
  • Callback

They are all typed as a Promise so that the code can be async. However, this does not mean that it guarantees execution, it just means that the async promise has been queued for execution. Void Return and Callback both call Capacitor.nativeCallback(). The promise resolves in the web code when it has been queued for execution, not when it has finished running. In the case of Callback. Value Return, on the other hand, resolves the promise once you have a value to return.

Void return is probably a poor name in the documentation, but if you want your code to work as you intend it to, you can change the code to just use @PluginMethod() and call call.resolve() without any parameters and functionally, it is the same.

For more info, you can take a look at the definitions for nativeCallback() and nativePromise() on GitHub.

1 Like

Ok thank you so much for the explanation! It sounds like it is the expected behavior then. I had deduced that removing the annotation “returnType=PluginMethod.RETURN_NONE” causes the JavaScript promise to behave as I wanted.

I would suggest more details in the documentation page that I linked. To be fair, when a JavaScript developer sees a plugin method returns a Promise, the developer is definitely going to assume that if you await that promise, then the native Android Java function has completed its work. The last thing anyone would guess is that the resolved promise only indicates that your task has merely been queued. I mean, I understand that now after your explanation, but I would have never guessed that based on the documentation.

Docs are indeed incorrect.
PluginMethod.RETURN_NONE returns nothing, so the TS code should be method1(): void;.

That’s different from returning a Promise<void>, for that you should use the regular PluginMethod.RETURN_PROMISE annotation (or nothing since it’s the default), and then use resolve() on the native code to return the promise with no value.

PluginMethod.RETURN_NONE is rarely used as most times you’ll want to await for the plugin execution even if it returns nothing.

I’ve created an issue to track it

2 Likes

An error in the docs indeed makes more sense. Glad I was able to help identify it, and thank you for creating the issue to fix it.