Foreign code call not working. Am I missing something?

I have the following code in a Uno module:

public static object Log(Context c, object[] args)
{
	foreach (var arg in args) debug_log arg;
	return null;
}

[Foreign(Language.Java)]
public static extern(Android) void ALog(string message)
@{
	android.util.Log.d("ForeignCodeExample", message);
@}

If I do this (on a real Android device):

MyModule.Log('Hello from JS!")

I get “Hello from JS!” in the log as expected.

However, if I do:

MyModule.ALog('Hello from JS!")

I just get:

Source line: Nfc.ALog("Hello from JavaScript!");
( 1606): JS stack trace: TypeError: MyModule.ALog is not a function
( 1606):     at Object._tempMethod (Pages/SplashPage.js:6:5)
( 1606):  in Fuse.Reactive.JavaScript.DiagnosticSubject<Pages/SplashPage.js:6>

…in the log.

Am I missing something for Android native calls?

Thanks.

Could you please show the whole .uno file in question? The problem might be somewhere outside of the snippets you’ve shared.

Sure, here it is:

(I have since changed the module name to Nfc both here and in the JS)

using Fuse;
using Fuse.Scripting;
using Uno.UX;
using Uno.Compiler.ExportTargetInterop;

[UXGlobalModule]
public class Nfc : NativeModule
{
    static readonly Nfc _instance;

    public Nfc()
    {
        if (_instance != null) return;
        _instance = this;
        Resource.SetGlobalKey(_instance, "Nfc");
        AddMember(new NativeFunction("Log", (NativeCallback)Log));
    }

    public static object Log(Context c, object[] args)
    {
        foreach (var arg in args) debug_log arg;
        return null;
    }

    [Foreign(Language.Java)]
    public static extern(Android) void ALog(string message)
    @{
        android.util.Log.d("ForeignCodeExample", message);
    @}
}

To me it seems the problem then is that you haven’t exposed the function to JS. Similarly how you have the AddMember for Log, you should add one for ALog:

AddMember(new NativeFunction("Log", (NativeCallback)Log));
AddMember(new NativeFunction("ALog", (NativeCallback)ALog));

Ah, yes of course! However, adding this gives the compile error:

E3102: There is nothing named 'ALog' accessible in this scope

Well that’s because there isn’t, if you’re running it on local preview. Foreign code is very well aware of the platform it is being run on, so you need to take care about defining what a method does in every case.

Here’s a complete, working example that you can use like so:

<App>
	<JavaScript>
		var Nfc = require("Nfc");
		Nfc.ALog("ohai");
	</JavaScript>
</App>

And the Uno code, with helpful comments:

using Fuse;
using Fuse.Scripting;
using Uno.UX;
using Uno.Compiler.ExportTargetInterop;

[UXGlobalModule]
public class Nfc : NativeModule
{
    static readonly Nfc _instance;

    public Nfc()
    {
        if (_instance != null) return;
        _instance = this;
        Resource.SetGlobalKey(_instance, "Nfc");
        AddMember(new NativeFunction("ALog", (NativeCallback)ALog));
    }

    // this is the "proxy" method that dispatches calls to whatever the platform-specific implementation is
    // native functions exposed to JS need to return an object, so we define the return type like such, and return null in this case
    public static object ALog(Context c, object[] args)
    {
        ALogImpl(args[0].ToString());
        return null;
    }

    // this is called from the "proxy" method when the app is not running on mobile targets (local preview, for one)
    public static extern(!Mobile) void ALogImpl(string message)
    {
        debug_log message;
    }

    // this is called from the "proxy" method when the app is running on Android
    [Foreign(Language.Java)]
    public static extern(Android) void ALogImpl(string message)
    @{
        android.util.Log.d("ForeignCodeExample", message);
    @}

    // this is called from the "proxy" method when the app is running on iOS
    [Foreign(Language.ObjC)]
    public static extern(iOS) void ALogImpl(string message)
    @{
        NSLog(@"%@", message);
    @}
}

Take a look at this library to see how more complex (but still relatively simple) platform-aware things can be implemented.

Thank you, that’s great. I understand how it works now.