Activating a Progressive Web App on Windows 10 using a toast notification


If you have some familiarity with Progressive Web Apps or if you have read the great blog post by my team mate Mike on how to integrate notifications in a Progressive Web App, you’ll already know that web applications running on Windows 10 have a big advantage. When they are packaged as an app, in fact, even if all the code is stored server side, they can leverage native Windows APIs through JavaScript. This allows a Progressive Web App to perform operations that aren’t typically exposed to the web platform, putting them in pair with a native app in terms of features and capabilities.

One of the many features that you can expose from your application are toast notifications, which can leverage the new rich adaptive model that was introduced with the first release of Windows 10. If you already have some experience with UWP, you know that sending a toast notification within the app is a quite straightforward operation. A toast notification is made by a XML payload, which is stored inside a XML object and then delivered using the Windows.UI.Notifications.ToastNotificationManager API.

However, when it comes to Progressive Web Apps, a challenge may come when it comes to handle the activation from the toast notification. You want your app, in fact, to deliver notifications that can be actionable and can lead the user to the right context. If my application displays a toast notification about a breaking news, I want to bring the user directly to read it when he clicks on the notification, not to a random page. Here comes the challenge. In a regular UWP app built with C# / C++ and XAML, you have the App class which acts as an entry point. By default, it takes care of initializing all the required app infrastructure and then it redirects the user to the main page. However, you can override multiple activation events, to handle the scenario where the app can be opened using a different entry point than clicking on the icon or the tile in the Start menu. Some examples are voice commands, protocol activation, secondary tiles or... toast notifications!

How can we implement the same approach in a Progressive Web App, since we don’t have an App class? In such apps, in fact, usually the entry point is simply the main page of the website.

The answer is leveraging some specific WinRT APIs, which can be invoked directly from the main page of your application through JavaScript. Let’s see how to do it!

Sending the toast notification

For the purpose of this demo, I’ve created an ASP.NET Core 2.0 website using the standard Visual Studio template and I’ve published it on Azure using a Web Aapp. The template can be found in Visual Studio by choosing New –> Project –> Visual C# –> Web –> ASP.NET Core Web Application. I won’t go into details on ASP.NET Core or the Azure publishing, since they would be out of scope. We’re just going to use some plain HTML and JavaScript to accomplish our goal. Azure App Service and the ASP.NET Core template are just an easy way to quickly get a website up & running that I can package as a Progressive Web App.

In this demo I’m going to take the existing template and make some small changes. In the home page, I’m going to add some buttons to send a toast notification. Each toast notification will redirect the user to a specific news, by including a reference to its unique identifier. When the user clicks on a toast notification he will be redirect to a dedicated News page of the website, which will show the details of the selected news.

Let’s start digging into the code and see the JavaScript code we can use to send the toast notification. I’m going to add this code to the main page of the website which, in case of the standard ASP.NET Core template, is the one called Index.cshtml inside the Pages folder.

<script type="text/javascript">
    if (typeof Windows !== 'undefined' &&
        typeof Windows.UI !== 'undefined' &&
        typeof Windows.UI.Notifications !== 'undefined') {

        function showToast(newsId) {
          
            var notifications = Windows.UI.Notifications;

            // Get the toast notification manager for the current app.
            var notificationManager = notifications.ToastNotificationManager;

            // The getTemplateContent method returns a Windows.Data.Xml.Dom.XmlDocument object
            // that contains the toast notification XML content.
            var xml = '<toast launch="' + newsId +'"><visual><binding template="ToastGeneric"><text>Breaking news!</text><text>Click here to open the news</text></binding></visual></toast>';
            var toastXml = new Windows.Data.Xml.Dom.XmlDocument();
            toastXml.loadXml(xml);

            // Create a toast notification from the XML, then create a ToastNotifier object
            // to send the toast.
            var toast = new notifications.ToastNotification(toastXml);

            notificationManager.createToastNotifier().show(toast);
        }
    }
</script>

The first thing we do is checking if the Windows property returns a value. This condition is true when the website is running packaged as an app and not directly inside a browser, which means that we have access to all the UWP APIs exposed by Windows 10. Without checking for this condition first, the user would start to get multiple errors when he opens the website using his favorite browser, since all the JavaScript APIs to access to UWP features would fail. If you have already worked with UWP notifications before, the code should be simple to understand, since conceptually it’s the same one we use in a C# application. We create a new XmlDocument object with the XML payload of the toast notification that we want to send. Then we load it inside a ToastNotification object and we send it using the ToastNotificationManager class and the show() method. We’re using a very minimal template for the notification, with just a title and a message that inform the user that there’s a breaking news. The selected news identifier is passed as a parameter of the showToast() function.

