READ THIS FIRST
Update: 3-4-2016: If you're finding this blog and looking for information related to Azure Mobile Services, I'd strongly recommend checking out Azure Mobile Apps. Azure Mobile Apps is a new version (consider it a v2) of Azure's mobile backend support. All of the same features of Azure Mobile Services are there, with a lot of other very cool features to go along. You can read more about Azure Mobile Apps, and how to transition from Azure Mobile Services, here.A while back (a year ago counts as awhile back, right?), I posted about the General Availability of Azure Notification Hubs. Since then, I’ve given a few presentations at different conferences (TechEd, AnDevCon, CocoaConf, 360iDev, etc) on how you can make use of Notification Hubs to power your push notifications. However, I haven’t really posted about it or released sample code demonstrating how it works on the different platforms. Technically, I did release a very comprehensive app, named LensRocket, that uses Notification Hubs, however, that is a huge sample so if you’re just looking for “how do i do push?” it might be confusing. Today I’m going to start fixing that by releasing a fairly comprehensive explanation of push focusing on iOS clients. I’ll follow this up shortly with the same walkthrough for Android. Note that if you want to pull down the completed app, you can access all of the source code here in GitHub.
Push Notifications
For those who may not already know, I’ll give a quick overview of what Azure Notification Hubs are and why they exist. When building client applications (whether they’re iPhone, Android, WinPhone, or WinStore apps) one of the capabilities apps have is to receive Push Notifications. Push Notifications are a way of delivering information to your app without your app having to go out and request it. These pushes could inform the user / app of many many different things and just a few examples of them include: you’ve received an email (including increasing a badge number), play a sound, display a message, download some data from a server. What’s really key here is that you can send this information down to a device without your app needing to request it first, or without your app even running! This means that push notifications end up being used a LOT. Fortunately, when you’re building an app (regardless of the platform) it’s pretty simple for you to take advantage of push notifications.
For each client platform, there is a Push Notification Service (PNS) provided by the vendor primarily responsible for the OS. So:
- iOS has Apple Push Notification Service (APNS)
- Android has Google Cloud Messaging (GCM) and Amazon Device Messaging (ADM) for Google Android and Amazon Android, respectively
- WinPhone has Microsoft Push Notification Service (MPNS)
- Windows Store has Windows Notification Service (WNS)
- Other OSes provide their own services but are more bit players
Each of these PNS will provide to your application some identifier that identifies your app and device to the PNS. Once you get this identifier, you can then use it to talk to the PNS and ask it to deliver a push notification to the device / app. While you COULD request a push from the same device that you’re running the app on, what is common practice is to have a backend somewhere that you send the identifier to. This backend can then talk to the PNS and request a push. This backend could be a website running somewhere, a web service, a virtual machine, a client application, or really anything else capable of making an HTTP request to the PNS.
Notification Hubs
So far we have a client app, we have a PNS, and we have a backend. Let’s talk about how Notification Hubs fits into this. Notification Hubs is basically built to be a pseudo backend / service that will handle talking with the PNSes. That’s a pretty big simplification though because there is a lot behind that. First, when your backend asks a PNS to deliver a push notification, every request has to be signed with either an API key or a certificate (depending on which PNS it is). So not only does NH need to be able to handle taking in the keys / certs and using them for each request, it needs to be capable of updating to new versions as the certs do expire! While that’s more of administrative details, NH also enables easy targeting of push notifications using Tags (more on this below) as well as enabling single line of code cross-platform push via templates (more on this also below). Let’s start seeing some code so we understand how this works.
Registering with APNS
The code to register with APNS is all standard Objective-C and doesn’t have anything to do with NH:
Here we have two methods in the AppDelegate. The first didFinishLaunchingWithOptions is the first point in the app’s code that you can start doing things. Here we call registerForRemoteNotificationTypes and pass in the types of push we might want to receive. The second method, didRegisterForRemoteNotificationsWithDeviceTokenb is the callback that is invoked when APNS responds. Here we’re just saving that as a property on the view controller. The last method above is the didReceiveRemoteNotification method which is called when our app is in the foreground and receives a push notification. Here we could do anything we want (show something to the user, pull down more data, play a sound, etc) based off the data sent over. In this case we’re pulling out a value and displaying a UIAlertView.
Setting up Notification Hubs and your Project
The next thing you’ll want to do is get the push certificate for your app identifier from the Apple Dev Portal and create a Notification Hub in the Azure portal. If you haven’t done either of those before, they’re both documented in detail here. Once you’re done with that, go to your Hub in the portal and go to the CONFIGURE tab. Find the area for apple notification settings and upload your p12 certificate file. Be sure to select the MODE (either production or sandbox) that corresponds to your cert type (as obtained in the Apple Dev Portal) or pushing won’t work. After that’s done, you’ll need to download the Azure Mobile SDK. Note that this download currently contains two frameworks: one for Mobile Services and one for Notification Hubs. We’ll only be using the Notification Hubs framework so you can ignore the other. Copy the WindowsAzureMessaging.framework into your project. Before you continue on, make sure the Bundle Identifier (Found in your iOS project’s General Settings) matches the one you created in the Apple Dev Portal. Then go to your project’s Build Settings and set the Provisioning Profile and Code Signing Identity to the ones generated to match your App ID.
Creating a Notification Hub Instance
The first step in our apps code for talking to our hub is to set up a SBNotificationHub. I’ve added a property to hold this value in my ViewController and then will instantiate it in the viewDidLoad method:
You’ll want to get the hub name from the hub you created and the ListenSharedAccessSignature can be obtained by going to the DASHBOARD tab of your hub in the portal and clicking CONNECTION INFORMATION at the bottom. Make sure you DO NOT get the full signature for now. Now you’re ready to start using your hub.
Today, we’re going to go over three different ways of registering and the accompanying way to push for that registration. The first is just plain registration with the hub which will facilitate Broadcast push (i.e. push to all the iOS devices).
Broadcast Push
Broadcast push is a way of doing untargeted push notifications. In other words, push to all of the iOS devices that have registered with my hub. Let’s look at the client side code first:
Here we’re calling registerNativewWithDeviceToken on the hub we just created. Note that the first parameter is the push token we receive from APNS and the second parameter is currently nil. When this runs it will contact our hub and basically say “I’m an iOS device that is registered with APNS, here’s my push token.” Now let’s look at the server side code to trigger a push.
Here you’ll replace the strings in call to createNotificationHubService with your hub’s name and the FULL Access Signature. We then use the notificationHubService object to call apns.send where we specify the payload we want to send. Running this script will trigger our hub to push that payload to any iOS / APNS device that has connected to the hub. One other thing to note is that these scripts were created to be run as Mobile Service Custom APIs in order to ease testing out (with the permissions set to Everyone, I can trigger this script by going to the endpoint for it in a browser). I’ll talk more about connecting to Notification Hubs to trigger pushes later, but for now just know that this same Node.JS Script could run anywhere you can run Node. You’d just need to pull down the Azure Node Package.
Pushing to Tags
Up next we’ll talk about targeted pushes. So instead of “Broadcast to everyone” we’ll be able to say “Push to user X” or “Push to this group of users”. The first step in doing this is registering from the client:
This is identical to the code we had on our client before except now we’re passing in a second parameter, an NSArray of tags. In this case, my array has three tags: MyTag, AllUsers, and iOSUser. When we run this it basically says, “I’m an iOS device that is registered with APNS, here’s my push token. Also, tie it to these three tags.” What’s great about this is then from our backend, we can tell our hub to only push to devices that are tied to a specific tag, like this:
Here we’re doing the same thing on the server, EXCEPT we’re passing in a first parameter (previously it was null). In this case we’re passing in the tag MyTag so only devices registered with my hub, with APNS, will receive the push notification.
Tag Expressions
Pushing based off of tags is pretty cool. You could use tags for lots of different things (sports teams, stock ticker labels, zipcode (i.e. geopush), etc). You could even set the user’s username (if they were logged in) as a tag and then you’d have the ability to say “push to all of Chris’ devices” if you wanted. Another powerful feature of hubs is called Tag Expressions. This basically enables you to trigger pushes based off logical expressions of tags. So for example, if I was registering with the username and a group I’m in, I could say “Push to GROUPNAME except ME”. So push to everyone in the group I’m in but not my devices. The actual tag string I would use would probably look something like this “Group:ID && !User:ID” where the tags I register with might be “Group:ID” and “User:ID”.
Templates
So far we’ve looked at broadcast and targeted based push. However, all of the pushing we’ve done has been constrained to APNS devices. If I’m building a cross-platform app, I need to be able to handle pushing to other platforms as well. Now I could have the same code to do a push to Android and WinPhone and WinStore right after my apns.send code everywhere I want to do a push, but that’s a decent amount of code. Especially if I am pushing in many different places. The alternative to that is using Templates. When you register with your hub, in addition to sending the push identifier and the tags you want to be tied to, you can also specify a template that indicates the format of the payload you want to receive. Let’s take a look:
Here we’re creating a template string that contains the aps JSON format that we previously saw in our server script. Inside of that payload is the important bit $(message). From the server side, when we push, it will use that message bit to figure out how to format the payload with the data we want to inject. The other difference here is that we’re now calling registerTemplateWithDeviceToken. Let’s take a look at the Node script to push to our client now:
Now we’re creating a payload object which is a JSON object with a message field in it. We can then use notificationHubService.send (notice there is no apns in the middle anymore) and set the payload as the second parameter (we’re not using a tag to do a targeted push here but we could). When this runs, it will cause the hub to find any device registration that has a payload with the message field in it and format the payload with the data from the payload we’re sending in. So effectively, when we send to our iOS device, we’ll use the aps payload format specified on the client. If i was doing something for Android, we’d send it in the Android specific format, and so on. So we now have a single line that is capable of handling pushing to any device type!
Talking to Notification Hubs from Your Backend
The idea here is that Notification Hubs can effectively handle all the aspects of Push Notifications for you. I can have a backend (website or web service) running somewhere, whether its on Azure or not or it’s in .NET or not doesn’t matter at all, and just talk to my Notification Hub whenever I need to trigger a push. Now in the samples above, I’m trigger my pushes from Node.js scripts which make use of the Node.js Azure module. These could be running anywhere but to make things easy, I usually run them from Azure Mobile Services (super quick to get them up and running). You can also use the .NET SDK for triggering push notifications, and their are guides for doing so from Java as well as from PHP. Everything in Notification Hubs is also exposed over a REST API so even if there isn’t an SDK or a guide for talking to your hub from your platform of choice to trigger pushes, you can always fall back on the REST API.
Summary
I hope this post proves helpful for anyone looking at using Notification Hubs to handle iOS push notifications. As I said above, I’ll post a tutorial on doing the same thing from Android (though it will be 90% the exact same post because only the client side code is really different) and you can do everything I’ve described above from any of the other client platforms. Notification Hubs are a super easy way to get into doing push notifications for your apps and make it super simple to make them working on different platforms. Add to all these features the fact that you get 1 million push notifications for FREE every month (as of Sept. 1, 2014) and it’s a pretty sweet deal! You can grab both the client and server source code for the app seen above here.
Comments