vrijdag 4 april 2014

A 10-step guide in setting up Push Notifications in XCode5/iOS7

Prerequisites

We are assuming that:

  • You have XCode5 installed; 
  • At least know your way around XCode; 
  • Have a developer account at Apple.

Introduction

This document describes the different steps to take when setting up an iOS app with Remote Notifications. The detailed information about sending Remote Notifications can be found here:

https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/Introduction.html.

The summary:

  • You need to generate a certificate that is tightly linked with your app. With the certificate the APNS servers know where to send the messages to, and if you are allowed to do so; 
  • The certificates open an SSL connection over which a payload is sent. 
  • The payload contains a JSON encoded message, which at least should have a body like:
    {
        aps =     {
            alert = "FooBarPushDemo test!";
        };
    }
    
  • The APS key in the payload is mandatory, other keys can be added (e.g. foobar to store additional information)
    {
        aps =     {
            alert = "FooBarPushDemo test!";
        };
        foobar =     {
            some = "data";
        };
    
    }

Step 1: Generate a Certificate Signing Request (CSR)

  • Open the Keychain Access application on your Mac; 
  • Open the Keychain Access menu, and choose Certificate Assistant > Request a Certificate from a Certificate Authority 


  • Enter the following information: 
    • User E-mail Address: your email address;
    • Common name: the name of the application, e.g. FooBarPushDemo
    • Select the option to save the CSR to disk (save it to a specific folder as you will need to store more files there, e.g. ~/Desktop/FooBarPushDemo and name it FooBarPushDemo.certSigningRequest 
  • Close the window after completion, but stay in the Keychain Access application

Step 2: export the private key

  • In the Keychain Access application, select the newly created Private Key  
  • Right-click (or ctrl-click) it and select Export “FooBarPushDemo” 
  • Name the file FooBarPushDemoKey.p12 and save it to the same location as your CSR file; 
  • You will be prompted for a password (passphrase) to protect the exported key. Choose “foobar” as passphrase 
  • Next you will be prompted for your Mac password, and continue 
  • Save the exported key in the same folder as the CSR.


Step 3: create a new iOS application

  • Create a new XCode iOS project via File > New > Project 
  • From the iOS Application menu, select Single View Application and click ‘Next’ 

  • Enter the following information for: 
    • Product Name: FooBarPushDemo 
    • Organisation Name: your company name
    • Company identifier: be.acuzio.ios.demo
    • Class Prefix: FooBar 
    • Devices: iPhone 

  • The bundle identifier in our example now is be.acuzio.ios.demo.FooBarPushDemo. This bundle identifier will be need in the next steps when you create an App ID. 
  • Press Next to choose the location where to store your project and create the project

Step 4: set-up an AppID

  • Log in to the iOS Development Central (https://developer.apple.com/devcenter/ios/index.action)
  • Select Certificates, Identifiers & Profiles from the iOS Developer Program menu 
  • Select Identifiers from the iOS Apps menu 
  • In the App ID’s option, add a new identifer with this information: 
    • App ID description: FooBarPushDemo 
    • Select “Explicit App ID”, which is a requirement when you are using the Apple Push Notifications Service (APNS) 
    • Enter the bundle identifier: be.acuzio.ios.demo.FooBarPushDemo 
    • Check the box next to Push Notifications in the App Services block 
  • Click Continue and confirm by hitting Submit 
  • Click Done to return to the App IDs overview, you should now see the newly created AppID in the list

Step 5: set-up the Push Notification SSL Certificate


  • Select the newly created AppID from the list, it should look more or less like this: 

  • The option “push notifications” in development is in orange, which means it’s not set-up yet. Click on Edit and scroll down to the Push Notifications Section 
  • Click on “Create Certificate” for the “Development SSL Certificate” group 

  • You will be informed that you will need a CSR file (see Step 1: Generate a Certificate Signing Request (CSR)). 
  • Click “Continue” 
  • Upload the CSR file you created before and click on “Generate” 

  • Download the certificate and move it to the same folder e.g. ~/Desktop/FooBarPushDemo, make sure it is named “aps_development.cer” 

  • Add the certificate by double clicking on the .cer file. It will add a public key to your private key

Step 6: create a PEM file

  • Open a Terminal window and change to the directory where you stored the CSR, P12 key and Development certificate

$ cd ~/Desktop/FooBarPushDemo  

  • Convert the .cer file into a .pem file

$ openssl x509 -in aps_development.cer -inform der -out FooBarPushDemoCert.pem

  • Convert the .p12 key into a .pem file

$ openssl pkcs12 -nocerts -in FooBarPushDemoKey.p12 -out FooBarPushDemoKey.pem
Enter Import Password:
MAC verified OK
Enter PEM pass phrase:
Verifying - Enter PEM pass phrase:

  • The first time you will be prompted for the Passphrase, which in this example is “foobar”. 
  • The second and third password will be to protect your newly created key. I chose the same “foobar” password, but you can choose a different one. This password will actually be required to set-up the SSL connection to the Apple Push Notification Service servers. 
  • Concat the 2 .pem files into a combined .pem file

$ cat FooBarPushDemoCert.pem FooBarPushDemoKey.pem > FooBarPushDemo.pem

  • Now check if a connection can be set-up to the servers:

$ openssl s_client -connect gateway.sandbox.push.apple.com:2195 -cert FooBarPushDemoCert.pem -key FooBarPushDemoKey.pem

  • If all goes well, you should see a lot of information on the certificate chain the SSL handshake etc. You will not see you prompt, because the service is actually waiting for you to send a payload over the SSL connection. Just enter “Hello” and enter. The connection will be closed. You’ll see something like this:

…
---
No client certificate CA names sent
---
SSL handshake has read 2731 bytes and written 2189 bytes
---
New, TLSv1/SSLv3, Cipher is AES256-SHA
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
SSL-Session:
    Protocol  : TLSv1
    Cipher    : AES256-SHA
    Session-ID: 
    Session-ID-ctx: 
    Master-Key: BFED02C173464345CE8D6B59CB93D5B383260560ACB87A81C441A8145E646CCBDE5F01BFC545946A147824D2222CF647
    Key-Arg   : None
    Start Time: 1396596337
    Timeout   : 300 (sec)
    Verify return code: 0 (ok)
---
hello
closed
$

  • In development you will be using the sandbox APNs, in production you will need the other one.

Step 7: create a provisioning file

  • Log in to the iOS developer portal, as you did when creating the App ID
  • Navigate to the “Certificates, Identifiers & Profiles” menu and go to the provisioning files
  • Click on the add button to add a new provisioning profile 
  • Select the iOS App Development as type of provisioning file and click “continue” 

  • Select the FooBarPushDemo App ID from the drop-down list and continue 


  • Select the certificates you wish to include in this provisioning profile. To use this profile to install an app, the certificate the app was signed with must be included 
  • Select the devices on which you are going to test. You will need a physical device to test on, as push notifications will not work on your simulator 
  • Enter “FooBarPushDemo Provisioning File” as name and click “Generate” 
  • When the generation finishes, click on “Download” and save the file next to the other certificates etc. 
  • To add the provisioning file to XCode, drag the “.mobileprovision” file and drop it on the XCode icon

Step 8: Making your iOS app Push-Notifications enabled

  • Open the application delegate (in our case FooBarAppDelegate.m” - When the application is launched, it needs to register with APNS in order to be able to receive Remote Notification (Push Messages). - You’ll need to add the registration code to your “didFinishLaunchingWithOptions” method

@implementation FooBarAppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    // Override point for customization after application launch.
    [[UIApplication sharedApplication] registerForRemoteNotificationTypes:
        (UIRemoteNotificationTypeAlert |
         UIRemoteNotificationTypeBadge |
         UIRemoteNotificationTypeSound)];
    
    
    return YES;
}

  • This registration will show the user, when the application is first launched, a message that this app uses remote notifications. 
  • A device token is used to send messages to a specific device. This can be accomplished by adding these lines of code:

 
- (void) application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
    NSLog(@" Registered device for remote notifications: %@", 
    [deviceToken.description stringByReplacingOccurrencesOfString:@" " withString:@""]);
}

- (void) application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
    NSLog(@" Failed to register for remote notifications: %@", error);
}

  • Start the application on a physical device, you log should print out a message resembling this (the device token is 64 characters in length):

