Scaling Up with the Notifications Hub - Learning Windows Azure Mobile Services for Windows 8 and Windows Phone 8 (2014)

Learning Windows Azure Mobile Services for Windows 8 and Windows Phone 8 (2014)

Chapter 6. Scaling Up with the Notifications Hub

The PNS facilities in Azure Mobile Services are great, but Azure has a more scalable solution, available to us from the Service Bus group of services.

The Notifications Hub has the following benefits over push notifications:

· Manages device URI handles for us

· Only requires a single request from the backend to broadcast notifications

· Offers generic notifications across all platforms as well as native notification types

· Tags to allow users to filter notifications

· Provides language support

The Notifications Hub flow is described as follows:

1. The mobile device establishes a channel with the PNS and retrieves its URI handle.

2. The device registers with the Notifications Hub.

3. A notification request is made by another service or an admin system to the hub.

4. The service makes a request for every device handle to the correct PNS.

5. The PNS notifies the device.

Scaling Up with the Notifications Hub

The main drawback of using the hub over Mobile Services push notifications is the separate pricing model. You get 1,00,000 pushes per month on 500 devices for free. On unlimited devices, you get 1 million pushes and 5 million pushes (per unit) for basic and standard subscriptions, respectively.

Configuring the Hub

First, we're going to configure our Notifications Hub in the Service Bus Portal. The steps are as follows:

1. In the Azure Portal, select SERVICE BUS from the left menu.

2. Click on CREATE A NAMESPACE.

3. Enter a name, select a region (pick the same one as you used for the database and mobile service), and choose a subscription:

Configuring the Hub

4. Click on the newly created namespace to enter the SERVICE BUS Portal:

Configuring the Hub

5. Select NOTIFICATION HUB from the menu.

6. Click on CREATE A NEW NOTIFICATIONS HUB.

7. From the pop-up menu, click on QUICK CREATE:

Configuring the Hub

8. Enter a name in the NOTIFICATION HUB NAME field and click on the CREATE A NEW NOTIFICATION HUB tick.

9. For Windows Store apps, under the Mobile Services Portal's CONFIGURE tab, copy the CLIENT SECRET and PACKAGE SID keys from the PUSH tab in the Mobile Services Portal, created when we configured push notifications in the previous chapter.Paste them into the windows phone notification settings section under the CONFIGURE tab.

Note

Note that, at the time of writing this, they were in the opposite order!

10. For Windows Phone 8 apps, under the CONFIGURE tab, check the Enable unauthenticated push notifications checkbox:

Configuring the Hub

If you have obtained a MPNS certificate, you can use it here to get un-throttled authenticated notifications.

Setting up Windows Store and Windows Phone 8 apps

The following procedure sets up hub notifications in Windows 8 and Windows Phone 8 apps:

1. Install the WindowsAzure.Messaging.Managed NuGet package by entering the following command in the Package Manager Console:

2. Install-Package WindowsAzure.Messaging.Managed

3. Add the following namespace references to the ChannelHelper (Windows Phone 8) or TileTapperPush (Windows 8) class we created in the previous chapter:

4. using Microsoft.Phone.Notification;

using Microsoft.WindowsAzure.Messaging;

5. Add the following constants to the top of the class and change the HUB_NAME constant to your hub name:

6. private readonly string HUB_NAME = "tiletapper";

private readonly string CONNECTION_STRING = "Endpoint=sb://tiletapper.servicebus.windows.t/;SharedAccessKeyName=DefaultListenSharedAccessSignature;SharedAccessKey=/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=";

7. In the NOTIFICATION HUBS Portal, click on the CONNECTION INFORMATION button on the toolbar to see the connection strings:

Setting up Windows Store and Windows Phone 8 apps

8. Copy the DefaultListenSharedAccessSignature string (use the copy button) and paste it into the CONNECTION STRING constant:

Setting up Windows Store and Windows Phone 8 apps

9. Add the following to the ChannelHelper.Register (Windows Phone 8) or TileTapperPush.UploadChannel (Windows 8) task:

10.// Register with hub

11.var hub = new NotificationHub(this.HUB_NAME, this.CONNECTION_STRING);

var result = await hub.RegisterNativeAsync(this._pushChannel.ChannelUri.AbsoluteUri);

12. For Windows Phone 8, uninstall the app, then re-deploy and run it to get the channel to refresh and register with the hub.

13. It may take a few minutes once the RegisterNativeAsync has been called for the channel to register and receive notifications.

Calling the hub from scripts

In the previous chapter, we talked about sending push notifications from our scripts using the PNS libraries. There isn't a built-in library for the Notifications Hub, but remember when we were looking at using NPM modules in our scripts? Well, we can pull in a reference to the Azure SDK for a Node NPM package, which is preinstalled in our service, so we don't even need to install it! The SDK is open source and can be found on GitHub at https://github.com/WindowsAzure/azure-sdk-for-node. It can be useful if you are having trouble finding examples of how to do certain things because you can look at the code.

If you remember in the previous chapter, we had to maintain a table of channels, then loop through the table, determine which provider to use, build a queue of PNS functions for each channel URI, and call them one at a time! Well, for the Notifications hub, this couldn't be simpler as we just need to make a single call to the hub for each notification type we want to send:

function sendAllHubNotifications(levelName)

{

sendToastHubMpns("TileTapper - New level available", levelName, null);

sendTileHubMpns("TileTapper - New level available", levelName, null);

sendToastHubWns("TileTapper", "New level available", levelName, null);

sendTileHubWns("TileTapper", "New level available", levelName, null);

sendBadgeHubWns("alert", null);

}

All PNS methods have prototypes similar to this (from SDK code):

MpnsService.prototype.send = function (tags, payload, targetName, notificationClass, optionsOrCallback, callback)

Most of the parameters are self-explanatory; however, the notificationClass controls the batching interval (you can read more on this at http://msdn.microsoft.com/en-us/library/hh221551.aspx).

All the scripts shown next use the same constants:

var CONNECTION_STRING = "Endpoint=sb://tiletapper.servicebus.windows.net/;SharedAccessKeyName=DefaultFullSharedAccessSignature;SharedAccessKey=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX=";

var HUB_NAME = "tiletapper";

Use DefaultFullSharedAccessSignature from the Notifications Hub Portal.

Creating WNS scripts (for Store apps)

We've already talked about the different notification types and templates, so we'll just look at the code.

Sending toast notifications

The following function sends a WNS toast notification:

function sendToastHubWns(text1, text2, text3, tagExpression)

{

var azure = require("azure");

var notificationHubService = azure.createNotificationHubService(HUB_NAME, CONNECTION_STRING);

var toast = "<toast>" +

"<visual>" +

"<binding template=\"ToastText04\">" +

"<text id=\"1\">" + text1 + "</text>" +

"<text id=\"2\">" + text2 + "</text>" +

"<text id=\"3\">" + text3 + "</text>" +

"</binding>" +

"</visual>" +

"</toast>";

notificationHubService.wns.send(tagExpression, toast, "wns/toast", 2, function(error) {

if (error) {

console.error(error);

}});

}

Sending tile notifications

The following function sends a WNS tile notification:

function sendTileHubWns(text1, text2, text3, tagExpression)

{

var azure = require("azure");

var notificationHubService = azure.createNotificationHubService(HUB_NAME, CONNECTION_STRING);

var tile = "<tile>" +

"<visual>" +

"<binding template=\"TileSquareText01\">" +

"<text id=\"1\">" + text1 + "</text>" +

"<text id=\"2\">" + text2 + "</text>" +

"<text id=\"3\">" + text3 + "</text>" +

"</binding>" +

"</visual>" +

"</tile>";

notificationHubService.wns.send(tagExpression, tile, "wns/tile", 1, function(error) {

if (error) {

console.error(error);

}});

}

Sending badge notifications

The following function sends a WNS badge notification:

function sendBadgeHubWns(value, tagExpression)

{

var azure = require("azure");

var notificationHubService = azure.createNotificationHubService(HUB_NAME, CONNECTION_STRING);

var badge = "<badge value=\"" + value + "\" />";

notificationHubService.wns.send(tagExpression, badge, "wns/badge", 2, function(error) {

if (error) {

console.error(error);

}});

}

Creating MPNS scripts (for Windows Phone 8 apps)

Again, we've already talked about the different notification types and templates, so we'll just look at the code.

Sending toast notifications

The following function sends a MPNS toast notification:

function sendToastHubMpns(text1, text2, tagExpression)

{

var azure = require("azure");

var notificationHubService = azure.createNotificationHubService(HUB_NAME, CONNECTION_STRING);

var toast = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +

"<wp:Notification xmlns:wp=\"WPNotification\">" +

"<wp:Toast>" +

"<wp:Text1>" + text1 + "</wp:Text1>" +

"<wp:Text2>" + text2 + "</wp:Text2>" +

"</wp:Toast> " +

"</wp:Notification>";

notificationHubService.mpns.send(tagExpression, toast, "toast", 2, function(error) {

if (error) {

console.error(error);

}});

}

Sending tile notifications

The following function sends a MPNS tile notification:

function sendTileHubMpns(backTitle, backContent, tagExpression)

{

var azure = require("azure");

var notificationHubService = azure.createNotificationHubService(HUB_NAME, CONNECTION_STRING);

var tile = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +

"<wp:Notification xmlns:wp=\"WPNotification\" Version=\"2.0\">" +

"<wp:Tile Template=\"FlipTile\">" +

"<wp:BackTitle>" + backTitle + "</wp:BackTitle>" +

"<wp:BackContent>" + backContent + "</wp:BackContent>" +

"<wp:WideBackContent>" + backContent + "</

wp:WideBackContent>" +

"</wp:Tile> " +

"</wp:Notification>";

notificationHubService.mpns.send(tagExpression, tile, "token", 1, function(error) {

if (error) {

console.error(error);

}});

}

Backend services

Similar to calling the Notifications Hub from our scripts, we can call it from any backend services we may have for generating app content and so on. To do this in a .NET application, follow this procedure:

1. Install the Windows Azure Service Bus SDK NuGet package by typing the following into the Package Manager Console:

Install-Package WindowsAzure.ServiceBus

2. Add the following namespace:

using Microsoft.ServiceBus.Notifications;

3. Add constants for the connection string and hub name:

4. private const string CONNECTION_STRING = "Endpoint=sb://tiletapper.servicebus.windows.net/;SharedAccessKeyName=DefaultFullSharedAccessSignature;SharedAccessKey=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=";

private const string HUB_NAME = "tiletapper";

5. We can then send notifications shown as follows:

6. private async Task SendToastHubMpns(string text1, string text2, string tagExpression)

7. {

8. NotificationHubClient hub = NotificationHubClient.CreateClientFromConnectionString(CONNECTION_STRING, HUB_NAME);

9. string toast = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +

10. "<wp:Notification xmlns:wp=\"WPNotification\">" +

11. "<wp:Toast>" +

12. "<wp:Text1>" + text1 + "</wp:Text1>" +

13. "<wp:Text2>" + text2 + "</wp:Text2>" +

14. "</wp:Toast> " +

15. "</wp:Notification>";

16. var result = await hub.SendMpnsNativeNotificationAsync(toast, tagExpression);

}

We'll not go into all the different notification types again as the payloads are very similar to the Node version.

Targeting audience using tags

The Notifications Hub has a concept of tagging notifications, whereby a user can pick the types of notifications they are interested in. The app registers these as tags and the backend service sends out tagged notifications, so users only get notifications they want to receive.

In the TileTapper game, I created a TagHelper class that allows the settings page to control notifications that the user wants to receive (via the view model):

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Threading.Tasks;

namespace TileTapper.Helpers

{

public class TagHelper

{

private const string TILE_HIGH_SCORE = "TILE_HIGH_SCORE";

private const string TOAST_HIGH_SCORE = "TOAST_HIGH_SCORE";

private const string TILE_LEVEL = "TILE_LEVEL";

private const string TOAST_LEVEL = "TOAST_LEVEL";

// Singleton instance

public static readonly TagHelper Default = new TagHelper();

private TagHelper()

{

}

public bool IsTileHighScoreEnabled

{

get { return StorageHelper.GetSetting<bool>(TILE_HIGH_SCORE); }

set { StorageHelper.StoreSetting(TILE_HIGH_SCORE, value, true); }

}

public bool IsToastHighScoreEnabled

{

get { return StorageHelper.GetSetting<bool>(TOAST_HIGH_SCORE); }

set { StorageHelper.StoreSetting(TOAST_HIGH_SCORE, value, true); }

}

public bool IsTileLevelEnabled

{

get { return StorageHelper.GetSetting<bool>(TILE_LEVEL); }

set { StorageHelper.StoreSetting(TILE_LEVEL, value, true); }

}

public bool IsToastLevelEnabled

{

get { return StorageHelper.GetSetting<bool>(TOAST_LEVEL); }

set { StorageHelper.StoreSetting(TOAST_LEVEL, value, true); }

}

public IEnumerable<string> GetTags()

{

var tags = new List<string>();

if (this.IsTileHighScoreEnabled)

tags.Add(TILE_HIGH_SCORE);

if (this.IsToastHighScoreEnabled)

tags.Add(TOAST_HIGH_SCORE);

if (this.IsTileLevelEnabled)

tags.Add(TILE_LEVEL);

if (this.IsToastLevelEnabled)

tags.Add(TOAST_LEVEL);

return tags;

}

}

}

When these change we need to re-register our channel with the hub with the list of tags like this:

// Register with hub

var tags = TagHelper.Default.GetTags();

var hub = new NotificationHub(this.HUB_NAME, this.CONNECTION_STRING);

var result = await hub.RegisterNativeAsync(this._pushChannel.ChannelUri.AbsoluteUri, tags);

Now, when we send notifications in our service, we can add tags to the requests as follows:

// Hub functions

function sendAllHubNotifications(levelName)

{

sendToastHubMpns("TileTapper - New level available", levelName, "TOAST_LEVEL");

sendTileHubMpns("TileTapper - New level available", levelName, "TILE_LEVEL");

sendToastHubWns("TileTapper", "New level available", levelName, "TOAST_LEVEL");

sendTileHubWns("TileTapper", "New level available", levelName, "TILE_LEVEL");

sendBadgeHubWns("alert", "BADGE_LEVEL");

}

At the time of writing this, there seems to be an issue with this working on Windows Phone; however, it works fine on Windows 8.

Summary

In this chapter, we've seen how using the Notifications Hub can save us a lot of work managing push notifications. It is probably a better choice over the built-in push notifications support in the service.

The hub also offers a really good template feature that allows apps to register templates for notification categories that the user is interested in, with just a single notification request required on the server side. This is very powerful as we can target multiple platforms with one request and provide localization support. Unfortunately, we do not have the time to look at this now, but there are some good references:

· http://msdn.microsoft.com/en-us/library/windowsazure/dn530748.aspx

· http://www.windowsazure.com/en-us/manage/services/notification-hubs/breaking-news-localized-dotnet/

Next is the final chapter in which we are going to look at tying up everything we've learned so far in the book and getting our apps ready for the store!