In the XML you can see the first step in handling the activation. We are passing the news identifier inside the launch attribute of the <toast> element. This parameter can be retrieved inside the activation event and we can use it to determine the context of the notification, so that we can redirect the user to the correct news.

The last step is to connect a button in the page to this function, so that when the user clicks on it the notification is sent. To do that I’ve removed most of the HTML code stored in the Index.cshtml page and I’ve left only one div block with the following code:

<div class="row">
    <div class="col-md-3">
        <h2>Toast notifications</h2>
        <ul>
            <li><button onclick="showToast('1');">Show toast for News 1</button></li>
            <li><button onclick="showToast('2');">Show toast for News 2</button></li>
            <li><button onclick="showToast('3');">Show toast for News 3</button></li>
        </ul>
    </div>
</div>

Nothing special here. Each button simply invokes the showToast() function we have just declared, passing each time a different parameter (which represents a different news identifier).

Testing the application

If you want to see the look & feel of the changes we’ve made it’s enough to press F5 to deploy the website locally and see the output in your favorite browser:

image

However, if you click on any of the buttons nothing will happen. This is the expected behavior, since we have made the showToast() function available only if we are running in the context of a Progressive Web App in Windows. Since we are just running inside Edge, the code is ignored and no JavaScript errors are raised. In order to try the code, we need first to package the website as an application. The easiest way is to publish the website somewhere and then create a Progressive Web App which points to the website’s URL thanks to the dedicated Visual Studio template. In my case, I’ve used a Web App hosted on Azure: the free plan is more than enough for my testing purposes and the publishing flow is extremely easy, since it’s integrated in Visual Studio. You can just right click on the website’s project, choose Publish and then you’ll be able to login to your Azure account and choose one of the existing App Services or create a new one directly from the wizard. At the end of the process, your website will be available at a URL like yourcustomname.azurewebsites.net. If you don’t have the chance to publish your website, you can leverage the one I have already published at https://toastwebsite.azurewebsites.net/

The easiest way to create your PWA project is to use the PWA Builder tool. It’s an open source tool developed by Microsoft and it’s available at https://www.pwabuilder.com/ The first step is to specify, in the main page, the URL of your website:

image

Click on Get started. The next page will allow you specify all the information to generate the manifest, which is the file that describes the look & feel of your website when it’s packaged as an application (name, icons, default orientation, etc.). For our testing purposes, we can leave all the default values and just press Next step at the end of the page. The next section is about the generation of the service worker, which is the component leveraged by a Progressive Web App to handle offline scenarios. For the purpose of my demo, I’ve just opted in to handle Offline pages and I’ve clicked Next step. Since the goal of this post is to show you how to handle toast notifications in Windows 10 and not how to build a full PWA app (there are plenty of good tutorials out there, like this one), I’m not going to deploy the service worker on my website since I’m not interested in enabling offline management (but if you’re building a real product, you should absolutely do it!).

In the last page you can download the various packages that the tool has generated for you starting from your website. There’s one for each different platform: web, Windows, Android and iOS. In our case, let’s download the Windows one by clicking on the Download button under the Windows section:

image

The downloaded zip file will contain multiple projects, including a UWP JavaScript one for our PWA on Windows 10. You can find it inside projects/PWA/Store packages/windows10/source. Just unpack it somewhere and open the App.jsproj file with Visual Studio (or add it to your existing Visual Studio solution with the ASP.NET Core project). You will notice that the project is basically empty, except for the UWP manifest (Package.appxmanifest) and some default assets. In a PWA, in fact, all the code is stored server side, so the manifest simply sets as entry point for the app the URL of our website. You can see it by double clicking on the Package.appxmanifest file. In the Application tab, the Start page will be set to your website’s URL:

image

In the Content URIs section, instead, you will find the same URL whitelisted:

image

As you can see the option WinRT Access is set to All. This means that our website will be able to use server side code to access to the UWP APIs. Without this entry in the manifest, the code we have written before to send a toast notification wouldn’t work even when the website is running packaged as an app.

Now you can press F5 and deploy the application on your machine. The look & feel will be exactly the same of your website running in a browser. However, if you click on one of the buttons we have previously added, this time you will see a toast notification popping out on the bottom right corner of the screen:

SNAGHTML57c8dac

It works! However, we still miss one piece of the puzzle. If you click on it, in fact, your application will simply be opened, without respecting the news choice made by the user. The reason is that we aren’t handling the activation event, so the application is simply just launched. Let’s see how to fix this.

Registering for activation

We’re going to add some code inside the Index.cshtml page. Also this new snippet must be included inside the Windows condition checking, to avoid executing it when the website is running inside a regular browser. Here is the full code:

<script type="text/javascript">
    if (typeof Windows !== 'undefined' &&
        typeof Windows.UI !== 'undefined' &&
        typeof Windows.UI.Notifications !== 'undefined') {

        Windows.UI.WebUI.WebUIApplication.addEventListener('activated', function (args) {
            if (args.kind === Windows.ApplicationModel.Activation.ActivationKind.toastNotification) {
                console.log('app activated');
                var newsId = args.argument;
                window.location.href = 'https://toastwebsite.azurewebsites.net/News?newsId=' + newsId;
            }
        });

    }
</script>

Using a UWP object (Windows.UI.WebUI.WebUIApplication.addEventListener) we subscribe to an event called activated. It’s the one triggered when the application is opened and it can have different types, based
on the way it has been activated. In case it’s launched because the user has clicked on a toast notification, the kind property of the function arguments will be equal to Windows.ApplicationModel.Activation.ActivationKind.toastNotification.

In this scenario, we can leverage another property of the function’s arguments, called argument, which contains the value we have set in the launch attribute of the XML payload of the toast notification. In our case, this value will be equal to the id of the news that has been requested by the user. We append this value as URL parameter and we redirect the user, using window.location.href, to the news landing page. Important! For security reasons, when the website is running packaged as an application, setting the window.location.href property only works if the new page is in the same domain of the current page.

The last step is to create the News landing page. In this case, since I’ve built an ASP.NET Core 2.0 website based on Razor pages, I’m going to add a new Razor page called News by right clicking on the Pages folder in my project and choosing Add –> Razor page.

In the code of the page I simply take care of retrieving the URL parameter which contains the news identifier and store it in a public property, so that I can expose it to the HTML. Razor pages makes this operation really easy:

public class NewsModel : PageModel
{
    public string NewsId { get; set; }

    public void OnGet(string newsId)
    {
        NewsId = newsId;
    }
}

It’s enough to add to the OnGet() function (which is hit every time the page is requested) a parameter with the same name of the URL parameter (newsId) and the framework will take care of injecting inside it the value taken from the URL. After we have stored the value in a public property, we can add the following in the News.cshtml file:

<h2>News @Model.NewsId</h2>
<p>This is a news</p>

Thanks to the @Model.NewsId property, we’re going to display the id of the news in the title of the page. If you want to test this, it’s enough to open this new page appending a random newsId parameter, for example https://toastwebsite.azurewebsites.net/News?newsId=5. You should see something like this:

image

Now publish the changes on your web server, so that we can try the Progressive Web App. Deploy it from Visual Studio, send a notification by pressing one of the buttons and click on the notification that is displayed in the bottom right corner. If you did everything well, you should be redirected to the News page instead of the main one. Additionally, the title of the page should display News X, where X can be 1, 2 or 3 based on the button you have pressed to generate the notification.

Awesome, isn’t it?

Debugging the application

Debugging the JavaScript code you have just written can be a bit painful because when we are launching the Progressive Web App the website isn’t running locally, but hosted on our web server. Using the dev tools built-in in Edge would be very helpful, but the website isn’t running in the browser, so we can’t simply press F12 to open them.

With the purpose to facilitate this scenario, the Edge team has released on the Microsoft Store a preview app called Microsoft EdgeTools Dev Preview, which can be downloaded from https://www.microsoft.com/store/productId/9MZBFRMZ0MNJ

This application includes the same exact dev tools which are built-in in Edge but, being exposed as a separate app, they can be attached to any other running process on the system or even on a remote one. When you launch it, you will see a list of processes on your machine which are rendering web content through Edge:

image

As you can see, one of the listed processes is exactly our Progressive Web App. Simply click on it and the debugger will be attached, allowing you to perform the same kind of testing you do with Edge, like placing breakpoints in JavaScript code or exploring the network trace. For example, if you open the Debugger section, in the left tree you can click on the URL of your domain and look for the JavaScript code you have added before to the main page of the website. Then you can place a breakpoint inside the registration of the activated event and see what happens when the application is started from a toast notification:

image

Wrapping up

In this blog post we have seen how to send a native Windows toast notification from a Progressive Web App running on Windows but, most of all, how to handle the activation process. This way, we can deliver a better user experience and make the website look more like a real native application. We have seen also how, thanks to the Microsoft Edge DevTool Preview app, we can easily debug a Progressive Web App, despite it runs outside the browser’s context.

Thanks to this approach, we can empower the rich toast model offered by the Windows platform. However, if you want to deliver a true cross-platform Progressive Web App, my suggestion is also to support a fallback using Web Notifications API, so that notifications can work also when your website is used on another device like an iPhone or an Android phone.

You can download the sample website and PWA created for this project from the official Windows AppConsult GitHub repository at https://github.com/Microsoft/Windows-AppConsult-samples-PWA/tree/master/ToastActivation

Happy coding!

Comments (0)

Skip to main content