Windows 8.1 Store, Azure Mobile Services & Offline Data

Following up on that post from yesterday, the other thing that I noticed with Mobile Services yesterday was the new support for working online/offline (via a local SQLite store).

There’s a “getting started” post on the website about this and I don’t think the client-side support stretches beyond .NET clients just yet.

There’s also discussion of this in some of the //Build sessions, specifically;

although that wanders off a little into a Xamarin discussion in the latter half of the session so you may want to watch/skip that depending on what you already know about Xamarin.

In terms of my own “getting” started, I can make myself a blank Windows 8.1 Store project inside of Visual Studio and then quickly add a mobile service;

image

leading to;

image

and then I can create a table inside of that service, let’s call it customers for example;

image

leading to;

image

and then with that in place I could write a little class which represents what data I want within my customers table;

 [DataContract(Name="customers")]
  class Customer
  {
    [DataMember(Name="id")]
    public string Id { get; set; }

    [DataMember(Name="firstname")]
    public string FirstName { get; set; }
    
    [DataMember(Name="lastname")]
    public string LastName { get; set; }
    
    [DataMember(Name="spam")]
    public bool SpamWithEmailInvitations { get; set; }
    
    [DataMember(Name="version")]
    [Version]
    public string Version { get; set; }
  }

Note the inclusion of the member called Version and that it has a Microsoft.WindowsAzure.Version attribute stamped on it. For quite a while now Mobile Services has created 3 additional columns on a table called _version, _createdAt, _updatedAt for the purposes of being able to detect conflicts on the server-side. In order to do this in combination with offline working, that version column needs to show up and be understood on the client side.

I can then add a reference to my project for Azure Mobile Services from NuGet;

image

and I can write a little class to wrap up my service access;

  class DataManager
  {
    void Initialise()
    {
      if (this.proxy == null)
      {
        this.proxy = new MobileServiceClient(
          "https://mttestservice.azure-mobile.net/");
      }
    }
    IMobileServiceTable<T> GetTable<T>()
    {
      this.Initialise();
      return (proxy.GetTable<T>());
    }
    public async Task<IEnumerable<Customer>> GetCustomersAsync()
    {
      var customers = await this.GetTable<Customer>().ReadAsync();
      return (customers);
    }
    public async Task InsertCustomerAsync(Customer customer)
    {
      await this.GetTable<Customer>().InsertAsync(customer);
    }
    MobileServiceClient proxy;
  }

and then use it to insert a record;

      DataManager manager = new DataManager();
      
      Customer c = new Customer()
        {
          FirstName = "Fred",
          LastName = "Astair",
          SpamWithEmailInvitations = false
        };

      await manager.InsertCustomerAsync(c);

and to query existing records;

      DataManager manager = new DataManager();
      
      var customers = await manager.GetCustomersAsync();

Now if I want to be able to do offline working on this data then I need to take a few steps and it’s important to realise that this is not an automatic “SQL Server replication” style feature but, instead, a way in which the application can ask for certain data to be available in a local offline store ( SQLite for Windows 8.1 here ) and then take explicit steps to push data to/from that local store back to the cloud ( with conflict resolution ) when the app determines that the user is online/offline ( probably based on the NetworkInformation class ).

The first step is to add a reference to SQLite. I already have the installed as an extension to Visual Studio and I got it from here: http://sqlite.org/download.html looking for the “Precompiled Binaries for Windows Runtime” side of things. That means that I can add a reference to my project;

image

Adding that reference means that we just added a reference to a native component (i.e. not .NET code) which means that we need to explicitly build packages for different processor architectures rather than just building MSIL code. So, Visual Studio warns;

image

until I say “ok, I’ll just build for x86 for now then”;

image

With SQLite available, the Mobile Services folks have built a store on top of it which plugs into the MobileServiceClient and so I can add support for that from NuGet;

image

and now (by way of example) I can change my data manger class a little bit – adding some explicitly named methods here;

namespace App97
{
  using Microsoft.WindowsAzure.MobileServices;
  using Microsoft.WindowsAzure.MobileServices.SQLiteStore;
  using Microsoft.WindowsAzure.MobileServices.Sync;
  using System.Collections.Generic;
  using System.Threading.Tasks;

  class DataManager
  {
    static readonly string DATABASE_FILE = "local.db";

