Mike Taulty's Blog
Bits and Bytes from Microsoft UK
“Point and Click” Cloud Push Notifications with Azure Mobile Services

Blogs

Mike Taulty's Blog

Elsewhere

Archives

I spotted an email thread internally where someone was talking about how it’s possible to use Azure Mobile Services to bootstrap cloud-delivered push notifications into your app by just pretty much pointing and clicking inside of Visual Studio.

I’ve demo’d this in a number of places and I’ll be demo’ing it again next week at a conference but I thought I’d write it up here.

So, here’s my step-by-step, point-and-click guide to how to get the very basics of Azure Mobile Services push notifications into (e.g.) a Windows 8.1 Store app. I think it’s testament to how far Visual Studio tooling has come with respect to accessing AMS bits from the cloud and I suspect that this will only continue to improve over time (of course, you don’t have to have Visual Studio to do this stuff, you might be an Android developer and want to do these operations from the Azure portal or from the REST API - - that’s all fine).

Step 1 – Make a new Project.

I start with a blank application in Visual Studio;

image

Step 2 – Create a Mobile Service

Then use Server Explorer to make a new Mobile Service in Visual Studio;

image

in order to do that you need to connect your Azure account to Visual Studio. If you haven’t done that you can use;

image

and that should lead you through the process of getting those things set up. With that working, I can create a new Mobile Service;

image

and with that last click of the Create button I have a service listening at http(s)://mtBlogPost.azure-mobile.net (note, I’ll have taken it down by the time this post gets published).

3 – Add Push Notifications

Now, if I was in a weird world where the only thing that I want to do with this service is send a push notification then I can add that straight away to my project. These involves a bunch of wizards which are really quite helpful although possibly quite confusing if you haven’t manually been through the steps that they are shortening for you.

If I go to the project and “Add”;

image

Then this is going to walk me through a number of steps with the first being to create an entry in the Windows Store for the project that I’m working on in order that push notifications via the Windows Notification Service can work.

You need to have an entry in the Store to make WNS work which means you need a developer account. It doesn’t mean that you ever need to publish the app.

This wizard does this for you automatically so that you get led through;

image

then signing it to your developer account on the Store;

image

then making an application name;

image

then selecting a Mobile Service;

image

and then pressing “go”;

image

and this process is done.

Step 4 – Figuring out What Just Happened

The first time you do this and especially if you’ve never executed the steps yourself you might now be thinking “What did that just do?”. It does a few things;

Added References to my Project

Because I didn’t already have the right .NET references in my project the wizard added (at least – i.e. I think it added Tasks, Http as well);

image

Made an Association with the Store

The wizard also made an association between the project that I’m working on in Visual Studio and the app that I called “mtBlogPost” in the Windows Store.

Essentially, it’s doing the same work as you do when you use this menu option in Visual Studio;

image

which involves copying a bunch of identity information from the Store’s metadata into the application package.

Created a Table for my Mobile Service

The wizard also added a table into my mobile service. That table is called channels;

image

The way that push notifications work is that you need;

  1. A URI which uniquely identifies the app for the user on the device. It might also identify (in the case of secondary tiles) a specific notification endpoint for that app for that user on that device. This URI is obtained by making an API call on the device.
  2. To store that URI somewhere in the cloud with perhaps some association to the user in question (unless you only want to do a broadcast to all/groups of users) such that in the future you can retrieve the right URI to send them a notification.
  3. Some piece of software somewhere (usually it’s a cloud service) decides that the user needs notifying of something. It may be the mobile service itself or some external bit of code elsewhere. Anyway, the mobile service can look up the right URI for the user and can then do an OAuth authentication across to the WNS service asking it to send one of many different formats of notification (tiles, toasts, badges, raw) to that specific URI.
  4. The WNS service works with the device (somehow – never seen it specified) to get the notification to the device.

The intention of this channels table is to provide a place to do (2) from the list above – store URIs to identify a way to send a notification to a user.

Created an Insert Script for the Channels Table

To go alongside that channels table, the wizard also created a non default script which runs on the server side whenever there’s an insert into the channels table. It looks like;

