Premium Hosted Website & Server Monitoring Tool.

(Sysadmin / Devops blog)

visit our website

Blog   >   API   >   How to build an Apple Push Notification provider server (tutorial)

How to build an Apple Push Notification provider server (tutorial)

how-to-build-an-apple-push-notification-provider-server-tutorial

See also: How to renew your Apple Push Notification Push SSL Certificate

One of the widely anticipated features of the new iPhone OS 3.0 is push notifications which allow messages to be sent directly to an individual device relevant to the application that has been installed. Apple have demoed this as useful for news alerts, or IM notifications however it fits in perfectly with the nature of our server monitoring service, Server Density.

Server monitoring iPhone application alert view

As part of the product, we have an iPhone application that includes push notifications as an alerting option so you can be notified via push direct to your iPhone when one of your server alerts have been triggered. This is useful since our app can then be launched to instantly see the details of the server that has caused the alert.

Apple provides detailed code documentation for the iPhone OS code that is needed to implement and handle the alerts on the device but only provides a higher level guide for the provider server side.

As a provider, you need to communicate with the Apple Push Notification Service (APNS) to send the messages that are then pushed to the phone. This is necessary so that the device only needs to maintain 1 connection to the APNS, helping to reduce battery usage.

This tutorial will go into code-level detail about how we built our push notification provider server to allow us to interact with the APNS and use the push notifications with our server monitoring iPhone application. Since we develop in PHP, our examples will be in PHP 5.

Basic Structure

  1. You connect to the APNS using your unique SSL certificate
  2. Cycle through the messages you want to send (or just send 1 if you only have 1)
  3. Construct the payload for each message
  4. Disconnect from APNS

The flow of remote-notification data is one-way. The provider composes a notification package that includes the device token for a client application and the payload. The provider sends the notification to APNs which in turn pushes the notification to the device.

- Apple documentation

APNS Flow

Restrictions

  • The payload is limited to 256 bytes in total – this includes both the actual body message and all of the optional and additional attributes you might wish to send. Push notifications are not designed for large data transfer, only for small alerts. For example we only send a short alert message detailing the server monitoring alert triggered.
  • APNS does not provide any status feedback as to whether your message was successfully delivered. One reason for this is that messages are queued to be sent to the device if it is unreachable, however only the last sent message will be queued – overwriting any previously sent but undelivered messages.
  • Push notifications should not be used for critical alerts because the message will only be delivered if the device has wifi or cellular connectivity, which is why we recommend combining push with another alerting method such as e-mail or SMS for our server monitoring alerts.
  • The SSL certificates used to communicate with APNS, discussed below, are generated on an application level. The implementation discussed in this tutorial only concerns a single iPhone application so if you have several, you will need to adapt the code to use the appropriate certificate(s) where necessary.

Device Token

Each push message must be “addressed” to a specific device. This is achieved by using a unique deviceToken generated by APNS within your iPhone application. Once this token has been retrieved, you need to store it on your server, not within your iPhone application itself. It looks something like this:

c9d4c07c fbbc26d6 ef87a44d 53e16983 1096a5d5 fd825475 56659ddd f715defc

For the Server Density iPhone application, we call the necessary generation methods on app launch and pass it back to our servers via an HTTP API call. This stores the deviceToken in a database on our servers for that user so we can then communicate with the device linked to that user.

Feedback Service

Apple provide a feedback service which you are supposed to occasionally poll. This will provide a list of deviceTokens that were previously but are no longer valid, such as if the user has uninstalled your iPhone application. You can then remove the deviceToken from your database so you do not communicate with an invalid device.

Using the feedback service is not covered by this tutorial.

Certificates

The first thing you need is your Push certificates. These identify you when communicating with APNS over SSL.

Generating the Apple Push Notification SSL certificate on Mac:

  1. Log in to the iPhone Developer Connection Portal and click App IDs
  2. Ensure you have created an App ID without a wildcard. Wildcard IDs cannot use the push notification service. For example, our iPhone application ID looks something like AB123346CD.com.serverdensity.iphone
  3. Click Configure next to your App ID and then click the button to generate a Push Notification certificate. A wizard will appear guiding you through the steps to generate a signing authority and then upload it to the portal, then download the newly generated certificate. This step is also covered in the Apple documentation.
  4. Import your aps_developer_identity.cer into your Keychain by double clicking the .cer file.
  5. Launch Keychain Assistant from your local Mac and from the login keychain, filter by the Certificates category. You will see an expandable option called “Apple Development Push Services”
  6. Expand this option then right click on “Apple Development Push Services” > Export “Apple Development Push Services ID123″. Save this as apns-dev-cert.p12 file somewhere you can access it.
  7. Do the same again for the “Private Key” that was revealed when you expanded “Apple Development Push Services” ensuring you save it as apns-dev-key.p12 file.
  8. These files now need to be converted to the PEM format by executing this command from the terminal:
    openssl pkcs12 -clcerts -nokeys -out apns-dev-cert.pem -in apns-dev-cert.p12
    openssl pkcs12 -nocerts -out apns-dev-key.pem -in apns-dev-key.p12
  9. If you wish to remove the passphrase, either do not set one when exporting/converting or execute:
    openssl rsa -in apns-dev-key.pem -out apns-dev-key-noenc.pem
  10. Finally, you need to combine the key and cert files into a apns-dev.pem file we will use when connecting to APNS:
    cat apns-dev-cert.pem apns-dev-key-noenc.pem > apns-dev.pem

It is a good idea to keep the files and give them descriptive names should you need to use them at a later date. The same process above applies when generating the production certificate.

Payload Contents

The payload is formatted in JSON, compliant with the RFC 4627 standard. It consists of several parts:

  • Alert – the text string to display on the device
  • Badge – the integer number to display as a badge by the application icon on the device home screen
  • Sound – the text string of the name of the sound to accompany the display of the message on the device
  • This tutorial will only deal with the basics by sending a simple alert text string but this can also be another dictionary containing various options to display custom buttons and the like.

Creating the payload

