Win $20,000. Help build the future of education. Answer the call. Learn more

Archived | Sending push notifications to iOS devices

Archived content

Archive date: 2019-08-29

This content is no longer being updated or maintained. The content is provided “as is.” Given the rapid evolution of technology, some content, steps, or illustrations may have changed.


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. 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.

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.

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 Related topics) 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 | 

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] 
    [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 Related topics 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 Related topics). 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.p12keypair.p12 is the p12 file exported from KeyChain Access, and passwordpassword 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 ="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.


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.