When I started digging into the WP7 push notification system I thought it would be pretty quick to learn. On the surface it’s not terribly complicated. My phone app tells a service that it wants to receive notifications and the service sends them. In the end, it pretty much is that simple. Getting there, however, was more difficult then it needed to be. This is mainly due to the beta nature of WP7. Working on beta platforms shows how poorly the web handles finding the most relevant information. There are lots of articles out there that explain the messaging system. I’m sure I could look harder, but the few samples and demos that I reviewed didn’t work out of the gate. I had to review several and piece together information from different sources to get a working demo. The good news in doing this, is I now have a pretty good understanding of how the notification system works. This article is my attempt at explaining what I’ve learned. When reading this, keep in mind that this is based on the first beta drop of the WP7 dev tools. I will try hard to keep the post updated as the API’s become more final.
What Is The WP7 Push Notification System And Why Do I Need It?
Push notifications aren’t new to the smart phone industry. The concept is simple. Here’s a scenario:
I installed an application on my phone. I am interested in the content of this application even when the application is not running. If something worthy of getting my attention happens I would like the application to alert me of it. Again, even when it’s not running.
How about a more concrete scenario:
I am signed up for Twitter. I want to be alerted whenever a new tweet arrives that references my name. I have a twitter app on my phone that I can manually check at any time. That’s great, but I want my phone to notify me when I’m not running my app. Push notifications can do that.
Kindly MS realized that this is an important scenario to solve and have included a solution with the WP7 API’s.
Push Notification Architecture
There are three important players in the WP7 notification scenario; the phone, the application service (owned by the app developer), and the Microsoft push notification service. In order to get up and running with sending notifications the following must occur:
- Run the application for the first time and subscribe to notifications from the the applications notification service.
- On each run of the application register to receive the notifications.
- Send notifications from the application service to the Microsoft push notification service.
- Receive notifications (when the application is running) from the Microsoft push notification service.
That’s not to complicated and makes sense. Let’s break down each step and look at the code necessary to make it happen.
Subscribing with The Notification Service
In this part of the flow there are two components involved. The phone application and the application notification service. Let’s start by looking at the phone.
In order to subscribe to the notifications you need two pieces of information; a unique identifier for the phone and a URI. The unique identifier is easy enough. When your app loads, generate a GUID and store it in isolated storage. If you’ve already stored it (running the app for the second time) then just load it from isolated storage.
if (IsolatedStorageSettings.ApplicationSettings.Contains("DeviceId"))
{
_deviceId = (Guid)IsolatedStorageSettings.ApplicationSettings["DeviceId"];
}
else
{
_deviceId = Guid.NewGuid();
IsolatedStorageSettings.ApplicationSettings["DeviceId"] = _deviceId;
}
Ok, so we have our unique identifier, what about the URI? This URI is what the WP7 push notification service will use to send a notification to the device. To generate that we need to look at the notification API and specifically at Channels. Channels are objects that allow you to possibly subscribe to multiple notification content. For example, if I wanted to subscribe to scores for all NHL games, and also to all MLS games. I could use two separate channels to cover that content and the device could decide which to subscribe to. To keep this example simple, we are only going to use one channel. Each channel you create needs a unique name. Once a channel is created, you can retrieve it instead of creating it again. Here is the code that does this:
private void SetupNotificationChannel()
{
_channel = HttpNotificationChannel.Find(ChannelName);
if (_channel == null)
{
_channel = new HttpNotificationChannel(ChannelName);
_channel.ChannelUriUpdated += ChannelUriUpdated;
_channel.ErrorOccurred += (s, e) => Deployment.Current.Dispatcher.BeginInvoke(() => ErrorOccurred(e));
_channel.Open();
}
else
{
RegisterForNotifications();
}
}
Now that we have a channel setup we can get our URI and subscribe with our application service. The Channel class has an attribute called ChannelUri. In order to subscribe we can call into our application service like this:
private void RegisterWithNotificationService()
{
var svc = new NotificationService.NotificationServiceClient();
svc.SubscribeCompleted += (s, e) =>
{
if (e.Error != null)
{
Debug.WriteLine("Error registering with notification service");
}
};
svc.SubscribeAsync(_deviceId, _channel.ChannelUri.ToString());
}
Now is a good time to talk about the second component involved; the application notification service. In my sample, my service exposes a method called Subscribe. The interface looks like this:
[OperationContract]
void Subscribe(Guid clientID, string uri);
In order to get a simple notification running, not much has to happen here. I will mention though that depending on your scenario you could need to do some more complex work here. This is all up to you as the application developer. Mainly I’m talking about receiving preferences from the application user. Again, we will keep this simple since we are just trying to learn the notification system. All I’m going to do in the Subscribe method is store the clientID and URI. For simplicity I’m going to store this in a static dictionary. This would be a horrible idea for a real system since you would loose all your subscribers as soon as the application restarts, but it will work for our example. Here’s the implementation of our interface I showed above:
private static Dictionary<Guid, Uri> _clientUris = new Dictionary<Guid, Uri>();
public void Subscribe(Guid clientID, string uri)
{
if (_clientUris.ContainsKey(clientID))
{
_clientUris[clientID] = new Uri(uri);
}
else
{
_clientUris.Add(clientID, new Uri(uri));
}
}
That’s it. We are now registered. To summarize, the phone created a unique identifier and a channel. It then sent that unique identifier and the channel URI to the application service in order to subscribe to notifications. The application service stored the unique ID and the URI for future use. Once we are subscribed, we need to manage what happens when a notification is sent.
Registering to Handle Notification Events
Registering is simply the act of wiring up to some events off your channel in order to handle notifications that come in when your application is running and also telling the channel how to handle notifications when your application is not running.This is a good time to talk about the different types of notifications that the WP7 notification system supports. There are three notification types:
- Toast Notifications – When the application is not running, these will slide in on the screen for a few seconds and then slide out. If the user taps the notification your application will launch. When a toast notification comes in and the application is running, the application decides what to do with it.
- Raw Notifications – When the application is not running, these notifications aren’t used. Only when the application is running can you receive these notifications and do something with the data. As the name suggests, the data here is raw. It can be whatever you want it to be as long as it is under the size limit.
- Tile Notifications – When the application is not running and if the application is pinned to the start page, these notifications will be handled by the phone. There are three items you can alter with a tile notification; the tile image, the tile number (think un-read email count), and the title. There is no way to receive tile notifications when you application is running.
Based on the three notification types, let’s look at how you register. You would want to take these actions every time your application is executed. If you were paying attention earlier you noticed that in the SetupNotificationChannel method I wired up to an event off the Channel class called ChannelUriUpdated. This event fires any time the Uri is updated. I haven’t found much more information then that, but based on experience, it is fired when the channel is first created. In the event handler I need to setup a few things on the channel. There are two key tasks the sample code is doing. First it tells the channel to “bind” to tile notifications and also tells it which URI’s are valid for loading external tile images. It then tells the channel that it would like to bind to toast notification. Binding basically tells WP7 device to handle these notifications when they are sent and the application is not running. Here’s the code:
private void ChannelUriUpdated(object sender, NotificationChannelUriEventArgs e)
{
_channel = HttpNotificationChannel.Find(ChannelName);
if (!_channel.IsShellTileBound)
{
var uris = new Collection<Uri> { new Uri("http://www.thisisfanzoo.com") };
_channel.BindToShellTile(uris);
}
if (!_channel.IsShellToastBound)
{
_channel.BindToShellToast();
}
RegisterForNotifications();
}
Now that we’ve told the phone what to do when we aren’t running, we now need to wire up to the appropriate events when we are running. One item to note here, is that none of these events come in on the UI thread. That means you need to use BeginInvoke magic to get back to the UI thread (assuming you want to update your UI). There are three events that are important here; HttpNotificationReceived, ShellToastNotificationReceived, and ErrorOccurred. Here’s the code to wire up:
private void RegisterForNotifications()
{
RegisterWithNotificationService();
_channel.ShellToastNotificationReceived += (s, e) => Deployment.Current.Dispatcher.BeginInvoke(() => ToastReceived(e));
_channel.HttpNotificationReceived += (s, e) => Deployment.Current.Dispatcher.BeginInvoke(() => HttpNotificationReceived(e));
_channel.ErrorOccurred += (s, e) => Deployment.Current.Dispatcher.BeginInvoke(() => ErrorOccurred(e));
}
Ok, let’s review. So far we have told our application notification service that we exist, we have told the phone to handle notifications when the application isn’t running, and we have event handlers to handle notifications when the application is running. Now we need to send some notifications and see how to handle them. We will start with sending notifications.
Sending Push Notifications from the Application Notification Service.
For learning and testing purposes I created a super simple WPF app to trigger each of the three types of notifications. This application calls into the application notification service to do the actual delivery of the notifications. Let’s take a quick look at what the WPF application is doing. First, here’s the code to send Toast:
private void BtnSendToastClick(object sender, RoutedEventArgs e)
{
var svc = new NotificationService.NotificationServiceClient();
svc.SendToast(txtToastTitle.Text, txtToastMessage.Text);
}
Here’s the code to send a Raw message:
private void BtnSendMessageClick(object sender, RoutedEventArgs e)
{
var svc = new NotificationService.NotificationServiceClient();
svc.SendRawNotification(txtMessage.Text);
}
Here’s the code to send a Tile message:
private void BtnSendTileUpdateClick(object sender, RoutedEventArgs e)
{
var svc = new NotificationService.NotificationServiceClient();
var logoPath = string.Empty;
switch (cmbImage.SelectedIndex)
{
case 0:
logoPath = "/Images/logo2.png";
break;
case 1:
logoPath = "/Images/logo3.png";
break;
case 2:
logoPath = "http://www.thisisfanzoo.com/blog/samples/logo2.png";
break;
}
int count;
if(Int32.TryParse(txtTileCount.Text, out count))
{
svc.SendTileUpdate(txtTileTitle.Text, count, logoPath);
}
}
As you can see, this code is simple. There’s not much worth explaining here so let’s move on and look at the service. The service has three methods, one for each notification type. Here is the interface:
[OperationContract]
void SendToast(string title, string message);
[OperationContract]
void SendTileUpdate(string title, int count, string imageUrl);
[OperationContract]
void SendRawNotification(string message);
Well, that’s pretty straight forward. Let’s jump right into the implementations. We’ll start with toast:
public void SendToast(string title, string message)
{
var toastMessage = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
"<wp:Notification xmlns:wp=\"WPNotification\">" +
"<wp:Toast>" +
"<wp:Text1>{0}</wp:Text1>" +
"<wp:Text2>{1}</wp:Text2>" +
"</wp:Toast>" +
"</wp:Notification>";
toastMessage = string.Format(toastMessage, title, message);
var messageBytes = System.Text.Encoding.UTF8.GetBytes(toastMessage);
foreach (var uri in _clientUris.Values)
{
SendMessage(uri, messageBytes, NotificationType.Toast);
}
}
There’s a little more meat there so let’s talk about it. First notice the toast XML. This was a source of some pain when learning this because the XML has changed a bit. Most examples I found on the intraweb are wrong. Fortunately MSDN is correct and so is this code (for now). So we generate some XML with our toast data, encode it into a byte array and then send it to each URI that has been subscribed. I knew that URI would come in handy. Sending the actual message is the same for all three notification types so I’ll talk about that last. The other two notifications types are very similar to this but let’s look at the code anyway. Here’s Tile notifications:
public void SendTileUpdate(string title, int count, string imageUrl)
{
var tileMessage = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
"<wp:Notification xmlns:wp=\"WPNotification\">" +
"<wp:Tile>" +
"<wp:BackgroundImage>{0}</wp:BackgroundImage>" +
"<wp:Count>{1}</wp:Count>" +
"<wp:Title>{2}</wp:Title>" +
"</wp:Tile> " +
"</wp:Notification>";
tileMessage = string.Format(tileMessage, imageUrl, count, title);
var messageBytes = System.Text.Encoding.UTF8.GetBytes(tileMessage);
foreach (var uri in _clientUris.Values)
{
SendMessage(uri, messageBytes, NotificationType.Tile);
}
}
There are three pieces of data that are sent in a Tile message. The count will be displayed over your tile and can be used to show the number of un-read messages that have been received. The title will be used as the tiles title on the start screen. The background image is the path to the image you want to use for your tile. This image can be a path to a local resource or to a remote resource. If you are using a remote resource there are some restrictions on how fast the image needs to load, and you also need to specify which URI’s will be valid to load images from. If you remember we did this earlier in the ChannelUriUpdated method.
Here are Raw notifications:
public void SendRawNotification(string message)
{
var messageBytes = Encoding.UTF8.GetBytes(message);
foreach (var uri in _clientUris.Values)
{
SendMessage(uri, messageBytes, NotificationType.Raw);
}
}
Now that that makes sense, let’s look at how we actually send the notification to the notification service. To do this we are simply going to make an HTTP POST to the subscribers URI. We can also look at the response to that POST in order to get some useful information. Here’s the code:
private static void SendMessage(Uri uri, byte[] message, NotificationType notificationType)
{
var request = (HttpWebRequest)WebRequest.Create(uri);
request.Method = WebRequestMethods.Http.Post;
request.ContentType = "text/xml";
request.ContentLength = message.Length;
request.Headers.Add("X-MessageID", Guid.NewGuid().ToString());
switch (notificationType)
{
case NotificationType.Toast:
request.Headers["X-WindowsPhone-Target"] = "toast";
request.Headers.Add("X-NotificationClass", ((int)BatchingInterval.ToastImmediately).ToString());
break;
case NotificationType.Tile:
request.Headers["X-WindowsPhone-Target"] = "token";
request.Headers.Add("X-NotificationClass", ((int)BatchingInterval.TileImmediately).ToString());
break;
default:
request.Headers.Add("X-NotificationClass", ((int)BatchingInterval.RawImmediately).ToString());
break;
}
using (var requestStream = request.GetRequestStream())
{
requestStream.Write(message, 0, message.Length);
}
try
{
var response = (HttpWebResponse)request.GetResponse();
var notificationStatus = response.Headers["X-NotificationStatus"];
var subscriptionStatus = response.Headers["X-SubscriptionStatus"];
var deviceConnectionStatus = response.Headers["X-DeviceConnectionStatus"];
Debug.WriteLine(string.Format("Device Connection Status: {0}", deviceConnectionStatus));
Debug.WriteLine(string.Format("Notification Status: {0}", notificationStatus));
Debug.WriteLine(string.Format("Subscription Status: {0}", subscriptionStatus));
}
catch (WebException ex)
{
Debug.WriteLine(string.Format("ERROR: {0}", ex.Message));
}
}
In the real world you’ll want to do something more significant with the response information then write it out to the debug console, but this is a sample. I’m pretty comfortable with web development so making a POST like this makes sense to me and I think the code is fairly self describing. There are two headers that we are adding to our POST. X-WindowsPhone-Target defines the notification type. If this header is not defined then it is a Raw notification. The X-NotificationClass defines when the service should send the notification. I’ve used an enum here to simplify, here it is:
public enum BatchingInterval
{
TileImmediately = 1,
ToastImmediately = 2,
RawImmediately = 3,
TileWait450 = 11,
ToastWait450 = 12,
RawWait450 = 13,
TileWait900 = 21,
ToastWait900 = 22,
RawWait900 = 23
}
Those wait values are in seconds (i.e.: ToastWait450 will send toast with a 450 second delay). I’m not quite sure why there is a separate value for each notification type, but there is.
There are a couple significant headers in the response as well. X-NotificationStatus will tell you the status of the notification that was received by the MS Push Notification system. Normally we are looking for a value of “Received". The X-SubscriptionStatus header will tell you what the status is of the subscription (sorry for the lame explanation). The expected value is “Active”. The X-DeviceConnectionStatus will tell you the connection status of the device (again, sorry for the lame explanation). We are looking for a value of “Connected”. You can find a good explanation of the possible values at the bottom of this page.
Ok, our notification is sent. The only part left is to look at how to handle the channel events on the phone if the application is running.
Handling Notifications When The Application Is Running
There are several scenarios on why you may want to handle notifications when the application is running. I’ll leave specific ideas to your imagination. There are two events that will fire from the channel class when a notification arrive. They are the HttpNotificationReceived event and the ShellToastNotificationReceived event. As you can probably guess ShellToastNotificationRecevied is fired when a toast notification is sent to the device. The HttpNotificationReceived is fired when a raw notification is sent. Let’s start by looking at the ShellToastNotificationReceived event:
private void ToastReceived(NotificationEventArgs e)
{
lblMessage.Text = "Toast Message Received";
txtMessage.Text = string.Format("Title:{0}\nMessage:{1}", e.Collection["wp:Text1"], e.Collection["wp:Text2"]);
Debug.WriteLine("Toast Recevied:" + e.Collection["wp:Text1"] + ", " + e.Collection["wp:Text2"]);
}
I’m not doing anything fancy with the results here. I’m just displaying the the toast data in a textbox and updating a label to indicate that a toast message was received. Notice that both text values (text1 and text2) are in the NotificationEventArgs.Collection collection (that’s awkward naming).
Now let’s look at the HttpNotificationReceived event:
private void HttpNotificationReceived(HttpNotificationEventArgs e)
{
var reader = new StreamReader(e.Notification.Body);
var message = reader.ReadToEnd();
Debug.WriteLine(string.Format("Raw notification received: {0}", message));
lblMessage.Text = "Raw Message Received";
txtMessage.Text = message;
reader.Close();
}
In this case I am using a StreamReader to get the data out. This is a raw message. The Body property could be anything (i.e.: Binary, xml, string, etc). In this case I’m simply sending over a string and so we can use a StreamReader to get the string out.
Phew… this is a long post, but I think that covers most of what you will need to know when getting started with the WP7 Push notification system. The sample application that I wrote is available here. If you want to jump right in and try it out, load the solution, and run the applications in the following order:
- Start Notifications.Service
- Start Notifications.Phone
- Start Notifications.Sender
Reminder, this is beta software. I’ve read about a bug where the phone emulator will have an issue getting notifications setup properly when it first loads. I haven’t experienced this so maybe this has been fixed. If you have trouble at first, let the emulator run for a few minutes and then try again.
I read a lot of articles in order to come up with this sample application and post so here are some links I used as reference: