Windows Phone 7 push notifications are tricky. Take Microsoft’s clear-as-mudd “overview” diagram, for example…
All sorts of colors and arrows going on there. The green parts represent code that you write, and the blue parts represent what Microsoft already has in place. The numbers and arrows represent the awkward dance between them. I’ll try to explain…
First, in your application (green side of step 1) you have to use Microsoft’s client side portion of their Push Notification Service, which is the HTTPNotificationChannel class (blue side of step 1) , to see if the app already has an open notification channel with Microsoft’s service (that’s the MPNS box in the bottom right). Only one channel per application is allowed, and you can use that single channel for all of your apps Push Notification needs.
If there is no channel open already, you can go ahead and use the aforementioned HTTPNotificationChannel class to set one up. This is step 2 in the diagram above, and here is a sample of it in code….
*I used this tutorial by Ben Cull for most of the code you’re going to see, until we get to the “multi-app” support on the server.
private void RegisterDevice() { HttpNotificationChannel httpChannel; httpChannel = HttpNotificationChannel.Find("TestChannel"); if (httpChannel != null) { SubscribeToChannelEvents(); SubscribeToService(); } else { httpChannel = new HttpNotificationChannel("TestChannel", "TestService"); SubscribeToChannelEvents(); httpChannel.Open(); if (!httpChannel.IsShellToastBound) { httpChannel.BindToShellToast(); } if (!httpChannel.IsShellTileBound) { var c = new System.Collections.ObjectModel.Collection<Uri>(); c.Add(new Uri("http://0.tqn.com/")); httpChannel.BindToShellTile(c); } } }
The call to the “SubscribeToChannelEvents()” method above is represented by step 3 in the diagram, and here’s what it looks like in code…
private void SubscribeToChannelEvents() { httpChannel.ChannelUriUpdated += new EventHandler<NotificationChannelUriEventArgs>(httpChannel_ChannelUriUpdated); httpChannel.HttpNotificationReceived += new EventHandler<HttpNotificationEventArgs>(httpChannel_HttpNotificationReceived); httpChannel.ShellToastNotificationReceived += new EventHandler<NotificationEventArgs>(httpChannel_ShellToastNotificationReceived); httpChannel.ErrorOccurred += new EventHandler<NotificationChannelErrorEventArgs>(httpChannel_ErrorOccurred); }
Step 4 in the diagram is sending your notification channel’s URI (it gets this URI through magic during step 2). This is where you need to have a WCF REST Service set up with a database to hold your device id’s and URI’s. The code below shows how to subscribe to the service from the client side, but if you want the server side setup and registration you can find all of that in Ben Cull’s blog post here
public string ParseANID(string anid) { if (!String.IsNullOrEmpty(anid)) { string[] parts = anid.Split('&'); IEnumerable<string[]> pairs = parts.Select(part => part.Split('=')); string id = pairs.Where(pair => pair.Length == 2 && pair[0] == "A").Select(pair => pair[1]).FirstOrDefault(); return id; } return String.Empty; } private void SubscribeToService() { if (!NetworkInterface.GetIsNetworkAvailable()) { return; } string id = ParseANID(UserExtendedProperties.GetValue("ANID") as string); var emulatorUrl = string.Format("http://localhost:55875/notifications/register?deviceid={0}&uri={1}", id, httpChannel.ChannelUri.ToString()); var wc = new WebClient(); wc.DownloadStringCompleted += new DownloadStringCompletedEventHandler(wc_DownloadStringCompleted); wc.DownloadStringAsync(new Uri(emulatorUrl)); }
The “ANID” that you see above is pretty much a unique guid generated for each user’s Live ID. Also, the emulatorUrl variable above is assuming that you’re running the WCF service on the same machine that you’re writing the client side phone code on, and that you’re using the phone emulator to debug. If you’re using an actual device to debug than “localhost” will produce nothing but disappointment, so adjust your url as needed. At this point, if you followed the WCF REST Service tutorial i linked to above, you’ll have the infrastructure in place to send push notifications, but no actual way of doing so yet.
For that we’ll need a service running on the server, with a timer that goes out and determines if there’s any updates it needs to send to anyone (for example, a weather service that checks for severe weather wvery 30 minutes based on a zip code sent by the client side phone app). But all of this is for a single app. If I wanted to do push notifications again in the future i would have to do this all over again for each new app. Instead, I decided to use a single REST Service, web service, and central device database. The service doesn’t contain the timer with the application logic. Instead, each app that you want to support is represented by a class that you instantiate in the OnStart() method of the service, and each app class contains its own timer, logic, and its own database with device id’s and any parameters required for it to work. For the sake of simplicity below are 2 examples of test apps that use the AppName property of the central database rather than their own seperate databases….
namespace PushNotificationService.Apps { public class TestApp { private WP7AppDBEntities _dbContext; private readonly string _appName = "TestApp"; public TestApp() { _dbContext = new WP7AppDBEntities(); var t = new Timer(); t.Interval = 10000; t.Elapsed += new ElapsedEventHandler(OnTimerElapsed); t.Start(); } private void OnTimerElapsed(object sender, ElapsedEventArgs e) { _dbContext.Devices.Where(d => d.AppName == _appName).ToList().ForEach(d => { try { PushNotifications.SendToast(d.URI, "Test", "Hello"); } catch{} }); } } }
namespace PushNotificationService.Apps { public class TestApp2 { private WP7AppDBEntities _dbContext; private readonly string _appName = "TestApp2"; public TestApp2() { _dbContext = new WP7AppDBEntities(); var t = new Timer(); t.Interval = 20000; t.Elapsed += new ElapsedEventHandler(OnTimerElapsed); t.Start(); } private void OnTimerElapsed(object sender, ElapsedEventArgs e) { _dbContext.Devices.Where(d => d.AppName == _appName).ToList().ForEach(d => { try { PushNotifications.SendToast(d.URI, "TestApp2!", "It's working!"); } catch{} }); } } }
The PushNotifications.SendToast method that you see above represents step 5 of the diagram, and is provided in detail here.
And here are the classes being instantiated in the OnStart() method of the service…
protected override void OnStart(string[] args) { var testApp = new TestApp(); var testApp2 = new TestApp2(); }
hi i am windows phone 8 developer.. am having one question how to create the channel url in windows phone 8 for creating push notification?..
please i need the answer very soon..if anyone know means please post in this site..
I’m not sure exactly. I haven’t tried it in WP8 yet. I will try to put a sample together later today.