dotnet / maui

.NET MAUI is the .NET Multi-platform App UI, a framework for building native device applications spanning mobile, tablet, and desktop.
https://dot.net/maui
MIT License
22.2k stars 1.75k forks source link

[Enhancement] (WebView) - Expose C# classes in JS #9424

Open TGSAN opened 2 years ago

TGSAN commented 2 years ago

Description

Add AddHostObjectToScript to MAUI's WebView so that the same code can be used to implement JSBridge on any platform The API design can be found in the Microsoft WebView2 documentation: https://docs.microsoft.com/en-us/dotnet/api/microsoft.web.webview2.core.corewebview2.addhostobjecttoscript

This feature is proposed to enhance the flexibility of calls between Javascript code and C# code in WebView.

It is possible to expose specific C# classes for Javascript code in WebView, so that Javascript code can call members on the classes (for notifications or transferring data). Previously using MAUI required developers to write duplicate code for each platform to implement similar functionality (a capability available in WebView implementations on various platforms), when this feature is implemented in MAUI, developers can focus solely on writing MAUI common code, lowering the barrier to use.

Public API Changes

In C#

[ClassInterface(ClassInterfaceType.AutoDual)]
[ComVisible(true)]
public class BridgeAnotherClass
{
    public string Prop { get; set; } = "Example";
}

[ClassInterface(ClassInterfaceType.AutoDual)]
[ComVisible(true)]
public class Bridge
{
    public string Func(string param)
    {
        return "Example: " + param;
    }

    public string BytesToString(byte[] bytes)
    {
        Encoding enc = new UTF8Encoding(true, true);
        return enc.GetString(bytes);
    }

    public byte[] StringToBytes(string str)
    {
        Encoding utf8 = Encoding.UTF8;
        return utf8.GetBytes(str);
    }

    public BridgeAnotherClass AnotherObject { get; set; } = new BridgeAnotherClass();

    [System.Runtime.CompilerServices.IndexerName("Items")]
    public string this[int index]
    {
        get { return m_dictionary[index]; }
        set { m_dictionary[index] = value; }
    }
    private Dictionary<int, string> m_dictionary = new Dictionary<int, string>();
}

In C# (Then add instances of those classes via AddHostObjectToScript(String, Object))

webView.AddHostObjectToScript("bridge", new Bridge());

In Javascript (C# class as a Javascript proxy object)

const bridge = window.DotNet.WebView.hostObjects.bridge;
console.log(await bridge.Func("testing..."));
// Uint8Array send to C#
const enc = new TextEncoder();
const bytes1 = enc.encode("This is a string converted to a Uint8Array");
console.log(await bridge.BytesToString(bytes1));
// C# to Javascript Unit8Array to C#
const bytes2 = await bridge.StringToBytes("This is a string converted to a Uint8Array");
console.log(await bridge.BytesToString(bytes2));

Intended Use-Case

In my project, I want to reuse some binary data processing libraries written in Javascript, and then notify and give the processed binary data to C# for processing. As another example, there are many HyperApps, that need to notify the C# side after the Javascript initialization on the web side is finished (e.g. the login is done in the WebView and then the Token is passed to the App).

jfversluis commented 2 years ago

Thank you so much for the suggestion @TGSAN! I don't know a lot about this area, but I see a lot of COM stuff, which immediately makes me think this is very specific to Windows? So this will only work on Windows?

ghost commented 2 years ago

We've moved this issue to the Backlog milestone. This means that it is not going to be worked on for the coming release. We will reassess the backlog following the current release and consider this item at that time. To learn more about our issue management process and to have better expectation regarding different types of issues you can read our Triage Process.

TGSAN commented 2 years ago

Thank you so much for the suggestion @TGSAN! I don't know a lot about this area, but I see a lot of COM stuff, which immediately makes me think this is very specific to Windows? So this will only work on Windows?

No, it can be implemented on any platform. Currently only WebView2 (Windows) provides this API. but this API design can be available on any platform (e.g. iOS and Android).

The specific implementation can be achieved through the WebView of different platforms. Register a proxy (Javascript Object) to the WebView Document through JSBridge, and when Javascript calls the members of the Proxy, the corresponding method is called in C# using reflection.

GitClickOk commented 4 months ago

Thank you so much for the suggestion @TGSAN! I don't know a lot about this area, but I see a lot of COM stuff, which immediately makes me think this is very specific to Windows? So this will only work on Windows?

Both Android WebView and iOS WKWebView implement this similar functionality, but not the Windows version, making this a stopper to any advanced task in MAUI+WebView. And this functionality works in other Windows platforms like WPF or Windows Forms...

All these are community hacks: https://learn.microsoft.com/en-us/answers/questions/1291522/using-addhostobjecttoscript-in-maui-apps https://stackoverflow.com/questions/73217992/js-net-interact-on-maui-webview https://stackoverflow.com/questions/72683537/corewebview2-addhostobjecttoscript-throws-system-exception https://stackoverflow.com/questions/73610413/webview2-addhostobjecttoscript-in-uwp-crashes https://github.com/microsoft/microsoft-ui-xaml/issues/7170

But all these are far from ideal, and most of these force us to work with WinRT adapters. Just look this article: https://learn.microsoft.com/en-us/microsoft-edge/webview2/how-to/winrt-from-js?tabs=winui3%2Cwinrtcsharp Currently, this is the "official" way to make it work. And it conveniently skips the part where we will need at least a ".Net Standard" library with the bridge object to be used by the WinRT Adapter, and then consumed in the final MAUI app (I hope it's possible). In other words, at least 2 extra projects to make it, and no warranties that will break something in the end...

By the way, I don't consider this an "[Enhancement]", but a request for a bug fix. In the end, a bug happens when the expected way does not work as intended, am I right?

EDIT: I got now that it is a "Request to add AddHostObjectToScript to Maui WebView", that currently does not have. But as I said, the only stopper is the Windows WebView (CoreWebView2), which does not correctly implement this.

Regards

jfversluis commented 2 months ago

As of .NET 9 we will have the HybridWebView, will that help in this scenario? See #22880