Sending push notifications to iOS devices

Deliver rich content directly to iOS apps

Using push notification technology, server applications can send rich content to apps installed on iOS or Android devices, saving you the trouble and expense of implementing Short Message Service (SMS) or Multimedia Messaging Service (MMS) in your mobile-messaging apps. Michael Yuan shows how to support this key feature in your iOS applications. You'll also see how to use an open source Java library that simplifies the notification process.

Share:

Michael J. Yuan, Chief Scientist, Ringful Health

Photo of Michael YuanDr. Michael Yuan is a well-known technologist in the area of enterprise computing and consumer mobile technologies. He is the author of 5 books on software engineering and has published more than 40 articles in both peer-reviewed and industry journals. Dr. Yuan is a pioneer in the emerging field of consumer-driven healthcare. His work at Ringful Health has been widely covered and recognized in national media such as the Wall Street Journal, New York Times, and Los Angeles Times.



14 February 2012

Also available in Chinese Russian Vietnamese Portuguese Spanish

Background

SMS and MMS messages are delivered by wireless carriers to specific devices via their phone numbers. Developers of server-side applications that implement SMS/MMS messaging must go to great lengths to interact with the existing, closed telecommunications infrastructure—including obtaining phone numbers. (Read more details on developing server-side SMS/MMS applications in "Build mobile messaging into your web apps.") SMS/MMS messages have no awareness of the applications installed on the recipient's device.

The popularity of super-smartphones like iPhone and Android devices (and their Wi-Fi cousins, such as the iPod Touch and iPad) has created a need for a modern mobile-messaging infrastructure that can deliver rich-media information directly to installed apps—rather than to phone numbers—and that is more open and less expensive than SMS/MMS.

Two-way messages

One of the great features of the SMS/MMS infrastructure is that server applications can receive and respond to messages sent from phones. Push messaging, in contrast, is one-way: you can send messages only from your server to the apps on a device. But that's not a major issue. Rather than having users "text XYZ to 12345 to receive information," you can tell them to "download app XYZ to receive information." And apps themselves can upload content to your server directly when needed, eliminating any need to message the server.

Apple and Google have both stepped up to the challenge by developing Internet-based infrastructure that can deliver messages to iOS and Android devices, respectively, from server applications. Push notifications are designed from the ground up to communicate with applications installed on mobile devices. They can send text, media files, and app-specific data such as alert sounds and badges to display on the app icon.

This article explains how push notification technology works on the iOS platform and how to incorporate it in your applications. To get the most from the article, you should have some iOS and Java development experience.


iOS push basics

Apple uses public-key digital certificates to authenticate push requests from your iOS application, so you first need to create authentication keys and register them with Apple. I'll cover this straightforward but rather lengthy process in the next section.