Using PHP it is very easy to create the payload based on an array and convert it to JSON:

$payload['aps'] = array('alert' => 'This is the alert text', 'badge' => 1, 'sound' => 'default');
$payload = json_encode($payload);

 
Echoing the contents of $payload would show you the JSON string that can be sent to APNS:

{
     "aps" : { "alert" : "This is the alert text", "badge" : 1, "sound" : "default" }
}

 
This will cause a message to be displayed on the device, trigger the default alert sound and place a “1″ in the badge by the application icon. The default buttons “Close” and “View” would also appear on the alert that pops up.

For the Server Density server monitoring iPhone application, it is important for the user to be able to tap “View” and go directly to the server that generated the alert. To do this, we add an extra dictionary in of our own custom values:

$payload['aps'] = array('alert' => 'This is the alert text', 'badge' => 1, 'sound' => 'default');
$payload['server'] = array('serverId' => $serverId, 'name' => $name);
$output = json_encode($payload);

 
The custom dictionary server is passed to the application on the device when the user taps “View” so we can load the right server. The JSON looks like this:

{
     "aps" : { "alert" : "This is the alert text", "badge" : 1, "sound" : "default" },
     "server" : { "serverId" : 1, "name" : "Server name")
}

 
The size limit of 256 bytes applies to this entire payload, including any custom dictionaries.

The raw interface

Once an alert is generated within Server Density, the payload is built and then inserted into a queue. This is processed separately so that we can send multiple payloads in one go if necessary.

Apple recommends this method because if you are constantly connecting and disconnecting to send each payload, APNS may block your IP.

As described by Apple:

The raw interface employs a raw socket, has binary content, is streaming in nature, and has zero acknowledgment responses.

APNS Binary Format

Opening the connection

The PHP 5 code to open the connection looks like this:

$apnsHost = 'gateway.sandbox.push.apple.com';
$apnsPort = 2195;
$apnsCert = 'apns-dev.pem';

$streamContext = stream_context_create();
stream_context_set_option($streamContext, 'ssl', 'local_cert', $apnsCert);

$apns = stream_socket_client('ssl://' . $apnsHost . ':' . $apnsPort, $error, $errorString, 2, STREAM_CLIENT_CONNECT, $streamContext);

 
If an error has occurred you can pick up the error message from $errorString. This will also contain the details if your SSL certificate is not correct.

The certificate file is read in relative to the current working directory of the executing PHP script, so specify the full absolute path to your certificate if necessary.

Note that when testing you must use the sandbox with the development certificates. The production hostname is gateway.push.apple.com and must use the separate and different production certificate.

Sending the payload

At this point, the code we use loops through all the queued payloads and sends them. Constructing the binary content to send to APNS is simple:

$apnsMessage = chr(0) . chr(0) . chr(32) . pack('H*', str_replace(' ', '', $deviceToken)) . chr(0) . chr(strlen($payload)) . $payload;
fwrite($apns, $apnsMessage);

 
Note that the $deviceToken is included from our database and stripped of the spaces it is provided with by default. We also include a check to send an error to us in the event that the $payload is over 256 bytes.

$apnsMessage contains the correctly binary formatted payload and the fwrite call writes the payload to the currently active streaming connection we opened previously, contained in $apns.

Once completed, you can close the connection:

socket_close($apns);
fclose($apns);

php-apns

There is a free, open source server library that does all the above functionality called php-apns. We chose to implement it ourselves because it has a further dependancy on memcached, we do not want to rely on 3rd party code for large and critical aspects of our code-base and I am apprehensive about the suitability of PHP for running a continuous server process. We do all the above queue processing using our own custom cron system which runs every few seconds – that way PHP scripts do not need to be run as processes, something I’m not sure they were designed to do!

All done