2014-04-04 10:39:26.764 FooBarPushDemo[2369:60b]  Registered device for remote notifications: 

  • We now know when the application is registered, of failed to register. The AppDelegate also captures the remote notification itself. In order to process the information, implement the following method:

- (void) application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
    NSLog(@" Received remote notifcation: %@", userInfo);
}

Step 9: change build settings

  • Make sure that the correct “team” is selected, it should match the signing identity you used for signing your certificate and provisioning file 

  • Enable the “Remote notifications” background mode: 

  • Open the build settings and look for the entry “provisioning profile” 

  • Change it to match the added Provisioning Profile

Step 10: sending push notifications


  • When you want to send a Push notification to a device, you need to open an SSL connection to the APNs servers using the generated certificate. 
  • When the connection is established, you can transmit a binary payload. 
  • If no feedback is returned, it means that the message was sent successfully! 
  • An example PHP script:

<?php

$device_tokens = array("e2f8cd253f2…d5b78ba7055e44f6…d1a77fddf350");

$passphrase = "foobar";

$message = "FooBarPushDemo test!";

$cert = dirname(__FILE__) . '/FooBarPushDemo.pem';

///////////////////////////////////////////////////////////////////////////////
$ctx = stream_context_create();

stream_context_set_option($ctx, 'ssl', 'local_cert', $cert);
stream_context_set_option($ctx, 'ssl', 'passphrase', $passphrase);


