共用方式為


Show a toast notification in a Cordova app on Windows 10 with the proper title and text

We have already talked multiple times about toast notifications on Windows 10 on this blog. Very recently, we have learned how to implement them in a Progressive Web App.

In this post I would like to show you how to do the same in a Cordova app and how to solve some issues you may face have when you try to release your application on the Microsoft Store.

The Cordova project

Cordova (formerly know as PhoneGap) is a framework to build cross-platform applications using web technologies. The Cordova approach can be summarized as "write once, run everywhere". You define the user interface using HTML and CSS, while the logic is developed in JavaScript. The same code is leveraged by Windows, Android and iOS, since the app basically runs inside the WebView control offered by the various platforms.

Cordova is able to take the same code and use it to build a project for each supported platform, using the native tools. For example, when you use Cordova to build a Windows project, you get as output a UWP project based on JavaScript that you can open in Visual Studio. When you build, instead, the iOS project, you get as output a XCode project.

The way Cordova exposes native features of the platform is through plugins. A plugin is a component made by:

  • A native library, one for each platform, which contains the specific code to leverage the feature using native code (C# in Windows, Java in Android, Objective-C in iOS)
  • A shim, which exposes the feature using JavaScript

The only part you need to care for your Cordova app is the shim, since it contains the JavaScript helpers you're going to use to leverage the feature. For example, if you want to get the current location of the user, the code you're going to write will look like this:

 navigator.geolocation.getCurrentPosition(onSuccess, onError);

Under the hood, when you build the project, let's say, for Windows, Cordova will take care of merging the base Windows project with the Windows version of the plugin, which uses the geolocation APIs exposed by the Universal Windows Platform.

The Cordova plugin ecosystem is quite rich, even if not all of them support every platform. Some of the plugins are developed directly by Cordova, some others instead by the community. They are usually available as NPM packages (since the command-line version of Cordova is based on Node.js) or directly on GitHub.

If we want to include notifications support in our Cordova app, we can get some help from the plugin community. Specifically, we can incorporate a plugin called cordova-plugin-local-notifications, which is available on NPM and GitHub.

Let's see how to use it and, most of all, how to solve a tricky problem that we may face while building the final version of our application for the Store.

Creating the Cordova project

Visual Studio includes full support for Cordova, thanks to a set of tools that were integrated inside the IDE a few years back. If you want to use them, make sure that in your Visual Studio setup you have enabled the feature Mobile development with JavaScript:

After you have installed the tools, you will find the Cordova template under JavaScript -> Mobile Apps -> Blank App (Apache Cordova).

To add the notifications plugin, we need to double click on the config.xml file. One of the benefits of using the Visual Studio tools for Cordova is that they give you a visual editor for the configuration file, which otherwise would require to manually edit the XML.

Move to the Plugins section and then choose Git. Specify as URL the the GitHub repository (https://github.com/katzer/cordova-plugin-local-notifications/) and then press the little arrow near the box. After a while Visual Studio should find the plugin and offer you the option to install it:

Choose Add and, after a few seconds, the plugin will be installed and ready to use.

Please note Make sure too add the plugin directly from the GitHub repository and not from NPM. The latter, in fact, is based on a previous version which will give you an error when you will try to use the plugin on Windows, due to the missing Microsoft.Toolkit.Notifications.Uwp library.

The purpose of this blog is how to solve a problem you may hit while preparing your app for publishing, so I won't explore all the different options you have to handle push notifications. I will just add a button the main page which will trigger a toast notification, which will be immediately displayed. To achieve this goal, I'm going to add a new button inside the default main page which is part of the standard Cordova template. You can find it under www -> index.html.

Here is the updated code of the body of the page:

 <div class="app">
    <h1>Apache Cordova</h1>
    <div id="deviceready" class="blink">
        <p class="event listening">Connecting to Device</p>
        <p class="event received">Device is Ready</p>
        <button id="sendNotificationButton">Send notification</button>
    </div>
</div>

Now let's open the file index.js under www -> scripts, which is the place where to add the JavaScript code we want to leverage from the main page. Inside the main function we're going to add a new one called sendNotification() , which will use the plugin we have just added to our project:

 function sendNotification() {
    cordova.plugins.notification.local.schedule({
        title: 'My first notification',
        text: 'Thats pretty easy...',
        foreground: true
    });

The code is pretty straightforward. We simply set the title and the text of the notification and we specify we want to display it in foreground. Since we haven't specified a date and time or a recurrence, the notification will be displayed immediately.

Then, inside the onDeviceReady() function (which is invoked when the application has completed the bootstrap process), we're going to hook this new function to the button we have added in the HTML page:

 document.getElementById("sendNotificationButton").addEventListener("click", sendNotification)

That's it! Now from Visual Studio choose Windows-x86 as target platform and press F5 to launch it. If you have done everything right, after pressing the button in the main page you should see a toast notification popping out in the bottom right corner of your screen:

Prepare for publishing

When you're ready to publish your application (either manually or on the Microsoft Store), you can use the same workflow you use with a traditional UWP application. Just right click on the project in Visual Studio, choose Store -> Create App packages and follow the wizard.
However, if you try to deploy the package created during the process, you will notice something weird when you press the button to trigger the notification:

As you can see from the image, the title and the text of the notification aren't anymore the values I've defined in the sendNotification() function. The title has been replaced by the name of the application, while the text by a generic "New notification" text.

What's wrong? The culprit here is .NET Native compilation or, to be more precise, the lack of a configuration file in the Windows project created by Cordova during the build process. .NET Native is a new compilation technique adopted by UWP apps, which helps to deliver faster apps with a reduced memory footprint. This is achieved by compiling the managed code directly in native code. This is the reason why the package creation process takes so long. Packages, in fact, are compiled in Release mode, which enables .NET Native by default. When you're in the development phase, instead, the app is compiled by default in Debug mode, which keeps .NET Native disabled in order to speed up the compilation and debugging times.

This is the reason why, when we tested the app before, we couldn't see the problem. When we pressed F5 in Visual Studio, we launched it in Debug mode and, as such, everything was working as expected. As soon as you create the package for the Store (or you simply switch to Release as compilation mode in Visual Studio), the notification will start to pop up with the wrong values.

Why is .NET Native causing this? The reason is that (as well explained here) .NET Native links implementation code into your app only if it knows that your app actually invokes that code. If it's not obvious that your application requires some the metadata or the implementation code, native application might be built without it. What's happening is that, since Visual Studio isn't aware of the usage of the plugin (the merging of the platform specific plugin's code with the base Cordova platform happens during the Cordova build process), it isn't including all the required classes in the generated native code. As such, the title and the text properties of the toast notification object are ignored.

The way UWP apps deal with this is by adding a Runtime Directives file (rd.xml) to the project, with all the information about which program elements are available for reflection. The reason why this problem is happening is that the default Cordova project for Windows lacks this file and, as such, ignores all the custom types defined by the notification plugin.

In order to fix this, we need to build the Cordova project for Windows at least once. This will trigger the download, inside the platforms folder of your application, of the base Windows project leveraged by Cordova. After that, open the folder which contains your project and move to platforms -> windows. Open with Visual Studio the solution called CordovaApp.sln. You won't be able to load two projects of the solution: CordovaApp.Windows and CordovaApp.Phone. This is expected because they're, respectively, the Windows 8.1 and the Windows Phone 8.1 project. Visual Studio 2017 doesn't include support for them. However, it isn't a problem because we're working on a UWP app, so we need to focus on the CordovaApp.Windows10 project.

Right click on it and choose Add -> New item. Look for the XML File template and name it Default.rd.xml. Copy and paste inside it the following content:

 <!--
    This file contains Runtime Directives used by .NET Native. The defaults here are suitable for most
    developers. However, you can modify these parameters to modify the behavior of the .NET Native
    optimizer.

    Runtime Directives are documented at https://go.microsoft.com/fwlink/?LinkID=391919

    To fully enable reflection for App1.MyClass and all of its public/private members
    <Type Name="App1.MyClass" Dynamic="Required All"/>

    To enable dynamic creation of the specific instantiation of AppClass<T> over System.Int32
    <TypeInstantiation Name="App1.AppClass" Arguments="System.Int32" Activate="Required Public" />

    Using the Namespace directive to apply reflection policy to all the types in a particular namespace
    <Namespace Name="DataClasses.ViewModels" Seralize="All" />
-->

<Directives xmlns="https://schemas.microsoft.com/netfx/2013/01/metadata">
  <Application>
    <!--
      An Assembly element with Name="*Application*" applies to all assemblies in
      the application package. The asterisks are not wildcards.
    -->
    <Assembly Name="*Application*" Dynamic="Required All" />
    
    
    <!-- Add your application specific runtime directives here. -->


  </Application>
</Directives>

With this configuration file we're simply telling to the .NET Native compiler that we want to enable reflection for all the assemblies included in the application package. Now close the solution and go back to your Visual Studio instance with your Cordova's application opened. Build it again choosing Release as compilation mode and Windows-x86 as target, then press F5. If you have done everything properly, the application will now work as expected and the content of the notification will be the one you have specified in the function's code.

Wrapping up

In this post we have seen how to support toast notifications in a Cordova app for Windows 10. Thanks to a community plugin the procedure is pretty easy, however there are a couple of issues you may face and the requires a couple of workarounds:

  1. Make sure to clone the plugin directly from GitHub, since the NPM version misses a library which is required by the Windows build
  2. You need to add a Runtime Directive file to the Windows project, otherwise the .NET Native compilation ignores the custom classes defined by the notification plugin.

Happy coding!