Condividi tramite


Simulated Push Notifications on Windows Phone 7

I’m currently prepping for my TechEd Europe WP7 session and I like to prepare for the worst. My demo relies on some cloud services and also on WP7 push notifications. Push notifications are nice but they have one drawback for demos, I need an internet connection.

Now in the real world my application would indeed need an internet connection or it would actually be kind of useless, but in demoland I don’t need one as I can run the cloud services portion in the Azure DevFabric which is nice in case the internet connectivity in the demo room goes down, I have a backup.

The same isn’t true of push notifications, they are managed by Microsoft and if I can’t get to them I can run my demo even though my own cloud services are running locally. In order to get around this I wrote a simple WCF service that also runs in my Azure DevFabric along with my other services that can act as a push notification endpoint. It’s not totally seamless but it works quite nicely for me as I now have a fallback for push notifications as well as for my own cloud services.

The Service

The service is a REST based WCF service that has two methods on it’s rather simple interface.

01 [ServiceContract]
02 public interface INotifications
03 {
04     [OperationContract]
05     [WebInvoke(Method="POST", UriTemplate = "Notify",
06         BodyStyle = WebMessageBodyStyle.Bare, RequestFormat = WebMessageFormat.Xml)]
07     void Notify(Message message);
08  
09     [OperationContract]
10     [WebInvoke(Method = "GET", UriTemplate = "Notifications/{id}")]
11     Message GetNotifications(string id);
12 }

The service then queues any incoming messages until the client gets them. The notify endpoint will quite happily take push notification requests from my existing Azure services without modification.

01 [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single,
02                     ConcurrencyMode = ConcurrencyMode.Multiple)]
03 public class NotificationsService : INotifications
04 {
05     private class NotificationMessage
06     {
07         public string MessageContentBase64 { get; set; }
08     }
09  
10     private ConcurrentDictionary<string, ConcurrentQueue<NotificationMessage>> _messages;
11  
12     public NotificationsService()
13     {
14         _messages = new ConcurrentDictionary<string, ConcurrentQueue<NotificationMessage>>();
15     }
16  
17     public void Notify(Message message)
18     {
19         // Grab the notification from the message body
20         var queryParams = ParseQueryString(message.Headers.To.Query);
21         var body = message.GetReaderAtBodyContents();
22  
23         XElement data = XElement.Load(body);
24  
25         var queue = _messages.GetOrAdd(queryParams["id"], key => new ConcurrentQueue<NotificationMessage>());
26  
27         queue.Enqueue(new NotificationMessage
28                         {
29                             MessageContentBase64 = (string)data,
30                         });
31  
32         Log.Default.TraceInformation("Notification from: {0}", queryParams["id"]);
33     }
34  
35     private static IDictionary<string, string> ParseQueryString(string queryString)
36     {
37         var queryItems = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase);
38  
39         queryString = queryString.TrimStart('?');
40         string[] strings = queryString.Split('&');
41  
42         foreach(string queryItem in strings)
43         {
44             int equalsIndex = queryItem.IndexOf('=');
45             if (equalsIndex >= 0)
46             {
47                 queryItems.Add(queryItem.Substring(0, equalsIndex),
48                                 queryItem.Substring(equalsIndex + 1));
49             }
50             else
51             {
52                 queryItems.Add(queryItem, null);
53             }
54         }
55  
56         return queryItems;
57     }
58  
59     public Message GetNotifications(string id)
60     {
61         XElement response = new XElement("notifications");
62         ConcurrentQueue<NotificationMessage> queue;
63  
64         Log.Default.TraceInformation("Checking notifications for: {0}", id);
65  
66         if (_messages.TryGetValue(id, out queue))
67         {
68             NotificationMessage message;
69             while (queue.TryDequeue(out message))
70             {
71                 response.Add(new XElement("notification", message.MessageContentBase64));
72  
73                 Log.Default.TraceInformation("De-queued notification for: {0}", id);
74             }
75         }
76  
77         WebOperationContext.Current.OutgoingResponse.Headers[HttpResponseHeader.CacheControl] = "no-cache";
78         return WebOperationContext.Current.CreateXmlResponse(response);
79     }
80 }

The Client

The WP7 application isn’t quite as lucky. I could probably have done something that faked getting the notifications a little better but the simplest solution was to just have a background timer that polls my service and gets the notifications. It then processes these using the normal code path for push notifications in my application.

I really should probably use the reactive extensions (RX) to manage my push notifications in the client as that would make them nice and mockable too but for now this works as a solution and keeps the client code reasonably understandable for demo purposes.

All in all it works quite nicely and means I don’t have to have an internet connection to now demo my application, even with push notifications.

 

Originally posted by Robert Garfoot on 5th October 2010 here: https://garfoot.com/blog/2010/10/simulated-push-notifications-on-windows-phone-7/\#more-321