In this eighth part of my series on architecting Windows Phone 7 (WP7) applications, i’ll show you how to deal with Toast Push Notifications. If you’ve missed the past parts of the series, you you can visit them here:
When I say Toast notifications, I am specifically talking about being able to send the phone (e.g. the user) a push notfication that something is important and that the user might possibly want to launch the application. Its a near-time notification to the phone (if connectivity is there). This notification can either alert the application when its running or give the user a notification window at the top of the phone like so:
Toast push notifications are one type of notifications that Windows Phone 7 supports. The Push notification system for Windows Phone 7 is a service that Microsoft provides to allow you to communication with the phone without worrying about whether the phone is available or to queue up the notification for when the phone is available.
Typical this requires some sort of service to push the notifications to the “Microsoft Push Notification Service”. The “Microsoft Push Notification Service” then is responsible for alerting the phone when the phone is available.
To use Push Notifications, your application must warn your user that it uses Push Notifications and allow them to disable those notifications.
How It Works
The workflow for using notifications in general (and specifically for Toast notifications) is:
First you will need to register your channel during your application’s lifetime (I usually do this in an Options dialog when the user enables the feature):
const string CHANNELNAME = "FunWithToast";
private HttpNotificationChannel _myChannel;
public void CreateChannel()
{
try
{
if (_myChannel == null)
{
// Attempt to get the channel
_myChannel = HttpNotificationChannel.Find(CHANNELNAME);
// Can't Find the Channel, so create it
if (_myChannel == null)
{
_myChannel = new HttpNotificationChannel(CHANNELNAME);
WireChannel(_myChannel);
_myChannel.Open();
}
else
{
WireChannel(_myChannel);
}
// Make sure that you can accept toast notifications
if (!_myChannel.IsShellToastBound) _myChannel.BindToShellToast();
// If the channel uri is valid now, then send it to the server
// Otherwise it'll be registered when the URI is updated
if (_myChannel.ChannelUri != null)
{
SendChannelUriToService(_myChannel.ChannelUri.ToString());
}
}
return;
}
catch (Exception)
{
// NOOP
}
}
To walkthrough the code:
void WireChannel(HttpNotificationChannel channel)
{
channel.ChannelUriUpdated += myChannel_ChannelUriUpdated;
channel.ShellToastNotificationReceived +=
channel_ShellToastNotificationReceived;
channel.ErrorOccurred += channel_ErrorOccurred;
}
When I wire up the channel, I can wire up events for:
In my web service (WCF but it could be REST, ASMX or MVC too), I am simply getting the URI and saving it to the database for use later:
public class MyPhoneService : IMyPhoneService
{
public bool RegisterPhoneApp(string toastUrl)
{
// Add to list of URI's to notify
using (var ctx = new PhoneEntities())
{
if (ctx.Phones.Where(p => p.PhoneUrl == toastUrl).Count() == 0)
{
var phone = Phone.CreatePhone(0, toastUrl, DateTime.Now);
ctx.Phones.AddObject(phone);
ctx.SaveChanges();
}
}
// Always return true;
// should send better status, but this is an example ;)
return true;
}
}
The last part of the puzzle is the actual sending of the message. The messaging service that the Microsoft Push Notification Service uses is not SOAP based but simple HTTP messages. For Toast Push Notifications you will need to POST (e.g. HTTP message) an XML message that looks like this:
<?xml version="1.0" encoding="utf-8"?>
<wp:Notification xmlns:wp="WPNotification">
<wp:Toast>
<wp:Text1>First Line</wp:Text1>
<wp:Text2>Second Line</wp:Text2>
</wp:Toast>
</wp:Notification>
This means the notifications are a one-off network call:
HttpWebRequest sendNotificationRequest =
(HttpWebRequest)WebRequest.Create(pushUri);
// For Toast Update
sendNotificationRequest.ContentType = "text/xml";
sendNotificationRequest.Headers.Add("X-WindowsPhone-Target", "toast");
sendNotificationRequest.Headers.Add("X-NotificationClass", "2");
sendNotificationRequest.Method = "POST";
// Unique ID to help not repeat msg (Optional)
sendNotificationRequest.Headers.Add("X-MessageID",
Guid.NewGuid().ToString());
// Send it
var msg = string.Format(toastMessage,
"Toast Message",
"This is from the server");
byte[] notificationMessage = Encoding.UTF8.GetBytes(msg);
sendNotificationRequest.ContentLength = notificationMessage.Length;
using (Stream requestStream = sendNotificationRequest.GetRequestStream())
{
requestStream.Write(notificationMessage,
0,
notificationMessage.Length);
}
You’ll note that in the I am using headers when I make the call. The first two headers (“X-WindowsPhone-Target” and “X-NotificationClass”) specify that this is a toast notification. The third header is specifically to help you limit duplication of a message. You can download the example here: FunWithToast.zip
Architectural Considerations
So you now know how its done, but what are the implications? I was chatting on Twitter today about why so few apps and @justinangel reminded me of one compelling problem with the service…it its a one call per message API. NOw if you’re building a modest application, especially if you have an existing, scalable web-tier, its not too bad. But imagine if you need to send 25,000 toast notifications? It would require you be able to scale up your service pretty quickly (e.g. something like EC2/Azure would help). It also requires that you have more than just the Phone application, but have a web presence to handle the incoming and outgoing http communication. So you have the extra expense which makes it hard for small-scale application development.
I am not as cynical as Justin on this fact, but I do know that when you build your application, that using Notification services (especially Toast since they are typically time-sensitive) means you have to take scale into account.