for($i = 0; $i < count($device_tokens); $i++) {
    $device_token = mb_convert_encoding($device_tokens[$i], 'utf-8');


    echo "Device token : $device_token, length <" . strlen($device_token) . ">" . PHP_EOL;

    // Open a connection to the APNS server
    $fp = stream_socket_client(
        'ssl://gateway.sandbox.push.apple.com:2195', $err,
        $errstr, 60, STREAM_CLIENT_CONNECT|STREAM_CLIENT_PERSISTENT, $ctx);

    stream_set_blocking($fp, 0);
    stream_set_write_buffer($fp, 0);

    if (!$fp)
        exit("Failed to connect: $err $errstr" . PHP_EOL);

    echo 'Connected to APNS' . PHP_EOL;

    // Create the payload body
    $body['aps'] = array(
        'alert' => $message
    );

    // Encode the payload as JSON
    $payload = mb_convert_encoding(stripslashes(json_encode($body)), 'utf-8');

    // Build the binary notification

    if(strlen($device_token) != 64) {
        die("Device token has invalid length");
    }

    if(strlen($payload) < 10) {
        die('Invalid payload size');
    }

    $msg = chr(0)
            .pack('n', 32) //token length
            .pack('H*', $device_token) //token
            .pack('n', strlen($payload)) //length of payload
            .$payload;


    // Send it to the server
    $result = fwrite($fp, $msg /*, strlen($msg) */);

    if (!$result)
        echo 'Message not delivered' . PHP_EOL;
    else {
        echo 'Message successfully delivered, result:' . $result . PHP_EOL;

        echo "Sent {" . strlen($msg) . "} to server, received {" . $result . "}" . PHP_EOL;

        $response = fread($fp, 6);
        var_dump($response);

        $messageResult = unpack('Ccommand/CstatusCode/Nidentifier', $response);
        var_dump($messageResult);

    }

    //connect to the APNS feedback servers
    //make sure you're using the right dev/production server & cert combo!
    
    $apns = stream_socket_client('ssl://feedback.sandbox.push.apple.com:2196', $errcode, $errstr, 60, STREAM_CLIENT_CONNECT, $ctx);
    if(!$apns) {
        echo "ERROR $errcode: $errstr\n";
        return;
    }

    echo "Feedback returned: " . PHP_EOL;

    $feedback_tokens = array();
    //and read the data on the connection:
    while(!feof($apns)) {
        $data = fread($apns, 38);
        var_dump($data);
        if(strlen($data)) {
            $feedback_tokens[] = unpack("N1timestamp/n1length/H*devtoken", $data);
        }
    }

    var_dump($feedback_tokens);

    fclose($apns);
    fclose($fp);
}

Geen opmerkingen:

Een reactie plaatsen