    async Task InitialiseAsync()
    {
      if (this.proxy == null)
      {
        this.proxy = new MobileServiceClient(
          "https://mttestservice.azure-mobile.net/");
      }
      await this.InitialiseDatabaseAsync();
    }
    async Task InitialiseDatabaseAsync()
    {
      if (!this.proxy.SyncContext.IsInitialized)
      {
        // Create/open DB.
        MobileServiceSQLiteStore store =
          new MobileServiceSQLiteStore("local.db");

        // Make sure that store defines our table (at least). NB: repeating
        // this seems to be "ok".
        store.DefineTable<Customer>();

        // Tell our proxy about our database and specify a default
        // IMobileServicesSyncHandler to do the work.
        await this.proxy.SyncContext.InitializeAsync(
          store, new MobileServiceSyncHandler());
      }
    }
    async Task<IMobileServiceSyncTable<T>> GetTable<T>()
    {
      await this.InitialiseAsync();
      return (proxy.GetSyncTable<T>());
    }
    public async Task<IEnumerable<Customer>> GetCustomersAsync()
    {
      var table = await this.GetTable<Customer>();
      var customers = await table.ReadAsync();

      return (customers);
    }
    public async Task InsertCustomerAsync(Customer customer)
    {
      var table = await this.GetTable<Customer>();

      await table.InsertAsync(customer);
    }
    public async Task SyncWithCloudAsync()
    {
      // NB: would be nice to deal with cancellation here at least.
      // NB: pulling this data from cloud->local database first
      // pushes data from the local store to the cloud first.
      // NB: calling when offline will cause errors that would
      // need to be handled.
      var table = await this.GetTable<Customer>();
      await table.PullAsync();
    }
    MobileServiceClient proxy;
  }
}

and the main change here is the initialisation of the SyncContext on the MobileServiceClient in the InitialiseDatabaseAsync method and also the fact that I am no longer making calls to MobileServiceClient.GetTable<T> but, instead, I’m making calls to MobileServiceClient.GetSyncTable<T> which means that my data access now is against the local SQLite store rather than the cloud store. I’ve then got the simplistic SyncWithCloudAsync method to attempt to rationalise my local table with the cloud table.

With 1 record in the cloud if I now execute this code;

      DataManager manager = new DataManager();
      
      var customers = await manager.GetCustomersAsync();

then I’m not going to see any data because the data is being pulled from the local SQLite database and there’s nothing in that database. If I change that code to include;

      DataManager manager = new DataManager();

      await manager.SyncWithCloudAsync();

      var customers = await manager.GetCustomersAsync();

then I’m going to get my 1 customer record back from the table because it’s been sync’d with the cloud first. Naturally, deciding when and where to call my SyncWithCloudAsync method within my app would be a crucial thing to get right.

Similarly, if I wrote code such as;

      DataManager manager = new DataManager();

      Customer c = new Customer()
      {
        FirstName = "Ginger",
        LastName = "Rogers",
        SpamWithEmailInvitations = true
      };
      await manager.InsertCustomerAsync(c);

then my local SQLite store is going to contain 2 records and my cloud is going to contain 1 record until I call my sync method again to update the cloud from the local table. It’s worth saying that because the id columns in Mobile Services are Guids rather than integers it’s possible to create records offline without worrying about creating collisions on the ids.

As an aside, the framework here is running through the table’s RESTful access methods on the service so if (e.g.) I have some custom script running on the service side to somehow filter/alter the data then the sync framework here is going to run through that code. It’s not using some “back door” type mechanism to get to the data.

Nonetheless, there may well be conflicts if I have 2 users working on the same data or if I have 1 user on multiple devices working on the same data so I guess conflicts will have to be handled.

In some frameworks you can define sets of columns to watch for the purposes of optimistic concurrency checking but, here, I think it’s just done at the level of a whole record and the version column is acting as the watchman on whether a record has got out of synchronisation or not.

I didn’t include an update mechanism on my DataManager class but I could relatively easily add one;

public async Task UpdateCustomerAsync(Customer customer)
    {
      var table = await this.GetTable<Customer>();
      await table.UpdateAsync(customer);
    }

and now I can cause trouble. The normal way to do this would be to run multiple instances of the app but let’s say that I write code such as this;

   DataManager manager = new DataManager();

      // we are in sync...
      await manager.SyncWithCloudAsync();

      // we get the customers from the local table
      var customers = await manager.GetCustomersAsync();
    
      // we update one of them
      var first = customers.First();

      first.LastName = "Smith";

      // we update the local table.
      await manager.UpdateCustomerAsync(first);

      // we update the cloud.
      await manager.SyncWithCloudAsync();