That’s it! If you have any problems, post in the comments below and we’ll do our best to help out. Also, Stack Overflow is your friend.

  • Dhaval

    Hi,
    I am facing problem in finding device token.
    I have used the Method “registerForRemoteNotificationWithTypes” for getting the device Token.
    But,It’s not returning it.
    Pls,help me.

    Thanks,
    Dhaval

    • http://www.serverdensity.com David M

      The method does not return anything. As per the documentation:

      “An application must register with Apple Push Notification Service for iPhone OS to receive remote notifications sent by the application’s provider. Registration has three stages:

      1. The application calls the registerForRemoteNotificationTypes: method of UIApplication.
      2. It implements the application:didRegisterForRemoteNotificationsWithDeviceToken: method of UIApplicationDelegate to receive the device token.
      3. It passes the device token to its provider as a non-object, binary value.”

      See http://developer.apple.com/iphone/library/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/IPhoneOSClientImp/IPhoneOSClientImp.html

      Please note this tutorial is for the server side, not the iPhone OS implementation so I can’t really help much.

  • Pingback: links for 2009-07-11 « Blarney Fellow

  • Dhaval

    Hey,

    Will above code (apns.php) work on the local web server??
    Is it necessary to put it on the Windows server?? or MAC server?
    Please,help me.

    Thanks,
    Dhaval

    • http://www.serverdensity.com David M

      It will work on any PHP 5 server, although the certificate generation instructions are written for Mac OS X 10.5.

      • Dhaval

        But, on which platform??
        I found that the Socket function used in the code is from Unix Not from Windows.

        So,how can we run this code from windows platform??
        Awaiting for reply.

        Thanks,
        Dhaval

      • http://www.serverdensity.com David M

        As I said, the code will work on any server. The PHP socket functions are platform independent.

  • Muezzin

    I have a problem sending multiple notifications using a single connection to the gateway. When I write them to the opened socket one after the other only the first one is received but all of the others are dropped. However, when I create a connection for each of them they are all received. Any idea why that might have happened?

    • http://www.serverdensity.com David M

      I suggest you post on the Apple iPhone forums at http://developer.apple.com as Apple staff will be able to check what is going on.

  • http://venikom.com Venikom

    Hi. Wee make Development Push SSL Certificate and everything work ok. We upload binnary to iTunes connect and se thet the ssl://gateway.sandbox.push.apple.com:2195 is for testing purpose.

    We make now Production Push SSL Certificate and change the php code to ssl://gateway.sandbox.apple.com:2195 and our pusn now is not working. Do we must make changes in our app??? I don’t know what is the problem now… Can someone help us?

    • http://www.serverdensity.com David M

      The production certificate will only allow you to push notifications to applications built using your production certificate i.e. ad-hoc or app store builds.

    • http://kinetics.magnatron.nl Maarten Wolzak

      @Venikom
      I think you left out ‘push’ instead of leaving out ‘sandbox’.
      It seems you should be using:

      ssl://gateway.push.apple.com:2195

      As David states in his tutorial.

      You try to use:

      ssl://gateway.sandbox.apple.com:2195

  • http://kinetics.magnatron.nl Maarten Wolzak

    Hi David,

    Great in depth tutorial!

    Thanks for the comprehensive explanation of the serverside of apns.

  • Juan

    David -

    Fabulous tutorial. Hardly anyone out there has put together such a simple, yet encompassing tutorial. I do have one question though.

    I know you’ve said any server with PHP5 capablities works just fine, but could you recommend a few to me, please? I am developing a game that will use the push-notifications to tell the person that an event has happened in the game. They will then connect to the server and download XML data with all the relevant new data for the game.

    Can you think of a server that can not only send push notifications, but also send/receive XML data and process it?

    I really appreciate the help!

    Juan

    • http://www.serverdensity.com David M

      It’s not really a case of “recommending a server”. Once you purchase a server, either a physical machine that you rent or a virtual server (Amazon EC2, Slicehost, etc) then you install the appropriate services on it. One of those might be PHP.

    • http://pemungkah.com Joe McMahon

      Juan, you’re probably going to want to handle this by sending a notification to your app that, well, notifies it that it needs to fetch data. Think of this as very like NSNotificationCenter: you get an ID from the device that says the device is eligible to receive the notification. You then use the notification service to send just enough data to the phone via the service to let it know it should access another URL to fetch the XML. This is an easy-enough thing to do, so when your app runs you get the effect that you sent it the XML – though there’s one more turn-around: your server to the notification service to the phone, back to your server and then to your app. As an extra advantage, if your data needs to be send dependably, you’re using HTTP and can be sure of the status.

      If the notification fails, then your notification-using service can check to see if the XML payload got picked up in some reasonable amount of time and resend the notification.

      If you’re running the notification code and the XML code in the same server, it should be trivial for them to share enough state for this to work. It’s always easier to use the tools as expected instead of trying to force them to do something outside their design spec.

      • http://www.andersontaylor.com Bryan

        Joe, Have you tested this method? Will this definately allow the application to confirm with your server that it has received notification so we can re-send if it has not?

  • Pingback: This Week in iPhone News - August 7/2009

  • http://www.johntwang.com John Wang

    This is an amazing write up. I’ve been trying to figure most of this out and Apple’s Programming Guide is actually pretty useful, though a tad bit information overload for the beginners.

    For those who are looking to implement APNS with Ruby on Rails instead of PHP, there’s a great resource on GitHub for it.
    http://github.com/markbates/apn_on_rails/tree/master

  • Pingback: Apple Push Notification Service Gateways | NSCoriolisBlog

  • Curtis Fued

    I am not sure apple is going to be a fan of developers pounding their apns service ssl connection setup/teardown once every few *seconds*. This is why you should use a more polite persistent server design.

    • Robert Simpson

      Yes, I’ve been trying to figure a way to avoid this problem with the code given above. Obviously the apns-php code is able to get round this problem using memcache but I would like to keep things simple for myself. So, how would I go about keeping a persistent connection in php? I have a feeling that crontabs would have a role to play in this.

      Rob

      • http://www.serverdensity.com David M

        PHP is not really designed for long running scripts like keeping a persistent connection open. If this is what you need then something more suited to socket programming would be more appropriate.

      • jsd

        there’s nothing wrong with using PHP for long running scripts. i have had a PHP script with a single connection open to apple’s servers for days at a time. i eventually changed the design of the script due to a bug in apple’s server.

        here’s the outline of what i do now:

        i use daemontools to keep the php script running. (daemontools automatically restarts the script if it crashes or exits).

        my script has an infinite loop that checks the database for new notifications to send. if it finds them, it checks to see if it has already connected to apple. if it hasn’t, it initiates the connection and sends the message. if it’s already connected, it just sends. after the batch is sent, the script sleeps for a few seconds before checking the database again.

        once the script has run for a few hours, it exits. daemontools restarts the script, i make a new connection to apple, voila. no problems with this in production for several months.

  • toni

    Thanks so much for this wonderful tutorial. I’ve been searching for this like crazy. I am not well versed in php and was wondering if you could package up this tutorial so I could understand and implement it effectively? If so, I guess you can just shoot it by email. Thank you very much.

  • john

    Dave

    I can only use Perl CGI script on the server I use. Do you know of any example APNS code using Perl??

    • http://www.serverdensity.com David M

      Sorry, I don’t know Perl.

  • Pingback: Push Notification Server Tutorial | iPhone Development Tutorials and Tips

  • Pingback: links for 2009-08-11 | Alones world

  • Abhishek

    hi david m thats a grate tutorial its really works but how we can send message like if time is matched to given time then massege should be send to apns and apns to user’s device

    • http://www.serverdensity.com David M

      You would need to implement this yourself.

  • pramod

    Hi,

    Is it possible to install the .pem certificate (SSL Certificate), on the non-mac os server. if so ,how it can be done.

    • http://www.serverdensity.com David M

      “Installing” the .pem is just copying the file. It can therefore go on any server OS. We run our code on Linux for example.

      • pramod

        Hi David,

        I would like to know where to copy the .pem file. Is it to be copied in the folder where php file lies.

        Thank u

      • http://www.serverdensity.com David M

        Put it where-ever you like. You reference the path in the code.

  • Ben

    Would this work if deploying to multiple shared web hosts? Example, we develop a PHP script, with a corresponding iPhone app we want to be able to push notifications to. If we include the .pem file with the PHP script, is this a security issue of any kind or it is fine?

    • http://www.serverdensity.com David M

      Yes this would work but then whoever you give your .pem cert to will be able to send notifications to users of your app. There would be a complication with storing the device IDs that you need to target the notification to a specific user. These need to be collected and stored somewhere and if you’re on multiple servers, that’d need to be centralised or have some kind of distributed sync.

      • Ben

        Right, so it really won’t work because distributing the .pem file would be an issue as it gives them the ability to push notifications to all users of the app.

  • http://www.alohathere.com Rob

    You say you use a custom cron system which runs every few seconds. Does this run a script that checks if there are any unsent notifications and then connect to the apns socket? I’m wondering if you are connecting and disconnecting from the socket for each of these runs or if you have some other way of keeping a persistent connection?

    • http://www.serverdensity.com David M

      Indeed – if there are queued notifications it will connect and send them all through one connection. If there are none waiting then no connection to APNS is made.

  • jeffrey

    I’m having a problem looping through all the queued payloads and then sending them.
    I stored the tokens in an array called $tokens and then used the following code.

    foreach ($tokens as $deviceToken) {
    $payload = json_encode($body);
    $msg = chr(0) . pack(“n”,32) . pack(‘H*’, str_replace(‘ ‘, ”, $deviceToken)) . pack(“n”,strlen($payload)) . $payload;
    fwrite($fp, $msg);
    }
    fclose($fp);

    For some reason it only send a notification to the first token in the array.

    Any help would be great appreciated.

    Jeff

    • http://www.serverdensity.com David M

      I suggest posting programming questions on http://www.stackoverflow.com

    • Muaddub

      It made me crazy too, and you seem the only one on the internet to try to do this! All the tutorials send 1 push per connection, it’s very badly optimized.

      I finally found the solution, you need to do a unique “fwrite”, not one per message:

      // Connect
      $str = ”;
      foreach ($messages as $message) {
      $str .= chr(0) . pack(“n”, 32) . pack(‘H*’, $message['deviceToken']) . pack(“n”, strlen($message['payload'])) . $message['payload'];
      }
      fwrite($fp, $str);
      // Disconnect

      • Luke

        Anyway, here is my attempt to solve this issue. I also posted this on the official developer forum.
        I know, it’s not rocket science code, but it seems to do the job. I’ve been testing it for several days now and it didn’t cause my server to slow down, crash or behave in other weird ways.

        Here is my php code:

        //php

        $message);
        if ($badge)
        $body['aps']['badge'] = $badge;
        if ($sound)
        $body['aps']['sound'] = $sound;

        $payload = json_encode($body);
        $msg = chr(0) . pack(“n”,32) . pack(‘H*’, str_replace(‘ ‘, ”, $deviceToken)) . pack(“n”,strlen($payload)) . $payload;
        error_log(date(‘Y-m-d H:i’).” – Pushing message to APNS”, 3, ‘PushLog.log’); //another log entry
        fwrite($apnsConnection, $msg); //this pushes the message to APNS

        //now let’s close and re-open connection every 2 hours
        if (strtotime(“now”) >= $inTwoHours) {
        $timeNow = strtotime(“now”);
        $inTwoHours = strtotime(“+ 2 hours”);

        //close APNS connection
        fclose($apnsConnection);
        error_log(date(‘Y-m-d H:i’).” – Closing connection to APNS”, 3, ‘PushLog.log’);

        //re-open APNS connection
        //connect to APNS – ONLY 1 time after script is started
        $ctx = stream_context_create();
        stream_context_set_option($ctx, ‘ssl’, ‘local_cert’, ‘apns-dev.pem’);
        // assume the private key passphase was removed.
        // stream_context_set_option($ctx, ‘ssl’, ‘passphrase’, $pass);
        $apnsConnection = stream_socket_client(‘ssl://gateway.sandbox.push.apple.com:2195′, $err, $errstr, 60, STREAM_CLIENT_CONNECT, $ctx);

        error_log(date(‘Y-m-d H:i’).” – Reconnecting to APNS”, 3, ‘PushLog.log’);
        }

        sleep(60); // loop every X seconds
        }
        ?>

      • Luke

        Sorry, something messed with my previous reply. Here is the correct code:

        //php

        $message);
        if ($badge)
        $body['aps']['badge'] = $badge;
        if ($sound)
        $body['aps']['sound'] = $sound;

        $payload = json_encode($body);
        $msg = chr(0) . pack(“n”,32) . pack(‘H*’, str_replace(‘ ‘, ”, $deviceToken)) . pack(“n”,strlen($payload)) . $payload;
        error_log(date(‘Y-m-d H:i’).” – Pushing message to APNS”, 3, ‘PushLog.log’); //another log entry
        fwrite($apnsConnection, $msg); //this pushes the message to APNS

        //now let’s close and re-open connection every 2 hours
        if (strtotime(“now”) >= $inTwoHours) {
        $timeNow = strtotime(“now”);
        $inTwoHours = strtotime(“+ 2 hours”);

        //close APNS connection
        fclose($apnsConnection);
        error_log(date(‘Y-m-d H:i’).” – Closing connection to APNS”, 3, ‘PushLog.log’);

        //re-open APNS connection
        //connect to APNS – ONLY 1 time after script is started
        $ctx = stream_context_create();
        stream_context_set_option($ctx, ‘ssl’, ‘local_cert’, ‘apns-dev.pem’);
        // assume the private key passphase was removed.
        // stream_context_set_option($ctx, ‘ssl’, ‘passphrase’, $pass);
        $apnsConnection = stream_socket_client(‘ssl://gateway.sandbox.push.apple.com:2195′, $err, $errstr, 60, STREAM_CLIENT_CONNECT, $ctx);

        error_log(date(‘Y-m-d H:i’).” – Reconnecting to APNS”, 3, ‘PushLog.log’);
        }

        sleep(60); // loop every X seconds – for chat, one could lower this to 5 seconds…
        }
        ?>

      • Luke

        Ok, it happend again. Sorry, feel welcome to delete my previous replies. Here is a link to the thread I mentioned: https://devforums.apple.com/thread/38022?tstart=0

  • Ben

    If we release a new version of an application does the .pem file have to be updated?

    • http://www.serverdensity.com David M

      I don’t think so.

  • MT

    Did anyone tested Feedback service? I am not getting any thing back from Apple Sandbox feedback server.

    • http://www.serverdensity.com David M

      I believe it’ll only return anything when there are device IDs that are inactive.

  • Jop Peters

    Please change
    $output = json_encode($payload);
    to
    $payload = json_encode($payload);

    Might be confusing otherwise..

    • David

      Thanks for the fantastic tutorial. One question. How could one set up the app where the user could set the time of the notification? For example, the user inputing Today, 10am or Tomorrow, 4am or 10/4/2009?

      • http://www.serverdensity.com David M

        You can just do this by setting a time to send the running a cron to only send those where the timestamp has passed.

  • http://www.icgdev.com ciri

    I have this error: Warning: socket_close(): supplied resource is not a valid Socket resource in /home/web/www.icgdev.com/www/icgdevClientiPush/push.php on line 20

    This is my code:

    $payload['aps'] = array(‘alert’ => ‘push notifications’, ‘badge’ => 1, ‘sound’ => ‘default’);
    $payload = json_encode($payload);

    $apnsHost = ‘gateway.sandbox.push.apple.com’;
    $apnsPort = 2195;
    $apnsCert = ‘apns-dev.pem’;

    $streamContext = stream_context_create();
    stream_context_set_option($streamContext, ‘ssl’, ‘local_cert’, $apnsCert);

    $apns = stream_socket_client(‘ssl://’ . $apnsHost . ‘:’ . $apnsPort, $error, $errorString, 2, STREAM_CLIENT_CONNECT, $streamContext);

    $apnsMessage = chr(0) . chr(0) . chr(32) . pack(‘H*’, ‘fbed918ca8e3bee54710a64d997a6a0fceadc2c584562d62a06c022ebdc2374w’) . chr(0) . chr(strlen($payload)) . $payload;
    fwrite($apns, $apnsMessage);

    echo “”.$errorString.””;

    socket_close($apns);
    fclose($apns);

    • http://www.serverdensity.com David M

      This is only a warning – you can just remove the socket_close() call.

      • http://www.nocturnalware.biz Steve Jarman

        I’m getting the same warnings constantly in my logs. Does removing the socket_close have any negative effect?

  • Pingback: 【iPhone】Push Notificationの実装方法 | iphoneアプリで稼げるのか

  • Lakshmikanth Reddy

    This is the great tutorial for APNS provider, its very clear with the complete flow of push notification, but i am having one doubt here it is like:
    how to send the unique notification badge numbers (1,2,3,4,….) for each device? and when to send it?

    Do we need to set the timer for sending the notifications or any other process?

    Do we need to store the device ids and there respective messages on to the database ?

    • http://www.serverdensity.com David M

      You would need to handle this yourself using a DB to store device IDs.

      The badge # can be sent via the payload – see the tutorial sample code above and the Apple docs.

  • LakshmiKanth Reddy

    can anyone help me out in how to use the feedback service .

    Thank you.

  • Florin O.

    Hi,

    I am getting an SSL error

    Warning: stream_socket_client(): SSL operation failed with code 1. OpenSSL Error messages:
    error:14094416:SSL routines:func(148):reason(1046) in test.php on line 15

    Line 15 is

    $apns = stream_socket_client(‘ssl://’ ..);

    from your code.

    Any idea why is that? I have installed the last openssl (0.9.8c).

    Thanks

  • http://adeem.me Adeem Basraa

    ok i have one question for you, i m testing application from my mac and it works fine but when i put this php code on server it gives me an error of
    “Warning: stream_socket_client() [function.stream-socket-client]: unable to connect to ssl://gateway.sandbox.push.apple.com:2195 (Connection refused) in /home/content/14/4875914/html/code/push/apns.php on line 15
    Failed to connect 111 Connection refused ”

    any idea?

    • http://www.serverdensity.com David M

      Make sure the outgoing port 2195 is open.

      • Roshan

        Does we need some setting to our server to open the outgoing port 2195?

      • http://www.serverdensity.com David M

        This would be in your firewall config.

  • Pingback: Roundup: Push And Purchase at Under The Bridge

  • http://jamesjennin.gs james

    Howdy, great write up! Makes more sense than the dense Apple documentation.

    I was wondering if anybody has any insights on the load a typical APNS message server experiences. I’m curious how many messges per second people were able to achieve.

    I’ll follow up here, but I’ve also posted this question on Stack Overflow: http://stackoverflow.com/questions/1456663/apple-push-notification-service-server-load

    • http://www.serverdensity.com David M

      We haven’t had any issues with load but are only sending a few hundred messages per day. The operation is really lightweight though in that it is only a simple loop and socket manipulation.

  • Simon

    Well, it is general a helpful article on APNS.

    Unfortunately, I am udner the same situation with ciri.
    Warning: socket_close(): supplied resource is not a valid Socket resource in /home/www/websites/…/iapns.php on line 46

    Any idea about this?
    Is that mean my socket connect to apns was in fact fail ?

    • http://www.serverdensity.com David M

      You can ignore this warning.

  • Suresh

    Hi

    Good article in APN. But apple’s document says “You should also retain connections with APNs across multiple notifications. (APNs may consider connections that are rapidly and repeatedly established and torn down as a denial-of-service attack.)”. The document is http://developer.apple.com/iphone/library/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/CommunicatingWIthAPS/CommunicatingWIthAPS.html#//apple_ref/doc/uid/TP40008194-CH101-SW1.

    Though this code works, you will end up your certificate revoked as mentioned by Curtis Fued and ackoledged bu you. So, have you found a way to keep the connection open and if so, can you update the article please?

    • http://www.serverdensity.com David M

      We keep the connection open when sending messages, the queue for which is parsed every minute. I don’t see this to be an issue right now due to the low volume of messages we’re sending (hundreds per day) and I really only see it to be an issue if we were opening and closing the connection every minute…a problem that would arise when sending thousands of messages per day.

      In that case PHP is not designed for this and I would probably look at writing a daemon in Python to keep the socket open.

      • Suresh

        Thanks for the quick reply. You’re saying that you’re keeping connection when sending messages, the queue for which is parsed every minute…but also saying “I really only see it to be an issue if we were opening and closing the connection every minute.”. So, it appears you’re opening connection every minute and keep the connection open in that minute period. If that is the case, then its an issue. Could you please explain if my understanding differs from what you tried you explain?

      • http://www.serverdensity.com David M

        If we have 5 messages in the queue, the connection will be kept open whilst those 5 messages are sent. And the queue is processed every minute, but we don’t have new messages to send for each minute – we’re only sending a few hundred a day. As such, the connection isn’t opened. The situation starts to become more concerning if we were opening and closing the connection every minute. To be honest, I don’t thin that kind of frequency is what Apple are worried about but even so that would be the point when I would re-factor this code to something more suitable.

    • Luke

      Great tutorial! Thanks!

      I’d really be interested in a good way, to retain APNS connection. In my current project I plan on a user to user communication feature and opening/closing connection every time a message gets pushed would soon get me into trouble.
      Any help appreciated! Thanks in advance

  • Casey

    Were you able to find a server (preferably free) that allows a message to be sent out on port 2195. I have a working solution on my computer, but when I upload the files to a server, each free solution I try blocks 2195.

    Is there any way to send a message w/o buying a virtual machine?

    • http://www.serverdensity.com David M

      Everything has it’s price. If you want something free don’t expect it to work how you want. VPSs are so cheap nowadays (SliceHost, Linode, Rackspace Cloud) that if you care about your app I can’t think why you wouldn’t be willing to pay.

    • Josh

      There are a variety of different little services popping up that offer hosted push notifications (as a good alternative to hosting your own server) such as http://www.appnotify.com/. I know the people building this and it looks pretty solid, but it’s not quite ready yet. Still something to look forward to!

  • http://kamalchalla.com Kamal Challa

    Hi

    great tutorial

    when i tried the code

    i got

    Warning: stream_socket_client() [function.stream-socket-client]: unable to connect to ssl://gateway.sandbox.push.apple.com:2195 (Connection timed out) in apns.php on line 9
    Failed to connect

    What might be the issue ?

    iam on bluehost

    • http://www.serverdensity.com David M

      Most likely the host is blocking that outgoing port.

      • http://kamalchalla.com Kamal Challa

        Yeah , i checked and you are right

        Then i tried to run using local mac machine but go the following errors

        Warning: stream_socket_client() [function.stream-socket-client]: Unable to set private key file `/Applications/XAMPP/xamppfiles/htdocs/apns/apns-dev.pem’ in /Applications/XAMPP/xamppfiles/htdocs/apns/apns.php on line 9

        Warning: stream_socket_client() [function.stream-socket-client]: failed to create an SSL handle in /Applications/XAMPP/xamppfiles/htdocs/apns/apns.php on line 9

        Warning: stream_socket_client() [function.stream-socket-client]: Failed to enable crypto in /Applications/XAMPP/xamppfiles/htdocs/apns/apns.php on line 9

        Warning: stream_socket_client() [function.stream-socket-client]: unable to connect to ssl://gateway.sandbox.push.apple.com:2195 (Unknown error) in /Applications/XAMPP/xamppfiles/htdocs/apns/apns.php on line 9

        Warning: socket_close() expects parameter 1 to be resource, boolean given in /Applications/XAMPP/xamppfiles/htdocs/apns/apns.php on line 11

        Warning: fclose() expects parameter 1 to be resource, boolean given in /Applications/XAMPP/xamppfiles/htdocs/apns/apns.php on line 12

        I got PEM certificated generated from other machine,

        SO i am totally confused what i have to do now,

        Please let me know what i need to do to run it in localhost

        Thanks in advance

      • http://www.serverdensity.com David M

        I suggest you post your question on http://www.stackoverflow.com

  • Josh

    Great article! Sums up the process very well.

  • Jo

    Hi,
    I tried the instructions and wow! (almost) work like a charm.
    My payload looks like this :
    {“aps”:{“alert”:”push notifications”,”badge”:1,”sound”:”default”}}
    it doesnt display the all important alert. Can you help me please?

    • http://www.serverdensity.com David M

      The payload looks correct so there is probably an issue somewhere else in your call to APNS or the registration of your app/device.

      • Jo

        Yes – I was being retarded, I copied the register code from apple’s site – where they have excluded the UIRemoteNotificationTypeAlert parameter. Thanks for the reply.

  • http://Noneuntilnow!! Ram

    Hi,

    We are new to the MAC/Unix environment and wanted to demonstrate APNS. While executing the command php -f /usr/local/push/PushMonitor.php as suggested in readme doc of PHP APNS. I get some unusual dump on my terminal like!DOCTYPE html PUBLIC “-//W3C//DTD HTML 4.01//EN” “http://www.w3.org/TR/html4/strict.dtd”>

    .
    .
    .

    for sure i know the script is not running. Can you please tell me what i must do. Please help.

  • Pingback: Update « iBirthday Dev & Project Blog

  • rahulvyas

    where is the objective-c code to implement this in the iphone app.could you explain this step by step.suppose i have an application and i want to use push notification.now tell me each step to take

    • http://www.serverdensity.com David M

      See the Apple docs.

  • bhoomesh

    hi david,

    i generated both certificate.pem and privatekey.pem file from .p12 files. they converted properly, but when i used to check the .pem file with following command
    $openssl verify pkey.pem
    giving
    error 20 at 0 depth lookup:unable to get local issuer certificate.
    what does it mean?
    thanks in advance.

  • Pingback: How we fixed production push notifications not working while sandbox works | Start IT up

  • Daniel Sundström

    Thank you for this post! It helped me alot when first enabling Push Notifications!

    Keep up the good worK!

  • rahulvyas

    can you post your php croning code?

    • http://www.serverdensity.com David M

      We built our own cron daemon in Python so that it can run more often than every minute.

  • rahulvyas

    ok
    thanks for the quick reply……….

  • Pingback: Laurent Pantanacce » links to blog.pantanacce.com (weekly)

  • Dev

    Thanks for the great writeup. This really helps!

  • http://www.thumbgenius.com thumbgenius

    Hi – great tutorial. hard to find much on the topic.
    worked like a charm, but it would make the tutorial all encompassing if you included (or at least mentioned)

    1. creating a push-enabled AppID. push notifications need ABCDEFG.com.company.title AppID rather than the ABCDEFG.* AppID. And then you have to create a provisioning profile based on that AppID – you’re app must use this provisioning profile.
    2. some clientside code. i used a simple view-based app and put the following code in the appdelegate.m
    - (void)applicationDidFinishLaunching:(UIApplication *)application {

    // Override point for customization after app launch
    [window addSubview:viewController.view];
    [window makeKeyAndVisible];

    [application registerForRemoteNotificationTypes:UIRemoteNotificationTypeBadge|UIRemoteNotificationTypeSound|UIRemoteNotificationTypeAlert];
    }
    - (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
    {
    NSLog(@”device Token is:%@”, deviceToken);
    // this is the what you need to put into your PHP apns server
    }
    - (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
    {
    NSLog(@”Failed to register for push %d %@”, [error code], [error userInfo]);
    }
    3. (and i may get laughed at about this – but i did it). your app must be closed for the alert to pop up on your phone.

    thanks again – the certificate stuff helped GREATLY!

  • http://www.thumbgenius.com thumbgenius

    had trouble with removing the passphrase (which you mentioned).
    didnt work for me, but you can keep it and just have PHP set it by

    stream_context_set_option($streamContext, ‘ssl’, ‘passphrase’, $yourpassphrase);

  • Online Car Insurance >> http://onlinecarinsuranceclaims.com/

    [... - blog.boxedice.com is other nice place of tips. Online Car insurance claims [... -

  • http://www.synopse.net Christoph Burgdorfer

    Hi, what could be the problem if there is no private key, i.e. the “Apple Development Push Services” is not expandable?

    Thanks,
    C.

    • http://www.serverdensity.com David M

      You might want to contact Apple support or post on their forums.

    • http://www.alexcurylo.com/blog Alex Curylo

      That probably means that someone who’s not you activated APNS for your app ID, and uploaded a .certificateSigningRequest file from their machine. As you are not them, their private key is not on your machine.

      At least, that’s why I had those symptoms.

      To be absolutely certain everything should be in order, turn off APNS for the app ID, revoke the developer/production certificates, delete all your .mobileprovision files, then regenerate them from scratch using the same .certificateSigningRequest for all of them. And actually delete all existing .mobileprovision files, don’t just edit them; otherwise they won’t pick up the proper permissions after APNS is enabled.

    • http://www.synopse.net Christoph Burgdorfer

      Thanks Guys, it turns out the key was not correctly exported from the machine which was actually also provisioning/certifying the application.

  • rahulvyas

    Hello david, i am getting a strange problem.I have created all the necessary certificates and all shows right.I created an urbanairship.com account.I send a testing push notification on my device Urbanairship shows sent successfully but i never get any push notification.Do you know something about this,BTW my device is iphone 3G and it’s hactivated and jailbroken still i’m getting device token from APNS.

  • Pingback: Philipp Schmid » Blog Archive » good apple push notification tutorial

  • http://orangeslideapps.com Bryan Crampton

    I’m getting this error

    Warning: stream_socket_client() [function.stream-socket-client]: Unable to set private key file `apns-dev.pem’ in /home/bryan/sendpush.php on line 14

    Warning: stream_socket_client() [function.stream-socket-client]: failed to create an SSL handle in /home/bryan/sendpush.php on line 14

    Warning: stream_socket_client() [function.stream-socket-client]: Failed to enable crypto in /home/bryan/sendpush.php on line 14

    Warning: stream_socket_client() [function.stream-socket-client]: unable to connect to ssl://gateway.sandbox.push.apple.com:2195 (Unknown error) in /home/bryan/sendpush.php on line 14

    Warning: fwrite(): supplied argument is not a valid stream resource in /home/bryan/sendpush.php on line 17

    Warning: socket_close() expects parameter 1 to be resource, boolean given in /home/bryan/sendpush.php on line 19

    Warning: fclose(): supplied argument is not a valid stream resource in /home/bryan/sendpush.php on line 20

    • http://tixocloud.com Tereno

      Hi Bryan,

      Not sure if you’ve managed to figure it out already but the first error could be due to improper generation of certificates. I’ve tried renaming the final pem file and redoing the whole certificate and key generation process again. Hope it helps!

  • http://www.synopse.net Christoph Burgdorfer

    Just a quick side question: has anyone experience with setting up the PHP-APNS, David mentions?

    I’ve tried to set it up, find it a very painful process because of all the dependencies.

    Any other experiences?

  • Pingback: links for 2009-12-16 « Bloggitation

  • http://www.youloft.com owen

    just got one question, hope someone can help.

    my current Distribution provisioning profile is using apple ID with wildcard
    com companyName.*

    for using APN, I have to create a new apple ID, com.companyName.appName

    my question is, do i need create a new Distribution provisioning profile for this new apple ID? can I use the one for apple ID com companyname.*,

  • http://www.easyapns.com mrmidi

    There is an open source PHP/MySQL back-end for APNS. So if you want your own integration of push notifications on your own server, here you go :)

    Main Link: http://www.easyapns.com
    Google Code: http://code.google.com/p/easyapns/
    Google Group: http://groups.google.com/group/easyapns

  • syedhassan4

    Using above instructions I am able to send push notification using my mac system on which I generated these files.

    But when I moved these files as it is to my web server they stop working. Do I need to setup anything or server or I have to change certificate for the server -if yes how and from where?

    I also uploaded .p12 files (certificate and Key) on server but still did not work.

    Do I need more settings/config on the server? What I am missing. I spent two days to figuring it out but still no clue.

    It is working on my machine but not on server. where is the issue?

    I added some echo statements to check where code stop, php code show me “Trying to open connection”, after that no message, no warning nothing.

    I placed all my files in one folder.
    1. apns.php
    2. apnsdev.pem
    3. apnsdevcert.p12
    4. apnsdevkey.p12

    Trying to run apns.php like http://www.myserver.com/apntesting/apns.php

    try {
    echo “Trying to open connection”;

    $apnsConnection = stream_socket_client(‘ssl://’ . $apnsHost . ‘:’ . $apnsPort, $error, $errorString, 60, STREAM_CLIENT_CONNECT, $streamContext);

    if (apnsConnection == false) {
    echo “Failed to connect {$error} {$errorString}n”;
    print “Failed to connect {$error} {$errorString}n”;
    return;
    }

    echo “construct apnsMessage”;

    • http://www.serverdensity.com David Mytton

      Check for any firewall rules.

  • Ale

    Hi David,
    is there a way to insert a LF or CR within payload!?

    Thx
    /a

    • http://www.serverdensity.com David Mytton

      I don’t know. Probably not, but try it and see what happens. The payload is just JSON.

      • Ale

        already tried :(
        I’ll try with hex code or some other stuff… Maybe some escape char…

        Thx
        /a

      • Ale

        No, I was wrong…
        To insert LF just use “n” within JSON.
        Actually, it didn’t work because I forgot to escape the “n” -in “\n” – before escaping the whole payload for db…
        BTW, the alert is limited in height, pushing many strings will result in message truncation…

        Thx
        /a

  • loganathan

    Hi david,
    I can’t get the .csr files from apple web site. Can you explain how to download certificate files. I can’t see any of the login fileld on program portal. So i am create the files with extensions of .pem

  • Pingback: iPhoneGeek 爱疯极客 » 怎样编写Apple Push Notification服务器

  • Pingback: Push Notifications in your iPhone App using PHP and Ubuntu | The Discussion Delegate | Vercingetorix Technologies | iPhone, Android, Blackberry, PHP, Generatrix, Wordpress App Development in Pune, India

  • Sergio Garcia

    Hello, I have the same problem than rahulvyas: Hello david, i am getting a strange problem.I have created all the necessary certificates and all shows right.I created an urbanairship.com account.I send a testing push notification on my device Urbanairship shows sent successfully but i never get any push notification.Do you know something about this,BTW my device is iphone 3G and it’s hactivated and jailbroken still i’m getting device token from APNS.

    but I have installes pushfix from cydia and my other applications with push notifications works properly. Any ideas?

    Thanks

    • http://www.serverdensity.com David Mytton

      Best post on Apple forums / call Apple dev support.

  • http://www.citysavingsguides.com Jennifer Ely

    Thank you for a terrific tutorial.

    I believe I have implemented it correctly but I am not receiving the notifications on my phone. I have an ad hoc provisioning profile set up. The appID does not have a wild card. I have the device token from the phone through NSLOG to console. I have the php script above running without errors. Nothing is returned through a feedback PHP script I wrote. The notifications just don’t appear.

    Any ideas where I am going wrong?

  • Anil

    Very nice tutorial.. thanks a lot….

  • Duccio

    There is an open source PHP classes collection for APNS: ApnsPHP

    With this comprehensive collection of open source PHP classes you can also easily create a Push Server with one or more (forked) processes reading from a common message queue to speed-up sending activities.

    Google Code: http://code.google.com/p/apns-php/

    • http://www.serverdensity.com David Mytton

      This is linked in the tutorial itself but discounted because it runs PHP as a daemon, which it’s not designed to do, and has dependancies on things like memached.

      • Duccio

        Hi David, in the tutorial is linked php-apns, this is apns-php, just uploaded yesterday :-)

  • Juan Pablo

    Best tutorial ever! I thought it would take me more than a week to get push notifications running. But if you follow carefully this tutorial you will have them running in less than a couple of hours! thanks!

  • Pingback: The Complete iPhone Development Toolbox | iPhoneApp Dev Blog

  • http://winayak.com Priya mahajan

    Hi,
    Thanks for this post.

    I tried to do at my end. I’m not getting any error but still iphone not receiving any push. I checked iphone settings and it’s looks fine.

    Here is my server code. Can you please review it?

    Thanks,
    Priya

    • http://www.serverdensity.com David Mytton

      Sorry, this isn’t the place to review code. I suggest posted on the Apple Developer forums or Stack Overflow.

  • Pingback: Mail Web - Actualité Internet » Blog Archive » Delicious du février 9th au mars 5th

  • Artin

    Hi, I tried all these steps but still I’m getting the following error even in my local php server.

    Warning: stream_socket_client() [function.stream-socket-client]: Unable to set local cert chain file `apns-dev.pem’; Check that your cafile/capath settings include details of your certificate and its issuer.

    please help me to solve it thanks.

    • http://www.serverdensity.com David Mytton

      Post on Stack Overflow or the Apple Dev Forums.

  • Pingback: How to renew your Apple Push Notification Push SSL Certificate « Boxed Ice Blog

  • Pingback: The exciting adventures of an alert notification « Boxed Ice Blog

  • Pingback: App development per iOS, i tutorial - TheAppleLounge

  • Pingback: Mail Web – Actualité Internet » Blog Archive » Delicious du février 9th au mars 5th

  • Pingback: Apple Push Notification Resources « Brainwash Inc. – iPhone/Mobile Development

  • Pingback: Mind Juice » Push It Real Good

  • Pingback: » Push Notification Servers

  • Pingback: TheGoldenAura » Blog Archive » Mikko Ohtamaa: Apple push notifications (APN) with Python

  • Pingback: Trial’s & Tribulations with Apple push notification » Refactored scope