Wednesday 9 April 2014

Apple Push Notification Services and Local Notifications

Apple Push Notification Services and Local Notifications



  • Apple Push Notification Services


APNS requires, following


  • The CSR
  • The private key as a p12 file (PushChatKey.p12)
  • The SSL certificate, aps_development.cer

Step 1.Generating CSR




A CSR certificate will be generated here, save this certificate as “PushChat.certSigningRequest.


Step 2-Generating Private Key(.p12 file)







Once you generate CSR, you can find Private Key has been generated, save this private key as PushChatKey.p12




3.Generating SSL certificate, aps_development.cer


To generate SSL certificate, we need to create a Push Notification enabled Apple ID.



















Once Push Notification enabled App ID is created, select the one you created from list of App Ids, an example shown below.






















Next we will be editing the one we selected



















Create SSL for Development or Production, as required.Below we are creating SSL for Development.




Provide CSR we created.






Once the certificated is created, save this SSL Certificate. example- aps_development.cer.















Following Three Steps above, we have done following,


Created CSR,
Generated private key (.p12) from Keychain
Saved CSR, generated SSL Certificate.



Going further we need Private Key, Private Key (.p12 file) and SSL Certificate we generated.You Can forget the CSR but it is easier to keep it. When your certificate expires, you can use the same CSR to generate a new SSL Certificate.


Now we will combine Private Key(.p12) and SSL (.cer) to make a .PEM file.Open Terminal and do following.

  • Go to the folder where you downloaded the files, in my case the Desktop:
  • $ cd ~/Desktop/
  • Convert the .cer file into a .pem file:
  • $ openssl x509 -in aps_development.cer -inform der -out PushChatCert.pem
  • Convert the private key’s .p12 file into a .pem file:
  • $ openssl pkcs12 -nocerts -out PushChatKey.pem -in PushChatKey.p12
  • Enter Import Password:
  • MAC verified OK
  • Enter PEM pass phrase:
  • Verifying - Enter PEM pass phrase:



Finally, combine the certificate and key into a single .pem file:


  • $ cat PushChatCert.pem PushChatKey.pem > ck.pem
  • At this point it’s a good idea to test whether the certificate works. Execute the following command:
  • $ telnet gateway.sandbox.push.apple.com 2195
  •            Trying 17.172.232.226...
  • Connected to gateway.sandbox.push-apple.com.akadns.net.
  • Escape character is '^]'.



Let’s try connecting again, this time using our SSL certificate and private key to set up a secure connection:


  • $ openssl s_client -connect gateway.sandbox.push.apple.com:2195
  • -cert PushChatCert.pem -key PushChatKey.pem
  • Enter passphrase for PushChatKey.pem:


You should see a whole bunch of output, which is openssl letting you know what is going on under the hood.
If the connection is successful, you should be able to type a few characters. When you press enter, the server should disconnect. If there was a problem establishing the connection, openssl will give you an error message but you may have to scroll up through the output to find it.



Create Provisioning profile with the Push Notification enabled App ID.



Using APNS in our code.


In App delegate’s didFinishLaunchingWithOptions method do the following,




- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Let the device know we want to receive push notifications
[[UIApplication sharedApplication] registerForRemoteNotificationTypes:
(UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound | UIRemoteNotificationTypeAlert)];
   return YES;

}



Here we will generate the token to register Remote Notification,



- (void)application:(UIApplication*)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken
{
NSLog(@"My token is: %@", deviceToken);
}
- (void)application:(UIApplication*)application didFailToRegisterForRemoteNotificationsWithError:(NSError*)error
{
NSLog(@"Failed to get token, error: %@", error);
}


When your app registers for remote (push) notifications, it tries to obtain a “device token”. This is a 32-byte number that uniquely identifies your device. Think of the device token as the address that a push notification will be delivered to.
Run the app again and you should see something like this in Xcode’s console window:


You might generate some token as below,

<740f4707 462c7eaf="" 5f6aa01d="" 61bb78ad="" 8e335894="" 9b7c25d4="" a5ddb387="" bebcf74f="">



On server side use following php script

/ Put your device token here (without spaces):
$deviceToken = '0f744707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bbad78';
// Put your private key's passphrase here:
$passphrase = 'pushchat';
// Put your alert message here:
$message = 'My first push notification!';




PHP Script sample is given below,




// Put your device token here (without spaces):
$deviceToken = '0f744707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bbad78';


// Put your private key's passphrase here:
$passphrase = 'pushchat';


// Put your alert message here:
$message = 'My first push notification!';

////////////////////////////////////////////////////////////////////////////////



$ctx = stream_context_create();
stream_context_set_option($ctx, 'ssl', 'local_cert', 'ck.pem');
stream_context_set_option($ctx, 'ssl', 'passphrase', $passphrase);


// 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);


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,
'sound' => 'default'
);


// Encode the payload as JSON
$payload = json_encode($body);