and everything’s fine and our changes make it to the cloud. What if I opened up a direct route to the cloud though? That is, what if I added this method to my data manager which went around the local database!

    public async Task UpdateCustomerDirectWithCloudAsync(Customer customer)
    {
      await this.InitialiseAsync();
      await this.proxy.GetTable<Customer>().UpdateAsync(customer);
    }

Now I can cause some trouble, code like this for instance;

      DataManager manager = new DataManager();

      // we are in sync...
      await manager.SyncWithCloudAsync();

      // we get the customers from the local table
      var customers = await manager.GetCustomersAsync();
    
      // we update one of them
      var first = customers.First();

      first.LastName = "Jones";

      // we update the local database - note that this will leave the VERSION
      // field in the record (object) alone.
      await manager.UpdateCustomerAsync(first);

      // we update the cloud DIRECTLY - note that this will change the VERSION
      // field in the record (object).
      await manager.UpdateCustomerDirectWithCloudAsync(first);

      // we try to sync...we're going to fail...
      try
      {
        await manager.SyncWithCloudAsync();
      }
      catch (MobileServicePushFailedException ex)
      {
        foreach (var error in ex.PushResult.Errors)
        {
          Debug.WriteLine(
            string.Format("Hit an error with table {0}, status is {1}, item was {2}",
            error.TableName,
            error.Status,
            error.Item));
        }
      }

and this dumps out output to the debug monitor;

Hit an error with table customers, status is PreconditionFailed, item was {

  "id": "7D398BA8-1480-4499-A12D-BA9C0DA8BFC7",

  "firstname": "Fred",

  "lastname": "Jones",

  "spam": false,

  "__version": "AAAAAAAAB94="

}

Naturally, I might want to not expose these kinds of exception directly to the caller of my DataManager class so I could take steps there to wrap things up nicely but I won’t do that here.

Instead, I can make a decision about what to do about a conflict like this. There’s generally three things I can do;

  1. Decide that the client database wins.
  2. Decide that the server database wins.
  3. Ask the user whether the client database or the server database wins.

This is handled by the implementation of IMobileServicesSyncHandler which I pass to the call to IMobileServicesSyncContext.Initialize. I can change my DataManager class to accept a handler – changes below are to the constructor and the InitialiseDatabaseAsync methods (not listed whole class again);

  class DataManager
  {
    static readonly string DATABASE_FILE = "local.db";

    public DataManager(IMobileServiceSyncHandler syncHandler = null)
    {
      this.syncHandler = syncHandler != null ?
        syncHandler : new MobileServiceSyncHandler();
    }
    async Task InitialiseAsync()
    {
      if (this.proxy == null)
      {
        this.proxy = new MobileServiceClient(
          "https://mttestservice.azure-mobile.net/");
      }
      await this.InitialiseDatabaseAsync();
    }
    async Task InitialiseDatabaseAsync()
    {
      if (!this.proxy.SyncContext.IsInitialized)
      {
        // Create/open DB.
        MobileServiceSQLiteStore store =
          new MobileServiceSQLiteStore("local.db");

        // Make sure that store defines our table (at least). NB: repeating
        // this seems to be "ok".
        store.DefineTable<Customer>();

        // Tell our proxy about our database and specify a default
        // IMobileServicesSyncHandler to do the work.
        await this.proxy.SyncContext.InitializeAsync(
          store, this.syncHandler);
      }
    }

With reference to the sample here I could then try and make a “server side wins” sync handler;

  class ServerWinsSyncHandler : IMobileServiceSyncHandler
  {
    public async Task<JObject> ExecuteTableOperationAsync(
      IMobileServiceTableOperation operation)
    {
      // NB: assuming 2nd retry will work (might not be a good thing)
      JObject jObject = null;

      try
      {
        jObject = await operation.ExecuteAsync();
      }
      catch (MobileServicePreconditionFailedException ex)
      {
        // pick up the server-side value from the exception.
        jObject = ex.Value;
      }
      return (jObject);
    }

    public Task OnPushCompleteAsync(MobileServicePushCompletionResult result)
    {
      return (Task.FromResult(0));
    }
  }

and try it out;

     DataManager manager = new DataManager(new ServerWinsSyncHandler());

      // we are in sync...
      await manager.SyncWithCloudAsync();

      // we get the customers from the local table
      var customers = await manager.GetCustomersAsync();
    
      // we update one of them
      var first = customers.First();

      first.LastName = "Local";

      // we update the local database - note that this will leave the VERSION
      // field in the record (object) alone.
      await manager.UpdateCustomerAsync(first);

      first.LastName = "Cloud";

      // we update the cloud DIRECTLY - note that this will change the VERSION
      // field in the record (object).
      await manager.UpdateCustomerDirectWithCloudAsync(first);

      // we try to sync - the handler ensures that the conflict leaves
      // the cloud winning
      await manager.SyncWithCloudAsync();

      // This goes back to our local DB...
      customers = await manager.GetCustomersAsync();
      first = customers.First();
      Debug.Assert(first.LastName == "Cloud");

and that final assertion holds true – i.e. the conflict was detected and the cloud version of the record was left in the local database.

If I want to ensure that the client side of things wins then I can write a different sync handler but I haven’t quite managed to get the implementation of that to work for me just yet so I’ll update the post to add that last piece as/when I’ve got it working a little better than I have right now Smile

Update – trying to get a “client” wins handler working.

I talked to the Mobile Services team around why I couldn’t get a handler working that adopted a strategy of letting the client-side “win” when it came to a conflict between the client-side and the cloud-side.

In doing that, I think I hit a bit of a glitch that is resolved in an updated pre-release version of the mobile services SDK and so I installed an updated version of the SDK which was published yesterday;

image

and then wrote a ClientWinsSyncHandler which executes the request, checks for an exception back from the server and in the presence of that exception it attempts to replace the version number that the client-side record has with the version number that the cloud-side record has and then attempts to do the update again. That looks like;

  class ClientWinsSyncHandler : IMobileServiceSyncHandler
  {
    public async Task<JObject> ExecuteTableOperationAsync(
      IMobileServiceTableOperation operation)
    {
      JObject jObject = null;
      bool retry = true;

      try
      {
        // try for the first time.
        jObject = await operation.ExecuteAsync();
        retry = false;
      }
      catch (MobileServicePreconditionFailedException ex)
      {
        // take the version number that the server says it has and
        // stamp it into the client record so that on the next 
        // try there "should" be no conflict.
        operation.Item[MobileServiceSystemColumns.Version] =
          ex.Value[MobileServiceSystemColumns.Version];
      }
      if (retry)
      {
        // try again. this "should" work this time around although,
        // clearly, based on timing in the real world there could
        // be more conflicts so we might really want to loop.
        jObject = await operation.ExecuteAsync();
      }
      return (jObject);
    }
    public Task OnPushCompleteAsync(MobileServicePushCompletionResult result)
    {
      return (Task.FromResult(0));
    }
  }

and then keeping that previous DataManager implementation I can test this out with something like;

      DataManager manager = new DataManager(new ClientWinsSyncHandler());

      // we are in sync...
      await manager.SyncWithCloudAsync();

      // we get the customers from the local table
      var customers = await manager.GetCustomersAsync();
    
      // we update one of them
      var first = customers.First();

      first.LastName = "Local";

      // we update the local database - note that this will leave the VERSION
      // field in the record (object) alone.
      await manager.UpdateCustomerAsync(first);

      first.LastName = "Cloud";

      // we update the cloud DIRECTLY - note that this will change the VERSION
      // field in the record (object).
      await manager.UpdateCustomerDirectWithCloudAsync(first);

      // we try to sync - the handler ensures that the conflict leaves
      // the local version winning - i.e. it goes from our local store
      // to the cloud and then comes back again to the local store.
      await manager.SyncWithCloudAsync();

      // This goes back to our local DB...
      customers = await manager.GetCustomersAsync();
      first = customers.First();
      Debug.Assert(first.LastName == "Local");

and that final assertion now works out fine on that new version of the SDK – i.e. the local data “wins” and the cloud data is replaced when the 2 records are out of sync.

Windows 8.1 Store Apps, Azure Mobile Services & Notification Hubs

NB: Fairly rough notes of a couple of changes (particularly around Notification Hubs) I came across while working in Visual Studio 2013 Update 2 RC today. Take with a small pinch of salt and always consult http://www.windowsazure.com/mobile for definitive.

Ok, I’ll admit that from time to time I get a little behind the times but I noticed some changes today in the way things are working between Visual Studio (2013 Update 2 RC) and Azure Mobile Services so I thought I’d try and write them up as I demonstrated some of this functionality just last week and it looks like there have been updates.

Let’s say that I create a blank Windows Store 8.1 app;

image

and then imagine that I want to create an Azure Mobile Service for this app from the Server Explorer;

image

and I go ahead and fill in the various details;

image

and I create that service and then I create a table;

image

and I enter some details in there;

image

and get that created and then let’s say I want to write some code against that table so I use Visual Studio’s little “Add Connected Service…” dialog;

image

and that lets me pick the service in question;

image

and that does some work for me such as;

  1. Adding a reference to various libraries.
  2. Adding a definition of a proxy class (i.e. a MobileServiceClient) to my App.xaml.cs file.

with that code looking like;

image

and then I have a slight problem because I’ve configured my table to only allow for authenticated access so I could (e.g.) maybe add some UI to invoke a piece of code that causes a login;

image

and I could go and visit Google’s Developers Console (https://console.developers.google.com/project) and set up a new project like this one;

image

and then I can create a new client ID and secret for this project;

image

and copy them back into the Azure Mobile Services portal in the right section;

image

and exercise my code to login to the application via Google;

image

and that’s all good and I’m logged in and I could now write code to run and insert a record into my table such as;

image

and that all works quite nicely. Now, if I did (purely for an example) want to add some custom API that allowed a caller to get the data from this table then I can now do this via Visual Studio which is a new thing;

image

and that pops a dialog for me;

image

NB: this is just for an example and then I can edit the script in Visual Studio;

image

adding something like;

image

and then I can invoke that from (e.g.) PowerShell;

image

and that all works nicely and I’ve got a custom API created entirely in Visual Studio without visiting the Azure Mobile Services portal.

The next (big) change that I came across was the way in which notification hubs are brought into Azure Mobile Services for push notifications. I noticed when I created my service a new entry in the Server Explorer;

image

and I can see some properties on this in Visual Studio;

image

I’m not particularly well versed in Notification Hubs so I thought I’d engage in a bit of background reading;

Now, this isn’t new but it’s new to me as a Mobile Service user as I have tended to just manually do push myself previously even though hubs were available. What they seem to offer to me is;

  1. Scale – for broadcast/non-broadcast delivery.
  2. Abstraction of the differences between PNS systems.
  3. Management of PNS registration details.

I also wondered how this worked for free/paid Mobile Service usage and so had a look at the “Scale” option for my hub which was running in the “free” tier;

image

and that leads on to the page over here which lists the way that the pricing works.

Looking at the Azure Portal, I see a few things;

image

What I notice as “different” here is if I use the portal to create a new mobile service then this tab displays something different – by default creating a service this way doesn’t currently seem to switch on notification hubs,

image

and so there’s a different UI and that little button at the bottom, middle of the screen which presumably turns on hubs.

Where I noticed this first was in using the dialog that Visual Studio gives you for adding “push notifications” into a project. That dialog is still there;

image

and what it used to do for me was something like this;

  1. Add libraries into the project if they weren’t already there.
  2. Add a declaration of a proxy onto my App class (of type MobileServiceClient).
  3. Sets up the right bits at the Windows Store such that my app is able to use the WNS service.
  4. Add a new table called channels into my Mobile Service.
  5. Add a modified insert script to that table such that it did some work to attempt to de-duplicate any doubly registered channel URIs.
  6. Added code to my client side project to get hold of a unique installation ID and to post it to the channels table in the cloud along with the primary URI for notifications for my app for the user on this device.
  7. Sets my app manifest up to allow toast notifications.

With the integration of notification hubs, it doesn’t seem to do that any more by default for a new project like the one I’ve created in this post in Visual Studio 2013 Update 2 RC.

The dialogs involved are the same as before – i.e. you need to associate your app with a Store package if you haven’t already;

image

and then you select a mobile service;

image

What seems to happen now is something like;

  1. Add libraries into the project if they weren’t already there.
  2. Add a declaration of a proxy onto my App class (of type MobileServiceClient).
  3. Sets up the right bits at the Windows Store such that your app is able to use the WNS service.
  4. Configures the notification hub to be able to deliver via the WNS service.
  5. Writes some code in order to register my app’s channel with this hub for this user on this device.
  6. Writes some more code (in my App.OnLaunched override) to call the code that it wrote in Step (5) when the app starts up – I wasn’t too keen on this as I’d perhaps want this code elsewhere.
  7. Writes a new custom API called notifyallusers.js.

I can see (4) in both Visual Studio;

image

and in the portal display for my hub;

image

The code written in (5) looks like this and goes (as before) into a file called push.register.cs within a services sub-folder of the project;

  internal class mtaultyVampireHunterPush
  {
    public async static void UploadChannel()
    {
      var channel = await PushNotificationChannelManager.CreatePushNotificationChannelForApplicationAsync();

      try
      {
        await App.proxy.GetPush().RegisterNativeAsync(channel.Uri);

        // You would perhaps want to take this out 🙂
        await App.proxy.InvokeApiAsync("notifyAllUsers");
      }
      catch (Exception exception)
      {
        HandleRegisterException(exception);
      }
    }

    private static void HandleRegisterException(Exception exception)
    {

    }
  }

and it’s not entirely transparent what this code is doing but it’s clear that it’s getting (in this case) the application’s primary URI for notifications and is registering it with the cloud (the notification hub) and then the line of code making the call to InvokeApiAsync tries to immediately call the notifyAllUsers custom API that the dialog has just created so I removed that code pretty quickly.

That custom API script looks something like this;

// See documentation at http://go.microsoft.com/fwlink/?LinkId=296704&clcid=0x409
exports.post = function(request, response) {
    response.send(statusCodes.OK,{ message : 'Hello World!' })
    
    // 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(request);
};

// The following code should be moved to appropriate script in your app where notification is sent
function sendNotifications(request) {
    var payload = '<?xml version="1.0" encoding="utf-8"?><toast><visual><binding template="ToastText01">' +
        '<text id="1">Sample Toast</text></binding></visual></toast>';
    var push = request.service.push; 
    push.wns.send(null,
        payload,
        'wns/toast', {
            success: function (pushResponse) {
                console.log("Sent push:", pushResponse);
            }
        });
}

and what that’s doing is broadcasting a toast notification out to any channel that’s registered with the notification hub. What I found most interesting about this code is that it’s still using the push.wns.send method and that this seems to somehow have been rewired to use notification hubs rather than the previous mechanism which was (AFAIK) a direct communication with WNS (or MPNS etc.). Now, there is a bit in the docs which says;

“This operation resets your push credentials and changes the behavior of the push methods in your scripts”

which might be pointing to push.wns.send doing different things once a notification hub is in set up for the Mobile Service but I’ve not checked that out fully at the time of writing and that document that I pointed to doesn’t walk through doing this stuff via the dialog in Visual Studio, it seems to take a more manual route.

With all that in place though, I can put some UI (like a button) on the screen to invoke the code that’s been written for me (taking out the C# code above which tried to call NotifyAllUsers once the channel URI had been registered);

image

The first thing I noticed when I ran this code (without logging in) was that I got back an “unauthorized” exception back from the call to RegisterNativeAsync() listed previously.

I spent a bit of time on this, performing one of those “binary chop” style exercises on my own code versus a blank project until I realised that my code was constructing the MobileServiceClient proxy without an application key whereas the blank project was using the application key.

Based on that, I guess that access to the notification hub (i.e. the ability to register with the notification hub) is being controlled by passing the application key to the service. I’m not sure if that’s the only option (unlike a table or a custom API which allows other options) but that’s the only option that I’ve tried to make use of for the moment.

So, I changed my code to pass the application key when it constructed the MobileServiceClient instance and I seemed to be able to register with the hub. The portal showed me;

image

I’d actually expected this to show up in a different area but it was definitely a sign of life Smile

With that registration in place, I could debug my push notifications from the hub either from Visual Studio or from the portal which I thought was pretty cool. That is;

image

pops up a Visual Studio page;

image

which I can use to send the app a toast;

image

which is quite cool Smile

Having learnt how to broadcast, I wondered how I would direct a notification to a particular user. I read a little more around tags and (in the first instance) played around with the registration code so add the current user id as a tag;

image

and then tested that out from the debugging tool in Visual Studio and the notifications seemed to reach that user just fine.

Conversely, changing the tag slightly caused notifications to not show up.

I’m not at all sure at the time of writing whether that’s a reasonable thing to do so I’ll update the post if I find out that it’s not.

That’s it for this post – as I said at the start, these are rough notes in coming to the changes here in Mobile Services for the first time so posted here purely as a way of trying to be helpful if you’re grappling with similar things. There are docs on the main site but I found that they didn’t quite walk through the same path that I was trying to follow here and hence the post.

“Point and Click” Cloud Push Notifications with Azure Mobile Services

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.