Requirements
 
Prerequisite knowledge
This article requires at least an intermediate level of PhoneGap development experience.
 
User level
Intermediate
In this article, I will explain how to set up and use Apple Push Notifications (APNs) in your PhoneGap mobile applications. Push notifications are different than local notifications in that they come from a third-party server to inform the user of something, versus a local notification which is scheduled by the application and run on the device itself without any server interaction.
 
For instance you may receive a push notification from Facebook notifying you that someone has added you as a friend, or if you are a Words With Friends player, you may receive a push notification indicating it’s your turn to play. An example of a local notification would be an alert popping up at a certain time or at an interval as a reminder from a to-do application where you set a date/time for a task; the alert pops up to remind you at that specified time. To end users, both notifications may appear the same in that they both pop alerts that can have sounds associated and other features, but they are very different from a development perspective.
 
There are Cordova/PhoneGap plugins to do both local and push notifications for iOS, but this series will focus on how to develop for push notifications. If you’re wondering about developing push notifications for Android, there is a concept of push notifications, but the setup and process is a bit different and will be covered in a separate article.
 
The process to get started with setting up APNs can be a bit intimidating initially but it’s worth taking the time to do so as the use cases are endless. This article is intended to help you understand the whole process including setup and what occurs on the application and server-side, with sample code to get you started quickly.

Explanation of the APN workflow
 
To start, it’s best to understand the workflow of Apple Push Notifications (APNs).
 
● Upon launch, your application communicates with the Apple Push Notification Service to authorize it to receive push notifications.
● Apple responds with a unique token that must be used in all future communications to receive push notifications.
● Your application sends that token to a third-party server (your own or some other provider, such as Urban Airship) that stores it for later events when the application needs to be notified.

 

The workflow of Apple Push Notifications.
Figure 1. The workflow of Apple Push Notifications.
 
Image credit to Ray Wenderlich.

SSL certificates and provisioning
 
Don’t let this step scare you, it’s not that bad once you do it. There’s a great article by Ray Wenderlich, Apple Push Notification Services Tutorial: Part 1 of 2, which documents this process with screenshots and details that you should use for this step.
 
The following is a general overview for what you do to enable your app for push notifications. First, your application needs to be enabled for push notifications through the Apple iOS Provisioning Portal through an App ID (com.mysite.myapp) and signed with a provisioning profile that includes the push-enabled application identifier. Next, you also need to associate the App ID with an SSL certificate for communicating securely with Apple’s Push Notification Server. When you configure the App ID through the portal, a wizard will prompt you to create an SSL certificate that will be associated with your App ID and used for that purpose. Having the association with the App ID ensures that the notifications sent from your server to Apple’s APN Server only sends to the application with the matching id.
 
Once the certificate process completes, download a new provisioning file for this new App ID that contains the enabled push notifications. You then drag it into XCode and make sure it is the provisioning profile that is selected for your application in the Code Signing screen (under “Build Settings” for your project).

Setting up your application
 
This article assumes you know how to create a project including PhoneGap/Cordova, if not, please refer to the Getting Started with iOS section on the PhoneGap/Cordova website). Now that you’re ready to start coding your HTML/JavaScript/PhoneGap application, let’s go through the following steps to set it up.
 
1. Get the latest PhoneGap Push Notifications plugin from GitHub.
2. Drag and drop the PushNotification folder to the Plugins folder in XCode. Select Create groups for any added folders for the copy option as shown in the screenshot.

 

Setting up your app.
Figure 2. Setting up your app.
 
3. Go out of XCode and into Finder and copy the PushNotification.js file into a www\plugins (plugins subfolder underneath the www folder). It will automatically show up in XCode for you.
4. Add a script tag to refer to the PushNotification.js file in your HTML file, as follows:
<script src="js/plugins/PushNotification.js"></script>
5. Add the plug-in key and value to your Cordova.plist (find this file under your project root Resources folder).
 
Adding the plugin to your Cordova.plist.
Figure 3. Adding the plugin to your Cordova.plist.
 
The Cordova PushNotification plugin gives you a nice JavaScript API that you can use from your HTML/JS files to interact with the underlying native code to handle the registration and receive push notifications. Some of the functions provided are as follows:
● registerDevice()
● setApplicationIconBadgeNumber()
● getRemoteNotificationStatus()
● getPendingNotifications()
To use the JavaScript API, though, you first need to add some things to the native application code to bridge to the specific push notification code. The AppDelegate class for an application (located under your project Classes folder) implements the handlers for application-wide events, such as application launch, termination, and more. There are also events available for handling notifications. A list of them can be found in the Handling Notifications reference.
These methods are not implemented by default, however, so in order to support them you must add the code handlers and have them delegate to the included PushNotification.m class in the plugin code. The code to add is shown in the README file for the plugin, but we discuss it here as well. You are basically handling three events:

 

● didReceiveRemoteNotification
● didRegisterForRemoteNotificationsWithDeviceToken
● didFailToRegisterForRemoteNotificationsWithError
 
Add the following code block to your AppDelegate.m class before the @end to handle the notification events:
 
/* START BLOCK */
#pragma PushNotification delegation
 
- (void)application:(UIApplication*)app
didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken
{
    PushNotification* pushHandler = [self.viewController getCommandInstance:@"PushNotification"];
    [pushHandler didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
}
- (void)application:(UIApplication*)app didFailToRegisterForRemoteNotificationsWithError:(NSError*)error
{
    PushNotification* pushHandler = [self.viewController getCommandInstance:@"PushNotification"];
    [pushHandler didFailToRegisterForRemoteNotificationsWithError:error];
}
- (void)application:(UIApplication*)application didReceiveRemoteNotification:(NSDictionary*)userInfo
{
    PushNotification* pushHandler = [self.viewController getCommandInstance:@"PushNotification"];
    NSMutableDictionary* mutableUserInfo = [userInfo mutableCopy];
    // Get application state for iOS4.x+ devices, otherwise assume active
    UIApplicationState appState = UIApplicationStateActive;
    if ([application respondsToSelector:@selector(applicationState)]) {
        appState = application.applicationState;
    }
    [mutableUserInfo setValue:@"0" forKey:@"applicationLaunchNotification"];
    if (appState == UIApplicationStateActive) {
        [mutableUserInfo setValue:@"1" forKey:@"applicationStateActive"];
        [pushHandler didReceiveRemoteNotification:mutableUserInfo];
    } else {
        [mutableUserInfo setValue:@"0" forKey:@"applicationStateActive"];
        [mutableUserInfo setValue:[NSNumber numberWithDouble: [[NSDate date] timeIntervalSince1970]] forKey:@"timestamp"];
        [pushHandler.pendingNotifications addObject:mutableUserInfo];
    }
}
/* STOP BLOCK */
 
The above code essentially creates a reference to the PushNotification class.  It sets or reads some values and calls different methods with parameters depending on the event that occurred. Don’t worry if you don’t know any Objective-C or this seems complicated at this point, I have included the sample project that you can downloaded in the Requirements section for reference.
Finally, add a code fragment into the  didFinishLaunchingWithOptions  method in that same AppDelegate.m class to handle opening the application from a notification (and adding the received object to the pendingNotifications for later retrieval). Add this block right before the end return YES, as follows:
/* Handler when launching application from push notification */
// PushNotification - Handle launch from a push notification
NSDictionary* userInfo = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
if(userInfo) {
    PushNotification *pushHandler = [self.viewController getCommandInstance:@"PushNotification"];
    NSMutableDictionary* mutableUserInfo = [userInfo mutableCopy];
    [mutableUserInfo setValue:@"1" forKey:@"applicationLaunchNotification"];
    [mutableUserInfo setValue:@"0" forKey:@"applicationStateActive"];
    [pushHandler.pendingNotifications addObject:mutableUserInfo];
}
/* end code block */

PushNotification plugin – JavaScript APIs
 
Now that you have the project setup complete including the native Objective-C handlers above, you can actually start coding with the Cordova PushNotification JavaScript interfaces. Below is further detail about some of them and examples of interacting with them in your code. There are others that are available that are not covered specifically in this article.
 
Register device
 
The PhoneGap PushNotification plugin offers a registerDevice() API to register your application with Apple’s Push Notification Service to receive push notifications. In the function, you specify exactly which types of notifications are enabled (alerts/badges/sounds). The result is a unique device token that the server-side can use to send the notification to that device. Apple recommends that you register your application for push notifications on the device every time it runs, since tokens can change.
 
An example of using the registerDevice() function is included in the sample project that you can download at the beginning of this article in the Requirement section. I show the code below:

var pushNotification = window.plugins.pushNotification;
pushNotification.registerDevice({alert:true, badge:true, sound:true}, function(status) {
    app.myLog.value+=JSON.stringify(['registerDevice status: ', status])+"\n";
    app.storeToken(status.deviceToken);
});
 
Once the above code is in place and your application runs, you will receive an alert shown in Figure 4 asking the user whether he or she would like to receive push notifications (clicking “OK” or “Don’t Allow” results in a setting that can be modified in the user’s device settings as needed).
 
Prompt asking permission to send push notifications
Figure 4. Prompt asking permission to send push notifications
 
Get pending notifications
 
Pending notifications are those notifications that are received while the application was not active. When the application launches, retrieve them so that you can handle the data and send it as needed. The getPendingNotifications() function is available in the API for that purpose. See the example code below:

var pushNotification = window.plugins.pushNotification;
pushNotification.getPendingNotifications(function(notifications) {
    app.myLog.value+=JSON.stringify(['getPendingNotifications', notifications])+"\n";
    console.log(JSON.stringify(['getPendingNotifications', notifications]));
});
 
Get notification status
 
This method performs a registration check and returns those notifications that are enabled (alert, sound, badge).

var pushNotification = window.plugins.pushNotification;
pushNotification.getRemoteNotificationStatus(function(status) {
    app.myLog.value+=JSON.stringify(['Registration check -getRemoteNotificationStatus', status])+"\n";
});
 
Set badge number
 
The badge number indicates pending notifications for a particular application through a number displayed in the upper-right-hand corner of the application icon. You will likely need to set the badge number on the application icon as you process the notifications. For instance if you open from a push notification you just received, you may want to decrease or clear it. Setting the badge number to zero will remove or clear the badge as shown below.
 
var pushNotification = window.plugins.pushNotification;
app.myLog.value+="Clear badge... \n";
pushNotification.setApplicationIconBadgeNumber(num);
 

Anatomy of an Apple Push Notification
 
The maximum size for a notification payload is 256 bytes. If that limit is exceeded, it will be refused. Also note the delivery of notifications is “best effort” and not guaranteed, according to the Apple documentation, so you should not use it for sending critical or sensitive data, only to notify whether new data is available.
The notification payload is a JSON dictionary object that needs to contain another dictionary identified by the key aps . Then, the aps dictionary will contain one or more properties that specifies an alert to display, a badge number to set on the application icon, and/or a sound to play when the notification occurs. It’s also possible to create a custom payload but that is beyond the scope of this article. The alert object itself can contain just a string of text to display or a dictionary object with keys for a body, custom action button text to display and a custom launch image that you can set. See specific details about the payload at the Apple Push Notification Service documentation.
 
Here’s an example of a simple payload in bytes:
 
Anatomy of Apple Push Notifications.
Figure 5. Anatomy of Apple Push Notifications.
 
A typical aps dictionary object contains three properties: alert, badge and sound. For example, let’s look at the following output:

applicationLaunchNotification = 0;
applicationStateActive = 0;
aps = {
alert = {
“action-loc-key” = Play;
body = “Your turn!”;
“launch-image” = “mysplash.png”;
};
badge = 5;
sound = “notification-beep.wav”;
};
messageFrom = Holly;
timestamp = “1350416054.782263″;

Any custom sounds or launch images must be contained in the Resources folder in your project within XCode. For example in my image below, I have a notification-beep.wav and mysplash.png specified that I refer to in my push notification coming from the server.

Specifying custom sounds and images.
Figure 6. Specifying custom sounds and images.
 

Server-side code handling
 
Below is example code that you could use to get started with a server-side solution to handle device token registration, which I currently call from my application sample to show how and at which point you might want to communicate with a third-party server to store device tokens after receiving the device token from Apple. Currently it doesn’t do anything with the token other than show that it and the message were received. The next step would be to store the users’s device token and any other information in a database for later use.
 
var http = require('http');    
var apn = require('apn');
var qs = require('querystring');
 
var server = http.createServer(function (req, res) {
    if(req.method === "POST") {
        var fullBody="";
     
        req.on('data', function(chunk)  
        {
            fullBody += chunk;
            console.log("Full body " + fullBody);
        });
 
        req.on('end', function()
        {
            var data = qs.parse(fullBody);
            console.log("Token " +data.token);
            console.log("Message " + data.message);
            var myDevice = new apn.Device(data.token);
            // Now we need to store it! Add code to interface with a db
below...
 
            res.writeHead(200, {"Content-Type": "text/plain"});
            res.end("Thank you for registering\n");
            res.end();
        });
    }
}).listen(8888);
console.log("Server running at http://127.0.0.1:"+server.address().port);
 
I simply run this script using Node.js on my localhost. When testing from your actual device (since push notifications are not supported in the emulator), you can set up a Manual HTTP Proxy in your device’s Wi-Fi settings to point to the IP Address of your computer. Go to your Wi-Fi network and scroll to the bottom of the settings to set the Manual Proxy server and port. Here’s an example of mine:
 
Testing on your device.
Figure 7. Testing on your device.
 

Sending a notification with Argon (Node.js API)
 
Below is an example of some simple code to show how you could use the argon open source Node.js API to send a push notification to a user’s device. I simply hard-coded my device tokens in for quick testing, but ultimately you would be retrieving them from a database to use for sending the push notifications.
 
var http = require('http');
var apn = require('apn');
var url = require('url');
 
var myPhone = "d2d8d2a652148a5cea89d827d23eee0d34447722a2e7defe72fe19d733697fb0";
var myiPad = "51798aaef34f439bbb57d6e668c5c5a780049dae840a0a3626453cd4922bc7ac";
var myDevice = new apn.Device(myPhone);
var note = new apn.Notification();
note.badge = 1;
note.sound = "notification-beep.wav";
note.alert = { "body" : "Your turn!", "action-loc-key" : "Play" , "launch-image" : "mysplash.png"};
note.payload = {'messageFrom': 'Holly'};
note.device = myDevice;
var callback = function(errorNum, notification){
    console.log('Error is: %s', errorNum);
    console.log("Note " + notification);
}
var options = {
    gateway: 'gateway.sandbox.push.apple.com', // this URL is different for Apple's Production Servers and changes when you go to production
    errorCallback: callback,
    cert: 'PushNotificationSampleCert.pem',                 
    key:  'PushNotificationSampleKey.pem',                 
    passphrase: 'myPassword',                 
    port: 2195,                       
    enhanced: true,                   
    cacheLength: 100                  
}
var apnsConnection = new apn.Connection(options);
apnsConnection.sendNotification(note);
 
The above code produces a notification that looks like the following on a device, assuming that you have your device Settings > Notifications > MyAppName set to the Alerts option:
 
Alert notification on your device.
Figure 8. Alert notification on your device.
 
If you’re device settings for Alert Style on the application is set to Banners, it will look like this:
 
Banner notification on your device.
Figure 9. Banner notification on your device.
 
Important note: You must change the PEM files to your cert and private key created in the setup (the ones you ultimately combined into a single PEM file with instructions in the SSL certificates and provisioning section. Remember you first converted the CER to a PEM, and then your .p12 to a PEM. Those are the two files that you need here. See the argon readme for more details on argon parameters). In my example, my PEM files are in the same folder as my Node.js code. You can omit the password property if you did not keep a password for them.

Where to go from here
 
I’ve included a link to a sample project on GitHub that I created for a reference application since it includes everything discussed above. I don’t recommend actually using this project out of the box because you have to have your own unique App ID and Provisioning files set on your project. You are better off starting your own project from scratch and using the sample for reference only. I created this project to eventually be cross-platform using the cordova-client tool, as described in my article, Cross-Platform PhoneGap (aka Cordova) Project Templates in a Jiffy!, but currently only iOS is setup. Therefore, refer to the PGPushNotificationSample/platforms/ios path for the iOS specific code. You’ll find all of the JavaScript functions in the PGPushNotificationSample/platforms/ios/www/js/index.js file.
 
Creative Commons License