// See documentation at http://go.microsoft.com/fwlink/?LinkId=296704&clcid=0x409
function insert(item, user, request) {

    // The following call is for illustration purpose only
    // The call and function body should be moved to a script in your app
    // where you want to send a notification
    sendNotifications(item.channelUri);
 
    // The following code manages channels and should be retained in this script
    var ct = tables.getTable("channels");
    ct.where({ installationId: item.installationId }).read({
        success: function (results) {
            if (results.length > 0) {
                // we already have a record for this user/installation id - if the 
                // channel is different, update it otherwise just respond
                var existingItem = results[0];
                if (existingItem.channelUri !== item.channelUri) {
                    existingItem.channelUri = item.channelUri;
                    ct.update(existingItem, {
                        success: function () {
                            request.respond(200, existingItem);
                        }
                    });
                }
                else {
                    // no change necessary, just respond
                    request.respond(200, existingItem);
                }
            }
            else {
                // no matching installation, insert the record
                request.execute();
            }
        }
    })

    // The following code should be moved to appropriate script in your app where notification is sent
    function sendNotifications(uri) {
        console.log("Uri: ", uri);
        push.wns.sendToastText01(uri, {
            text1: "Sample toast from sample insert"
        }, {
            success: function (pushResponse) {
                console.log("Sent push:", pushResponse);
            }
        });


    }

}

but, clearly, unless you want to actually send a push notification every time a device registers its URI for push notifications then you’ll want to change this script to something more like;

// See documentation at http://go.microsoft.com/fwlink/?LinkId=296704&clcid=0x409
function insert(item, user, request) {
 
    // The following code manages channels and should be retained in this script
    var ct = tables.getTable("channels");
    ct.where({ installationId: item.installationId }).read({
        success: function (results) {
            if (results.length > 0) {
                // we already have a record for this user/installation id - if the 
                // channel is different, update it otherwise just respond
                var existingItem = results[0];
                if (existingItem.channelUri !== item.channelUri) {
                    existingItem.channelUri = item.channelUri;
                    ct.update(existingItem, {
                        success: function () {
                            request.respond(200, existingItem);
                        }
                    });
                }
                else {
                    // no change necessary, just respond
                    request.respond(200, existingItem);
                }
            }
            else {
                // no matching installation, insert the record
                request.execute();
            }
        }
    })
}

if you have a look at the code then you’ll notice that it’s expecting to receive an object containing properties (at least) installationId, channelUri and you’ll notice that it tries to make sure that we don’t end up with multiple entries in the table for the same installation id. What it doesn’t do is make any association with the user (beyond the installation id which might not really be the association you want) so you’d need to do something there to expand on that.

As an aside, the wizard secures the table in the following way;

image

image

which you’d possibly want to change as having the application key isn’t really much of a guarantee around who the client actually is.

Modified my App Class

