[Azure] Custom Function bindings + notification tags in Cordova apps

Previously I explained how I am using an Azure Notification Hub to send out notifications to a mobile application made with Cordova (read it here). This is cool, but in that scenario every notification was being sent out to every client. This is fine for some situations but in most cases you probably want some mechanism to send out notification to specific devices or a group of people. The most used example is news: you subscribe to a couple of subjects and receive only notifications for messages linked to one of those subjects. This post details how you can achieve this. 

Tags

Azure Notification Hubs support using tags. A tag is nothing more than a string. The sender can add tags to the message being sent. The goal is to make sure only devices listening to that specific tag will receive the message. Since each tag is a single string, you cannot do key/value pairs as such, but of course you can specify something similar like:

“deviceid:mydevice”

The method to append tags to a message depends on how you are sending that message. The article found here details how to push messages from your back-end, but it does not explicitely show how to deal with tags. Basically the SDK allows you to specify a collection of strings when posting your message, like this:

Dictionary<string, string> templateParams = new Dictionary<string, string>();
templateParams["messageParam"] = "Test notification";

string[] tags = new string[] { "deviceid:mydevice" };

var result = await _hub.SendTemplateNotificationAsync(templateParams, tags);

Ok cool. But in my case I’m using an Azure Function to send out notification message. The function will have a binding to the notification hub which has a “TagExpression” property. That’s nice, but it only allows for a static value and most likely you’ll want to determine the tag dynamically inside of your function code.

 

Dynamic Function bindings

In order to have more control of the binding, we can use a “dynamic function binding”. Basically this means we’ll delete the pre-configured binding from the function and instead create a binding at runtime in our code. This process is not yet that well documented, but once figured out it’s pretty straightforward.

What you’ll need is to add a parameter to your function named IBinder binder. The function runtime will then provide a binder object which you can use to dynamically create new bindings. With this binder object, we can now create a new binding as follows:

string devicetag = "deviceid:" + eventData.deviceid;

var attribute = new NotificationHubAttribute
{
    ConnectionStringSetting = "repsaj-submerged-notificationhub_NOTIFICATIONHUB",
    HubName = "repsaj-submerged-notificationhub",
    TagExpression = devicetag
};

IAsyncCollector<Notification> notifications = binder.Bind<IAsyncCollector<Notification>>(attribute);

The NotificationHubAttribute object is what we use to specify the binding parameters, which are basically the same things you would specify in the function settings otherwise. But now we’ve got this in code, we can more dynamically determine the values of these parameters. You could use the same principle to pick a different notification hub instance for instance. See how the TagExpression member is now set to a string of our choice, dynamically constructed using the incoming event data from the Function.

Note: the NotificationHubAttribute is not available by default. To use it, we need to add a #r reference on line 1 of the function:

#r "D:\Program Files (x86)\SiteExtensions\Functions\0.3.10261\bin\Microsoft.Azure.WebJobs.Extensions.NotificationHubs.dll"

We can now start adding notifications to the resulting IAsyncCollector object, like this:

message = $"{{'data':{{'message':'{message}'}} }}";
var notification = new GcmNotification(message);
await notifications.AddAsync(notification);

And that’s it! Your notification will now be sent out without the need for additional plumbing to get the actual notification hub object and such. You can check out the complete Function runtime code here.

Note: as me, you might find it weird to add the tags to the binding. Adding them to the GcmNotification object feels more natural. I agree, but the Tags member for that object has been deprecated and I did not succeed in finding another way to achieve the same. If you did, drop it in comments below!

What about the client?

As soon as you start to send out messages with tags, you’ll notice your client will stop receiving any of them. This makes sense as these messages are now targeted to clients listening for these tags and yours is not doing that… yet.

The tricky part of setting up the client is that the client SDK does not support subscribing to tags directly from the client. A little bit more background info can be found here, seems to have something to do with security although honestly I don’t exactly understand why this is. Instead, we’ll need to create a server side API which handles this process for the client. The good thing is that this is fairly simple and only requires the registrationId that you already have when your device is properly registered to receive push notifications.

Let’s start with the most important code:

NotificationHubClient _hub = NotificationHubClient.CreateClientFromConnectionString(notificationHubConnection, notificationHubName);

public async Task CreateOrUpdateInstallationAsync(string installationId, string registrationId, IEnumerable<string> tags)
{
    Installation installation = new Installation();
    installation.InstallationId = installationId;
    installation.PushChannel = registrationId;
    installation.Tags = tags.ToArray();
    installation.Platform = NotificationPlatform.Gcm;

    await _hub.CreateOrUpdateInstallationAsync(installation);
}

This function takes three parameters:

  • installationId: this is the installationId of your client. If you’re using the Azure mobile services client SDK, this ID will be sent as an HTTP header in every request with the name “X-ZUMO-INSTALLATION-ID”. You can get it like this:
  • string installationId = this.Request.Headers.GetValues("X-ZUMO-INSTALLATION-ID").First();
  • registrationId: this is the ID that your client gets after registering with the push service. Typically you’d have something like this in your code:
    this.pushRegistration.on('registration', (data) => {
        this.registrationId = data.registrationId;
        this.registerPush();
    });
    

    simply save the registrationId for later use and pass it to your API method.

  • tags: that’s the part we’re most interested in in this case. Simply put in any collection of strings as mentioned above.

So once you’ve wrapped this function in an API method and call that from your client; you should be good to go! The code will register the correct tags with the notification hub and that will now take care of routing the correct messages to your client. As long as you have matching tags with both client and sender, your notifications will start arriving again! Happy notifying!

, , , ,

Related posts

Long Term Support… or not?

Previously I explained how I am using an Azure Notification Hub to send out notifications to a mobile application made with Cordova (read it here). This is cool, but in that scenario every notification was being sent out to every client. This is fine for some situations but in most cases you probably want some mechanism to send out notification to specific devices or a group of people. The most used example is news: you subscribe to a couple of subjects and receive only notifications for messages linked to one of those subjects. This post details how you can achieve this. 

[DevOps] Should you migrate onto YAML release pipelines?

Previously I explained how I am using an Azure Notification Hub to send out notifications to a mobile application made with Cordova (read it here). This is cool, but in that scenario every notification was being sent out to every client. This is fine for some situations but in most cases you probably want some mechanism to send out notification to specific devices or a group of people. The most used example is news: you subscribe to a couple of subjects and receive only notifications for messages linked to one of those subjects. This post details how you can achieve this. 

Latest posts

Long Term Support… or not?

Previously I explained how I am using an Azure Notification Hub to send out notifications to a mobile application made with Cordova (read it here). This is cool, but in that scenario every notification was being sent out to every client. This is fine for some situations but in most cases you probably want some mechanism to send out notification to specific devices or a group of people. The most used example is news: you subscribe to a couple of subjects and receive only notifications for messages linked to one of those subjects. This post details how you can achieve this. 

[DevOps] Should you migrate onto YAML release pipelines?

Previously I explained how I am using an Azure Notification Hub to send out notifications to a mobile application made with Cordova (read it here). This is cool, but in that scenario every notification was being sent out to every client. This is fine for some situations but in most cases you probably want some mechanism to send out notification to specific devices or a group of people. The most used example is news: you subscribe to a couple of subjects and receive only notifications for messages linked to one of those subjects. This post details how you can achieve this. 

Leave a Comment

Leave a Reply

Your email address will not be published. Required fields are marked *