// Build the binary notification
$msg = chr(0) . pack('n', 32) . pack('H*', $deviceToken) . pack('n', strlen($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' . PHP_EOL;
// Close the connection to the server
fclose($fp);


Make sure to Copy your ck.pem file into the your .php file’s folder.


Then open a Terminal and type:
$ php simplepush.php
If all goes well, the script should say:
Connected to APNS
Message successfully delivered




Finally you must be receiving a notification as below









  • Local Notifications

Why Local Notifications??


Local Notifications are system based notifications, which can be triggered on scheduled time intervals.
We can schedule, rating of our app to user at regular intervals,remind user of something when he enters into app, asking user for in App Purchases,notify user about an event and many more alerts or notifications can be presented to user as and when required.


Local Notifications, unlike Remote Push Notifications do not interact with server, hence are faster and reliable.


An audio streaming app might notify user of the loss of network connection or a calendar based application can show an upcoming appointment.



How to use Local Notifications?



When the iOS delivers a local notification there are three possibilities:


  1. Your application is not running. The system displays the alert message, badges the application, and plays a sound – whatever is specified in the notification. The app can be run and brought to the front when user taps (or slides) the action button (or slider) of the notification. When it happens, your app’s delegate will receive application:didFinishLaunchingWithOptions: with an NSDictionary object that includes the local-notification object.
  2. Your application is not frontmost and not visible but is suspended in the background.The system displays the alert message, badges the application, and plays a sound – whatever is specified in the notification. The app can be brought to the front when user taps (or slides) the action button (or slider) of the notification. When it happens (user interacts with the notification), your app’s delegate will receive application:didReceiveLocalNotification:.


Your application is foremost and visible when the system delivers the notification, no alert is shown, no icon is badged, and no sound is played. Your app’s delegate will receive application:didReceiveLocalNotification:. In this case you have to notify users in your own way within your app.





// case 1 - AppDelegate.m , When application is running in Background


- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
   // Override point for customization after application launch.
   // ... your code here ...
   UILocalNotification *notification = [launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey];
    
   NSLog(@"%@", notification);
    
   if (notification)
   {
       UIAlertView *aw = [[UIAlertView alloc] initWithTitle:@"Case 1" message: notification.alertBody delegate:self cancelButtonTitle:@"Ok" otherButtonTitles:nil];
       [aw show];
   }
    
   // Remove the badge number (this should be better in the applicationWillEnterForeground: method)
   application.applicationIconBadgeNumber = 0;
   returnYES;
}



// case 2 and case 3 - AppDelegate.m
// case 2 - only triggered when a user interacts with the notification (taps or slides it)
// case 3 - always triggered


- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
{
   if (application.applicationState == UIApplicationStateInactive)
   {
       // case 2
       UIAlertView *aw = [[UIAlertView alloc] initWithTitle:@"Case 2" message: notification.alertBody delegate:self cancelButtonTitle:@"Ok" otherButtonTitles:nil];
       [aw show];
   }
   else if (application.applicationState == UIApplicationStateActive)
   {
       // case 3
       UIAlertView *aw = [[UIAlertView alloc] initWithTitle:@"Case 3" message: notification.alertBody delegate:self cancelButtonTitle:@"Ok" otherButtonTitles:nil];
       [aw show];
   }     
   application.applicationIconBadgeNumber = notification.applicationIconBadgeNumber - 1;
    
   NSLog(@"%@", notification);
}





Following properties can be used in Local Notifications


// UILocalNotification properties:


//   alertBody - the message displayed in the notification


//   alertAction - if notification is displayed as an alert, this is the label of the action button, if not specified, "Launch" will be used


//   soundName - the name of a sound file (AIFF, CAF or WAV) to be played when the notification appears, if not specified, no sound will be played. To use the default sound UILocalNotificationDefaultSoundName can be provided.


//   userInfo - you can pass an NSDictionary object with additional data to be used by our app after the notification fires (optional)


//   fireDate - an NSDate object specifying the date and time for the local notification to be fired (obligatory)


//   timeZone - an NSTimeZone object. If specified, the fireDate is measured against the user's local time zone, if not against universal time


//   repeatCalendar - the calendar (NSCalendar) the system should refer to when it reschedules a repeating notification


//   repeatInterval - the calendar interval (NSCalendarUnit) at which to reschedule the notification, the default is 0, which means don't repeat


//   alertLaunchImage - will be presented when your app is run or summoned from the background





You can also access all scheduled notification using the scheduledLocalNotifications property (NSArray) of the UIApplication object.


// The notification will be fired as specified and scheduled in the notif
[[UIApplication sharedApplication] scheduleLocalNotification:notif];


// The notification will be fired immediately
// Applications running in the background state can immediately present local notifications
// when there are incoming chats, messages, or updates.
[[UIApplication sharedApplication] presentLocalNotificationNow:notif]



Example Code-



You can also trigger local Notification, when application is running in back ground, Place code below in your App Delegate method.


- (void)applicationDidEnterBackground:(UIApplication *)application
{

   NSDate *alertTime = [[NSDate date]
         dateByAddingTimeInterval:10]; //Setting Time Interval.
   UIApplication* app = [UIApplication sharedApplication];
   UILocalNotification* notifyAlarm = [[UILocalNotification alloc]
         init];
   if (notifyAlarm)
   {
       notifyAlarm.fireDate = alertTime;
       notifyAlarm.timeZone = [NSTimeZone defaultTimeZone];
       notifyAlarm.repeatInterval = 0;
       notifyAlarm.soundName = @"bell_tree.mp3"; //You can use local sound file here
       notifyAlarm.alertBody = @"Staff meeting in 30 minutes";
       [app scheduleLocalNotification:notifyAlarm];
   }
}

To Test ,the application ,run you code. After compiling and linking the application, it will load and run in the iOS Simulator. Once the application has loaded into the iPhone simulator, click on the device home button to place the app into background mode. After 10 seconds have elapsed the notification should appear accompanied by the sound from the audio file.