W3C’S recent first draft publication of web badging API, a feature that was previously only available on iOS and macOS targets of Uno Platform, has finally made its way to Android and web platforms. The Badging API gives web developers a means of setting a badge on a document or application, to act as an identifier that the state has changed without displaying a more distracting notification.
In this article we will share with you how Martin Zikmund, a mobile/cloud developer and Uno Platform contributor, implemented the BadgeUpdateManager API in Uno Platform WebAssembly using this new API.
Web Badging API
The web Badging API provides a very simple set of methods to set and clear the badge:
await navigator.setAppBadge(42); // Sets the app badge to 42 await navigator.clearAppBadge(); // Clears the app badge
There is also a similar set of methods setClientBadge
and clearClientBadge
– these are very similar, but can only be used when the app is running (whereas setAppBadge
and clearAppBadge
can also be utilized from service worker).
It is also important to note, that the badge is only set when the app is running as PWA on the device – it will not be applied when the app is just running as a tab within the context of a browser (at least currently).
Badge notifications in UWP
To support badge notifications in Uno Platform, we need to map this functionality on the UWP API. At the core is the BadgeUpdateManager
class, which first provides us with a XML template which we can modify and then create a BadgeNotification
. After that, we can use BadgeUpdater.Update
to send the notification.
var badgeXml = BadgeUpdateManager.GetTemplateContent(BadgeTemplateType.BadgeNumber); XmlElement badgeElement = badgeXml.SelectSingleNode("/badge") as XmlElement; badgeElement.SetAttribute("value", BadgeTextBox.Text); var badgeNotification = new BadgeNotification(badgeXml); BadgeUpdateManager.CreateBadgeUpdaterForApplication().Update(badgeNotification);
Badge Notification
Let’s start with the BadgeNotification
class. This one is fairly easy, as it is just a container for the customized XML template of the notification.
namespace Windows.UI.Notifications { public partial class BadgeNotification { public BadgeNotification(XmlDocument content) { Content = content ?? throw new ArgumentNullException(nameof(content)); } public XmlDocument Content { get; } } }
I am skipping here over a “small detail” which is the XmlDocument
implementation. UWP uses a custom set of XML classes here, which closely mimic the built in .NET System.Xml
namespace classes. Implementing these was a necessary prerequisite and I did that in another PR, which you can find here, in case you are interested.
BadgeUpdateManager
Next up, BadgeUpdateManager
. For our purposes we need just two methods
public static partial class BadgeUpdateManager { public static BadgeUpdater CreateBadgeUpdaterForApplication() => new BadgeUpdater(); public static XmlDocument GetTemplateContent(BadgeTemplateType type) { // Although UWP has two "template types", both return the same XML. var xml = new XmlDocument(); xml.LoadXml("<badge value=\"\" />"); return xml; } }
CreateBadgeUpdaterForApplication
just creates a new instance of BadgeUpdater
which we will look into next. GetTemplateContent
should return the XmlDocument
template that the developer then modifies to set the badge value. Interestingly, even though UWP supports two different badge template types, both actually return the exact same XML, so that’s also reflected in our code.
BadgeUpdater
Here comes the most interesting part of our implementation: BadgeUpdater
this is the class that is actually responsible for applying the badge on the app icon.
Uno Platform has many platform targets, so usually implementations of various UWP/WinUI APIs have some shared logic which leads to a platform specific handling in the end.
Let’s focus on the shared part first:
public partial class BadgeUpdater { private const string BadgeNodeXPath = "/badge"; private const string ValueAttribute = "value"; internal BadgeUpdater() { InitPlatform(); } partial void InitPlatform(); public void Update(BadgeNotification notification) { if (notification is null) { throw new ArgumentNullException(nameof(notification)); } var element = notification.Content.SelectSingleNode(BadgeNodeXPath) as XmlElement; var attributeValue = element?.GetAttribute(ValueAttribute); SetBadge(attributeValue); } public void Clear() => SetBadge(null); partial void SetBadge(string value); }
The constructor just performs a platform-specific initialization (if there is need for one). In the Update
method, we essentially “unpack” the interesting part of the badge notification’s XML (we are only interested in the value
attribute’s value). When we have it, we send it to the partial method called SetBadge
, which is platform specific and will handle the rest. Finally, the Clear
method just calls SetBadge
again, just with a null
argument.
So how does the WebAssembly platform-specific code look? Let’s see!
public partial class BadgeUpdater { private const string JsType = "Windows.UI.Notifications.BadgeUpdater"; partial void SetBadge(string? value) { if (int.TryParse(value, out var number)) { WebAssemblyRuntime.InvokeJS($"{JsType}.setNumber({number})"); } else { WebAssemblyRuntime.InvokeJS($"{JsType}.clear()"); } } }
As the Web Badging API can only handle numeric badges, we parse the string
to an int
, and if successful, we invoke a TypeScript function, which is conveniently exposed on a class with the same name and namespace. For clearing the badge, we have another function with the same name.
TypeScript Implementation
So the final piece of our puzzle is the TypeScript counterpart of the BadgeUpdater
.
interface Navigator { setAppBadge(value: number): void; clearAppBadge(): void; } namespace Windows.UI.Notifications { export class BadgeUpdater { public static setNumber(value: number) { if (navigator.setAppBadge) { navigator.setAppBadge(value); } } public static clear() { if (navigator.clearAppBadge) { navigator.clearAppBadge(); } } } }
As we did most of the heavy lifting in the managed code, we essentially just forward our “request” to the Web Badging API here. Note I needed to declare the methods in the Navigator
interface, as the type definitions were not yet available in the TypeScript version Uno Platform is currently using (mostly because the Web Badging API is still a draft). In addition, I needed to omit the fact that the Web Badging API is using Promises. Unfortunately UWP/WinUI does not offer awaitable version of the BadgeUpdater
methods, so we need to assume the user will not call these in quick succession and that the previous request will predictably be resolved before the next one
Credit goes to Martin Zikmund, an Uno Platform contributor, for providing a detailed breakdown of how to implement the BadgeUpdateManager API in Uno Platform WebAssembly.
Uno Platform’s annual conference – UnoConf 2021 will be taking place virtually this November 30th, 2021. This is a perfect opportunity to learn more about Uno Platform and the future of cross-platform development. Register now to save your spot and join from around the world for the largest conference event dedicated to Uno Platform.