The wizard also made modifications to my App class in my project. It added this particular line of code;

    sealed partial class App : Application
    {
      // http://go.microsoft.com/fwlink/?LinkId=290986&clcid=0x409
      public static Microsoft.WindowsAzure.MobileServices.MobileServiceClient mtBlogPostClient = new Microsoft.WindowsAzure.MobileServices.MobileServiceClient(
      "https://mtblogpost.azure-mobile.net/",
      "FnKfVVbpmFxOsCkCmLvWJBqEGgULyY49");

and so it added a static field called mtBlogPostClient of type MobileServiceClient and it instantiated it in a static constructor and it passed it the URI of my service (over HTTPS) and it also sniffed out the application key for my service and embedded that too.

Now, I’m not a huge fan of adding this public static property to my App class so I’d probably re-factor this in some way but this is what the wizard did here.

As an aside, this is the same thing that the “Add Connected Service…” dialog does in Visual Studio (it also adds .NET references as before);

image

As a second aside, if you’re not careful and you use both the “Add Connected Service…” dialog and the “Add Push Notification…” dialog you’ll end up with 2 of these global static properties which can be mightily confusing so watch out for that Smile

Added Code to Get a Channel Uri and Register it in the Channels Table

One more thing, that wizard added a class with some code in it which knows how to make the right WinRT API call to get a URI for push notifications, a unique installed id for the app for the user on the device and save it into the channels table in the cloud.

The wizard puts that code a file called push.register.cs as below;

image

and that code looks like this;

internal class mtBlogPostPush
    {
        public async static void UploadChannel()
        {
            var channel = await Windows.Networking.PushNotifications.PushNotificationChannelManager.CreatePushNotificationChannelForApplicationAsync();

            var token = Windows.System.Profile.HardwareIdentification.GetPackageSpecificToken(null);
            string installationId = Windows.Security.Cryptography.CryptographicBuffer.EncodeToBase64String(token.Id);

            var ch = new JObject();
            ch.Add("channelUri", channel.Uri);
            ch.Add("installationId", installationId);

            try
            {
                await App.mtBlogPostClient.GetTable("channels").InsertAsync(ch);
            }
            catch (Exception exception)
            {
                HandleInsertChannelException(exception);
            }
        }

        private static void HandleInsertChannelException(Exception exception)
        {

        }
    }

with the idea being that somewhere in my app I need to call UploadChannel().

As another aside, I’d really like that UploadChannel method to return TASK rather than VOID – that’s something that for me needs to change in that wizard.

Enables Toast Notifications

One last thing that I think that wizard does is that it enables toast notifications within my application’s manifest. I think it does anyway – I certainly didn’t do it Smile

image

Step 5 – Putting the Pieces Together

With all that done, to get the very basics of push notifications working I need to add something to my app that calls UploadChannel. The simplest thing to do is have that code run when the app runs up. So, writing something like;

    void InitialiseApp()
    {
      var profile = NetworkInformation.GetInternetConnectionProfile();
      if (profile != null)
      {
        var level = profile.GetNetworkConnectivityLevel();

        if ((level != NetworkConnectivityLevel.None) && 
          (level != NetworkConnectivityLevel.LocalAccess))
        {
          // See, this really needed to return task, no?
          mtBlogPostPush.UploadChannel();
        }
      }
    }

this method is on my App class and I call it from the OnLaunched override in the case where that override is invoked and the app isn’t already running.

If I run that code and relax the permissions on my channels table so that I can do an HTTP get without the application key then I can use something like Powershell to do a quick HTTP GET and see the channel data in there;

image

and there’s the data sitting in the cloud.

Step 6 – Doing Something to Get a Push Notification to the Device

With all that done, how can something in my mobile service send a quick push notification down to this device? Let’s say I add a custom API to the service. I can’t do this via Visual Studio so I’ll resort to the portal where I might add something like;

image

and then create an API;

image

and for simplicity I’m making this work off of a GET verb when it shouldn’t but then I can write a script to be invoked from that API (I didn’t spend very long on this script I’d point out);

exports.get = function(request, response) {

    var message = request.query["message"];
    var error = false;        
    
    if (message)
    {
        try
        {
             var table = request.service.tables.getTable('channels');
             
             table.read(
                 {
                   success : function(results)
                   {
                       results.forEach(function(result)
                       {
                            request.service.push.wns.sendToastText01(
                                result.channelUri,
                                {
                                    text1: message
                                }                       
                            ); 
                       });
                       response.send(statusCodes.OK);
                   }
                }               
             );        
        }
        catch (ex)
        {
            error = true;
        }
    }    
    if (error)
    {
        response.send(statusCodes.BAD_REQUEST);           
    }
};

Step 7 – Trying it Out

Finally, I made things simple so that I can easily invoke that custom API from a command prompt and so I can just drop to Powershell and send all users of my amazing app (i.e. me) a simple message;

image

and that’s all I need to get the very basics of push notifications into a project with Visual Studio and Azure Mobile Services.


Posted Wed, Mar 26 2014 11:08 AM by mtaulty

Comments

site.cascadelaser.com wrote site.cascadelaser.com
on Thu, Oct 30 2014 6:31 AM

“Point and Click” Cloud Push Notifications with Azure Mobile Services - Mike Taulty's Blog - Mike Taulty's Blog