Whitepaper: The ins and outs of data type interop between .NET/WCF and Java – add it to your [web] services tool belt
Sending emails from Windows Azure using Exchange Online web services (BPOS, for the search engines)

Implementing Push Notifications for iOS with C# & MonoTouch using the Cloud & Urban Airship

A bit of mobile goodness today.

Communication-wise push notifications are one of my personal favorite features on any mobile device platform Smile So, it seems natural to write a bit about it.
This time we will see how to use a third-party (but still free) service in the cloud to handle the push notifications for us (independent of Apple, Google, Blackberry or Microsoft). The sample shown here in this blog entry is for iOS and is written and built in C# with MonoTouch.

Although, the ideas of push notifications are similar between iOS; Windows Phone 7 and other device platforms, there are differences in implementation (and partly in architecture). What I wanted to achieve for my push notification-enabled apps was two-folded:

  1. Being able to easily scale out and handle potentially huge numbers of user/subscribers and push messages without having to write, host and maintain my own code and services and servers
  2. Not having to implement push notifications logic (like register, unregistering, receiving, error handling etc) for each and every platform over and over

This lead me to two solutions:

  1. Use C# everywhere (oops, is this trademarked anywhere? Winking smile) with MonoTouch, MonoDroid and WP7 developer tools
  2. Use a cloud service offered by an independent third party. In my case this is Urban Airship (free for 1,000,000 messages per month)

Rock da cloud.

Turns out that Urban Airship has a web-friendly REST API (oh wonder!) which accepts JSON messages. Honestly, it was not hard to decide to write some C# to utilize this service instead of having to deal with Apple’s APNs directly.

Note: the code illustrated here is a bit outdated. It relies on some iOS intrinsics like UIApplication and NSUserDefaults and therefore is not portable across devices. I have some platform-independent code lurking here but need to fully implement, test and polish it first.

The most important piece of code is the PushNotifications class which handles all the necessary communication, especially with the Urban Airship service:

public static class PushNotifications
{
     public static void Subscribe ()
     {
          UIApplication.SharedApplication.RegisterForRemoteNotificationTypes (
UIRemoteNotificationType.Alert | UIRemoteNotificationType.Badge); } public static void Unsubscribe () { UIApplication.SharedApplication.UnregisterForRemoteNotifications (); } public static void EnsureDeviceRegistration (NSData deviceToken) { var str = (NSString)Runtime.GetNSObject ( Messaging.intptr_objc_msgSend (deviceToken.Handle, new Selector(
"description").Handle)); var deviceTokenString = str.ToString ().Replace ("<", "").Replace (">", "")
.Replace (" ", ""); var token = LoadFromSettings (); if (String.IsNullOrEmpty (token) || token != deviceTokenString) { Console.WriteLine (String.Format ("EnsureDeviceRegistration: {0}, {1}",
deviceTokenString, deviceTokenString.Length)); var server = @"https://go.urbanairship.com"; var url = String.Format("{0}{1}{2}", server, "/api/device_tokens/",
deviceTokenString); Console.WriteLine ("EnsureDeviceRegistration: URL: " + url); var putUrl = new Uri(url); var request = (HttpWebRequest)HttpWebRequest.Create(putUrl); request.Method = "PUT"; request.ContentType= "application/json"; request.Credentials = new NetworkCredential(Settings.APNsUser,
Settings.APNsPassword); var payload = @"{""alias"": """ + Settings.DeviceAlias + @"""}"; Console.WriteLine ("EnsureDeviceRegistration: Payload: " + payload); var byteArray = Encoding.UTF8.GetBytes(payload); request.ContentLength = byteArray.Length; var dataStream = request.GetRequestStream(); dataStream.Write(byteArray, 0, byteArray.Length); dataStream.Close(); var response = (HttpWebResponse)request.GetResponse(); Console.WriteLine("EnsureDeviceRegistration: Status: " +
response.StatusCode); SaveToSettings (deviceTokenString); } else { Console.WriteLine("EnsureDeviceRegistration: device already registered."); } } private static string LoadFromSettings () { var prefs = NSUserDefaults.StandardUserDefaults; return prefs.StringForKey("DeviceToken"); } private static void SaveToSettings (string deviceTokenString) { var prefs = NSUserDefaults.StandardUserDefaults; prefs["DeviceToken"] = new NSString (deviceTokenString); } }


Pretty simple – I did deliberately decide not to use any JSON API or serializer here.

Then, for my iOS apps I usually implement a couple of overrides in my AppDelegate class like this:

public override void RegisteredForRemoteNotifications (UIApplication application, 
NSData deviceToken) { Console.WriteLine ("RegisteredForRemoteNotifications"); PushNotifications.EnsureDeviceRegistration (deviceToken); } public override void ReceivedRemoteNotification (UIApplication application,
NSDictionary userInfo) { Console.WriteLine ("ReceivedRemoteNotification"); var aps = userInfo.ObjectForKey (new NSString (
CommunicationConstants.ApsRootElement)) as NSDictionary; var alert = aps.ObjectForKey (new NSString (
CommunicationConstants.ApsAlertElement)).ToString (); Console.WriteLine ("Alert: " + alert); var sound = SystemSound.FromFile (new NSUrl (ResourcesConstants.ExplodeSound)); sound.PlayAlertSound (); var av = new UIAlertView("Notification:", alert, null, "OK", null); av.Show (); (navigationController.VisibleViewController as EpisodesTableViewController)
.LoadEpisodesData (); }
public override void FailedToRegisterForRemoteNotifications (UIApplication application,
NSError error) { Console.WriteLine (String.Format ("FailedToRegisterForRemoteNotifications:
{0}, {1}, {2}"
, error.Domain, error.Code, error.LocalizedDescription)); }

Then in my FinishedLaunching method (or where you decide to do it) I subscribe to push notifications:
PushNotifications.Subscribe();


That’s it – hope this helps someone. If you have more questions, please let me know.


P.S.: a big Kudos to the MonoTouch and MonoDroid teams and their immediate help with technical questions & issues!

Comments

Feed You can follow this conversation by subscribing to the comment feed for this post.

Andy McGoldrick

Excellent, I was just about to start working on something very similar.

Thanks for the great information, you saved me a lot of effort here. Would love to see the platform independence changes you are going to make to the code above.

Thanks again!

Jay

hi,

can you explain how the push notifications works?
for example, i have a database that should be mointored for every insert in a table then this should send notification to the subscribed apps. how will that work using your code above and urban airship.

Thanks in advance.

Khaldoun

Hi thanks for this great work
How can we customize the urbainShip Service to communicate wih our webServices

Hichem

What is the object
-Settings
- ConmmunicationsConstant
- ResourcesConstans

Ahat is the dll that I have to use!!!
it does not work for me

Chris Moses

Thanks,
This looks very helpful. Could you please post a full sample project that both publishes and receives?

Christian Weyer

Hi Chris,

this sounds like a helpful thing, right? But I am so swamped at the moment that I cannot post a sample any time soon, sorry :(

Roycornelissen.wordpress.com

Nice post! I'd be very interested in the device independent stuff if you have it lying around.

Verify your Comment

Previewing your Comment

This is only a preview. Your comment has not yet been posted.

Working...
Your comment could not be posted. Error type:
Your comment has been saved. Comments are moderated and will not appear until approved by the author. Post another comment

The letters and numbers you entered did not match the image. Please try again.

As a final step before posting your comment, enter the letters and numbers you see in the image below. This prevents automated programs from posting comments.

Having trouble reading this image? View an alternate.

Working...

Post a comment

Comments are moderated, and will not appear until the author has approved them.

Your Information

(Name is required. Email address will not be displayed with the comment.)