Next, each device that installs the application and opts to receive push notifications for that app needs to be identified. The sequence works like this:

  1. An alert dialog in the iOS app requests the user's permission to receive push notifications.
  2. If the user grants permission, the iOS app contacts the Apple Push Notification service (APNs) to get an ID string that uniquely identifies this application installed on this device. (You can think of the ID as analogous to the recipient's phone number in a traditional messaging scenario.)
  3. The iOS app uploads the ID to your server application.
  4. When the server application needs to send a push message, it authenticates against Apple's push servers and then uses the ID from steps 2 and 3 to specify the message's recipient.
  5. If the recipient device is online, it receives and processes the message. If the device is offline, the message is queued and then delivered the next time the device is online.

An alternative to your own server: Urban Airship

Urban Airship is a service that enables you to push messages without needing to run your own server (see Resources). You upload your private keys to Urban Airship and use its web-based messaging console to push messages to your users. Urban Airship provides an iOS SDK that makes it easy for your application to register each installed device with its server so that those devices appear in the messaging console. Although Urban Airship simplifies the work for you, it can be expensive—and you still need to understand basic push notification concepts.

The APNs also enables your server applications to validate your stored list of application IDs periodically. That gives you an opportunity to remove the IDs of users who later delete the application or change their push opt-in status.

This probably sounds like a lot of work, and it is. That's why services like Urban Airship (see the sidebar) exist to broker messages for a fee.

After showing you how to register your application, I'll go into the details of developing an iOS push notification app and, with the help of an open source Java library, its server components.


Registering the application

To register your application for push notification, you first need to create a pair of private/public keys for authenticating your API calls against the APNs servers. You can do this on the Mac via the KeyChain Access application. Select KeyChain Access > Certificate Assistant > Request a Certificate from a Certificate Authority to create a certificate signing-request file. The request file contains the generated public-key pair, and the corresponding private key is saved in KeyChain Access. Be sure to select the Saved to disk option in the dialog box, as shown in Figure 1:

Figure 1. Generating a key pair and signing request from the KeyChain Access program on the Mac
Screenshot of generating a key pair and signing request from KeyChain Access program on Mac

Next, log in to Apple's application-provisioning portal (see Resources) and upload your signing-request file, which will be associated with the appropriate provisioning profile. Most applications have a development provisioning profile for testers and a production profile for the App Store, so you will most likely generate and upload two signing requests. After you upload the signing request, the portal generates a digital certificate for you to download. The certificate contains the public key, which APNs now recognizes as being associated with your application. Figure 2 shows an example:

Figure 2. Digital certificate from Apple
Screenshot of digital certificate from Apple

Download the digital certificate and double-click the downloaded file. KeyChain Access now automatically imports the digital certificate and associates it with the private key you generated when you created the signing request. Figure 3 shows a public and private key pair in KeyChain Access:

Figure 3. Public and private key pair in KeyChain Access
Screenshot of public and private key pair in KeyChain Access

Now, you export the key pair to a file using a format called Personal Information Exchange (p12). When you create the p12 file, KeyChain Access asks you to assign a password to protect the private key. You can use an empty password if you want.

From this point on, all your API requests to the APNs push server must be encrypted by the private key in the p12 file, and then digitally signed by the public key in the p12 file, to verify that the API call is really generated by you. I'll demonstrate how to use the keys later in the article, when I describe how to interact with APNs servers. (If you use Urban Airship, it asks you to upload the p12 file along with any password to its server so that it can send push messages on your behalf.)

Now that you have the push certificates, you must re-download and re-install your application-provisioning profiles—because the profiles are now updated to support push notification for the application.


Requesting and saving a device token

Your iOS application needs to request user permission to receive push notifications on the devices it's installed on. Typically, you do this in the application delegate via a simple API call, as shown in Listing 1:

Listing 1. Requesting user permission
[[UIApplication sharedApplication]
    registerForRemoteNotificationTypes:
        (UIRemoteNotificationTypeBadge |
         UIRemoteNotificationTypeSound | 
         UIRemoteNotificationTypeAlert)];

If the user grants permission, the application automatically contacts the APNs server for a device token. The token enables APNs to identify this particular application installed on this particular device as a message destination. This process is automatic and happens in the background. You don't need to write any code for it.

After the APNs server responds, the didRegisterForRemoteNotificationsWithDeviceToken method in the application delegate is called, with the device token passed in as a call parameter. You must save the device token and upload it to your own push notification server, as shown in Listing 2:

Listing 2. Receiving an ID and uploading it to the server
- (void)application:(UIApplication*)application
         didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken {

    NSString *tokenStr = [deviceToken description];
    NSString *pushToken = [[[[tokenStr 
      stringByReplacingOccurrencesOfString:@"<" withString:@""] 
      stringByReplacingOccurrencesOfString:@">" withString:@""] 
      stringByReplacingOccurrencesOfString:@" " withString:@""] retain];

   // Save the token to server

   NSString *urlStr = [NSString stringWithFormat:@"https://%@/push_token", RINGFULDOMAIN];
    NSURL *url = [NSURL URLWithString:urlStr];
    NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:url];
        
   [req setHTTPMethod:@"POST"];
   [req setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-type"];
   NSMutableData *postBody = [NSMutableData data];
   [postBody appendData:[[NSString stringWithFormat:@"username=%@", username] 
      dataUsingEncoding:NSUTF8StringEncoding]];
    [postBody appendData:[[NSString stringWithFormat:@"&token=%@", 
      pushToken] dataUsingEncoding:NSUTF8StringEncoding]];

   [req setHTTPBody:postBody];
   [[NSURLConnection alloc] initWithRequest:req delegate:nil];
}

Ideally, you associate the token with some information that identifies the user (such as that person's username in your system) so that your server knows who to send the message to later. (You can think of this as analogous to associating a phone number with a person's name.) If you don't associate tokens with your own user-identification information, you can still send messages to those devices, but you can't customize the message for each user, because all you have is an alphanumeric token string for the destination device.

The server should save the token and its related identification information in a database. In most applications, it's saved in the user-profile database.


Sending a push message

To send a push message, your server:

  1. Looks up a list of application IDs to send the message to
  2. Personalizes the message for each recipient based on that recipient's user profile
  3. Contacts the APNs messaging server

The web services API for the APNs server is complicated. Fortunately for Java developers, the open source JavaPNS library makes working with it much simpler. See Resources for links to the JavaPNS download and documentation.

The code in Listing 3 shows how to send an SMS-like message to the device using the JavaPNS library:

Listing 3. Sending a push message
String[] devices = {"token1", "token2};
List<PushedNotification> notifications
 = Push.alert("Hello World!", "keypair.p12", "password", false, devices);

The JavaPNS library's main interface methods are static methods in the Push class. APNs allows you to embed a variety of content in your message. Refer to the iOS push messaging guide for a complete list of supported payload types (see Resources). The Push class provides convenience methods for each type of message, and it handles conversion of the message to the JavaScript Object Notation (JSON) format accepted by the APNs servers. In Listing 3, keypair.p12 is the p12 file exported from KeyChain Access, and password is the password to the p12 file. The devices array is a list of device tokens received from the iOS application. All those devices will receive this push message. The false value in the parameter designates that the message should be sent to the APNs development server (the sandbox) instead of its production server. (Recall that you typically create one p12 key pair for the sandbox and a different one for the production server.)

The return value of the method call is a list of PushedNotification objects that you can use to ascertain the status of the push delivery, as shown in Listing 4:

Listing 4. Checking the status of the push delivery
for (PushedNotification notification : notifications) {
    if (notification.isSuccessful()) {
        /* Apple accepted the notification and should deliver it */
    } else {
        String invalidToken = notification.getDevice().getToken();
        /* Add code here to remove invalidToken from your database */
    }
}

If the notification object tells you that a certain device token is no longer active—for example, if the user removed the app from the device or disabled notification in the app settings—you should remove the token from the database so that you do not send more messages to it.

Another way to keep your list of active device tokens up to date is to have your server application check in periodically with the APNs servers. Listing 5 shows how to query the APNs feedback service using JavaPNS to receive a list of invalid device tokens from the APNs sandbox:

Listing 5. Checking in to update active device tokens
List<Device> inactiveDevices = Push.feedback("keypair.p12", "password", false);
/* remove inactive devices from your own list of devices */

It's important not to waste resources by sending messages to devices that have deleted your application or opted out of receiving notifications.


Additional considerations

Push notifications can't be tested on iOS emulators; you must deploy the application to an actual device to test it. Because the digital certificate used to authenticate the message is tied to the application's provisioning profile, you need to test with the development certificate when you are in development or distributing the app ad hoc. You must switch to the production certificate after the app is approved for and available in the App Store.

Also, it's important to understand that customizing and sending push messages for a large database of users is resource-intensive work. For instance, it's costly to crawl through a million-user database every 5 seconds to identify the 10 users who need to receive a message at that moment. The server-side infrastructure requires careful design and planning to support frequent push notifications to a large number of users. Conversely, sending a push message to a million users at once creates a lot of traffic that could be better handled via a pool of threads, as opposed to blocking a single thread. The JavaPNS library provides an easy API that uses thread pools for pushing messages to a large number of devices simultaneously.


Conclusion

Push technology lets your server applications bypass the telco carriers and send messages directly to iOS device apps over the Internet. Although implementing push notification isn't trivial—the need for client-side SSL certificates to authenticate against Apple servers is complicated—help from third parties like Urban Airship and JavaPNS can make it easier to send notifications. SMS and MMS have their place, and are still more reliable than push technology, but you can make your iOS applications richer and more versatile by implementing push messaging.

Resources

Learn

Get products and technologies

  • JavaPNS: Download the JavaPNS JAR.
  • Evaluate IBM products in the way that suits you best: Download a product trial, try a product online, use a product in a cloud environment, or spend a few hours in the SOA Sandbox learning how to implement Service Oriented Architecture efficiently.

Discuss

  • Get involved in the My developerWorks community. Connect with other developerWorks users while exploring the developer-driven blogs, forums, groups, and wikis.

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into Java technology on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Java technology, Mobile development
ArticleID=793616
ArticleTitle=Sending push notifications to iOS devices
publish-date=02142012