DEV Community

Cover image for Positron-JS for DotNet Maui
Akash Kava
Akash Kava

Posted on

Positron-JS for DotNet Maui

An Advanced Web View based Maui Project. This project was inspired from Cordova/Capacitor. However, building native plugins is very time consuming. Maui offers single language to write native plugins easy on any platform. So we decided to create Capacitor/Electron-JS for Maui.

Why?

Well C# is better language when we want to develop native apps for Android and iOS, however writing everything in C# and deploying app is very time consuming as every build needs to go through app store approvals. Often app store reviewers have no idea about the apps and they keep on clicking on the most unused parts of apps and complain about something trivial and reject the approval.

We wanted to use Capacitor or React Native, but both of them suffer plugin creation as plugins has to be created in native languages, (Java for Android and Swift on iOS), which requires two separate set of codebases and different expertise.

So we decided to build a PositronWebView that offers easy hybrid app creation along with simple API to access Device's native interface.

Is HTML Fast?

With recent advancement in HTML5 + CSS3 and JavaScript updates, creating web UI is easier than native UI, you can create almost all effects in HTML. The key to make app superfast is to drop the bulky JavaScript Frameworks, for example using instead of using $('.price').text("$20") you should use document.getElementsById("price").textContent = "$20".

Try to use inbuilt CSS animation vs JavaScript to emulate animations.

Our own Web Atoms framework is superfast, but you have your own choice to use any framework you want with PositronWebView.

Getting Started

  1. Create an empty folder
  2. Run npm init
  3. Run npm install -s @positron-js/cli
  4. Run node ./node_modules/@positron-js/cli init
  5. Run npm install

This will create a maui project inside maui folder. Along with build scripts to publish.

Open Web URL

PositronWebView expects URL for website, you can directly open remote URL and expose positron context. You can also limit exposure of positron context by setting ShouldInvokeScript delegate.

The url is set in build script's config.js file, you can also configure url in appsetting.json file.

Access .NET API

Positron exposes positron property on window object, with run method. Positron installs JavaScript engine (YantraJS in Android and JavaScriptCore in iOS), which integrates with CLR. So you can write JavaScript that access CLR objects directly.

Example - Get DeviceToken

Lets assume that we have configured push messaging and we want to retrieve the device token. (For convenience, we have created Positron class that gives you deviceToken), you have to enable RegisterPushMessaging in MauApplication.cs file, which is commented by default as it gives build errors if google-services.json is not configured correctly.


    import PositronInBrowser from "@positron-js/context/index.js";

    /** Although syntactically correct, this function
     * will be executed inside Positron's JavaScript Engine.
     * And the result will be available after successful execution.
     * 
     * This function cannot contain any closer except `global`;
     */

    /** Basically positron.run() will send script text and parameters */
    /* (as json) to Positron's JavaScript engine */
    const deviceToken = await PositronInBrowser.run(function () {

        // this runs inside App's Internal JavaScript Engine

        const NSPositronAssembly = this.clr
            .assembly("NeuroSpeech.Positron");

        const Positron = NSPositronAssembly
            // following is fully qualified name
            .NeuroSpeech.Positron.Positron;

        return Positron.deviceToken;
    });
Enter fullscreen mode Exit fullscreen mode

Example - Open Url

Lets assume you want to open native browser instead of navigating WebView.



    import PositronInBrowser from "@positron-js/context/index.js";

    await PositronInBrowser.run({url}, function (p) {

        const Essentials = this.clr.assembly("Microsoft.Maui.Essentials");

        const Browser = Essentials.Microsoft.ApplicationModel.Browser;

        Browser.default.openAsync(p.url);

    }, "https://socialmail.me")
Enter fullscreen mode Exit fullscreen mode

Note, we are sending parameters as first parameter in call to run method. And same parameters object can be accessed inside function(p). You can only pass plain non recursive primitive types in parameters. As they are sent to Positron as JSON.

Example - Create New Instance



    import PositronInBrowser from "@positron-js/context/index.js";

    await PositronInBrowser.run({url}, function (p) {

        const Essentials = this.clr.assembly("Microsoft.Maui.Essentials");
        const Graphics = this.clr.assembly("Microsoft.Maui.Graphics");

        const { Browser, BrowserLaunchOptions } = Essentials
            .Microsoft.ApplicationModel;

        const { Colors } = Graphics.Microsoft.Maui.Graphics;

        const options = new BrowserLaunchOptions();
        options.preferredToolbarColor = Colors.orange;

        await Browser.openAsync(p.url, options);

    }, "https://socialmail.me")
Enter fullscreen mode Exit fullscreen mode

Positron Context

PositronWebView creates a new JavaScript context (JavaScriptCore in iOS and YantraJS on other platforms) and it exposes clr.assembly function, which you can access by calling this.clr.assembly, from this assembly you can easily access namespaces and types.

Once you have types, you can call methods or construct an objects from it.

For convenience we have created @positron-js/context npm package which exposes typescript definitions for Microsoft.Maui.Graphics, Microsoft.Maui.Essentials and NeuroSpeech.Positron assemblies.

You can add your own assemblies easier, the source code is available on github https://github.com/Positron-JS/context

AOT and Linking

For PositronWebView to access all CLR objects, Linking must be disabled and interpreter must be turned on, these settings are automatically set when cli creates a sample project.

Using interpreter, reduces code size, but it does add performance overhead. However, if you do your most stuff inside HTML, browser's native performance will take care of everything. And if you only need to occasionally access .NET APIs such as geolocation or push registration token, select files etc, there would be no visible performance issues.

Publishing

To publish the project, you must execute .jsx file with @neurospeech/jex as shown below.

node ./node_modules/@neurospeech/jex/index.js ./build-android.jsx

Jex is a new scripting engine that executes jsx files as a build script, and you can easily edit them and utilize JavaScript in the build script.

You just have to configure environment variables and put your certificates in cert folder, you can review build-%%%%%-config.js file to find out what every platform needs.

Positron Web View Repository

GitHub logo Positron-JS / positron-web-view

Positron JS WebView for MAUI

Positron Web View

An Advanced Web View based Maui Project. This project was inspired from Cordova/Capacitor. However, building native plugins is very time consuming. Maui offers single language to write native plugins easy on any platform. So we decided to create Capacitor for Maui.

Positron Diagram

Get Started

NuGet

  1. Create an empty folder
  2. Run npm init
  3. Run npm install -s @positron-js/cli
  4. Run node ./node_modules/@positron-js/cli init
  5. Run npm install again.

This will create a maui project inside maui folder. Along with build scripts to publish.

Publish

To publish the project, you must execute .jsx file with @neurospeech/jex as shown below.

node ./node_modules/@neurospeech/jex/index.js ./build-android.jsx

Jex is a new scripting engine that executes jsx files as a build script, and you can easily edit them and utilize JavaScript in the build script.

Access .NET Inside Browser

Positron exposes positron property on window object, with run method. Positron installs JavaScript engine (YantraJS in Android and JavaScriptCore in…




Top comments (0)