Xcode: Notifications Push

mardi 28 mai 2013
Pour la mise en place d’un système de notification « push » dans notre application, il faut faire un peu d’administration sur le site d’Apple.

Via votre compte développeur, il faut activer les notifications « push » de votre application. Il faudra aussi fournir un certificat qui identifie le développeur qui enverra les notifications. C’est un certificat un peu particulier car il vous permettra de vous autoriser à envoyer les notifications depuis votre serveur vers le serveur Apns d’Apple. Ce certificat doit aussi être renouvelé chaque année !
Attention: il semblerait qu’il faut spécifier une application sans wildcard pour pouvoir activé les notifications.
En tout cas pour ma part pas possible d’activé pour l’application générique créé par Xcode (wildcard *)

Au final, vous devrez avoir une AppId qui ressemble à ceci: push notification in AppID

Voici maintenant la méthode pour générer les différents certificats /clés privés/ publique nécessaire pour l’envoi des notifications push.

Tout d’abord, il faut créer votre provisionning profile de distribution pour votre application comme tout application normal en spécifiant votre AppID et votre certificat de distribution. Ensuite, il faudra le télécharger dans votre appli ou l’assigner avec Xcode. provisionning profile

Pour terminer, il faut créer le certificat pour vous autoriser à envoyer des notifications. Cette partie est un peu plus fastidieuse:

1. Il faut créer un certificat de distribution de type « Apple Push Notification service SSL (Production) ». 1.push_certificat_prod

2. Sélectionnez votre AppId qui doit être défini comme supportant les notifications push! 2.push_appid_prod

3.Suivez les étapes pour générer votre certificat csr avec l’assistant du keychain 3.push_certificatCSR_prod

4. Télécharger le certificat généré. 5.push_downcertificat_prod

5. Double cliquez sur le certificat téléchargé afin de l’enregistrer dans votre keychain.

6. Recherchez dans votre keychain votre certificat APNS de production. Déployez la clé et exportez les deux clés. 4.push_keychain_prod

CONTONUER LE TUTO ICI http://stackoverflow.com/questions/21250510/generate-pem-file-used-to-setup-apple-push-notification

il faut créer un certificat pour identifier le développeur qui enverra les notifications chez Apple.
Pour cela, il suffit de récupérer votre certificat de développeur avec l’extension .certSigningRequest et de l’uploader sur le serveur.
Voilà vous devez obtenir ceci:
Téléchargez le certificat généré et gardez le bien au chaud car il sera nécessaire pour l’envoi des notifications.

Troisièmement, il faut créer le provisionning profile avec votre application spécifique. Maintenant, il faut l’insérer dans Xcode et l’assigner à votre application dans Xcode et bien mettre le bundle de votre appID dans Xcode pour la bonne correspondance.

Pour pouvoir envoyer des notifications push, il faut bien sûr le certificat qu’on a créé via l’interface d’administration d’Apple.
Il faut récupérer le certificat sur le site d’Apple dans la section AppID > votre application > Notifications Push >certificat ssl. Une fois installé dans votre trousseau de clés en cliquant dessus, vous pouvez l’exporter en .p12. Ensuite, il faut le crypter via openssl et la ligne de commande suivante:

openssl pkcs12 -in apn.p12 -out apn.pem -nodes -clcerts
Voilà , vous obtenez le certificat qui identifiera le compte développeur par lequel les envois de notifications « push » se feront d’où l’importance de le crypter.

En résumé, on créé deux certificats avec notre compte développeur chaque fois pour les applications qui utilisent le push.

  • iOS Development: Certificat de développement ou distribution classique qu’on mettra dans Xcode.
  • APNs Development iOS: Certificat de développement ou distribution pour l’envoi de notification push via notre serveur APNS.
    C’est bien celui-ci qu’on va exporter en .p12 et crypter en ssl .pem.
Quand on créé notre AppId, on oublie pas de spécifier qu’on veut des notifications push (boule verte) et on vérifie que le certificat de développement ou distribution pour les notifications push est en place dans les settings,. (APNs Development iOS). Pour terminer, il reste le provisionning profile de distribution ad hoc a créé et importé dans Xcode pour l’utiliser en mode release.

Idem si on souhaite créer une application OTA pour faciliter l’installation sur plusieurs devices, on créé un provisionning profile de distribution ad hoc avec le certificat de distribution classique.

Maintenant qu’on est prêt pour gérer les notifications, on peut rentrer dans le vif du sujet au niveau du code

Tout d’abord, il faut mettre le code qui permettra à l’utilisateur du device d’accepter les notifications push au moment du lancement de l’application.
Dans l’AppDelegate de l’application, insérez le code suivant dans la fonction didFinishLaunchingWithOptions:

[[UIApplication sharedApplication] registerForRemoteNotificationTypes:(UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound)];
Maintenant, toujours dans l’Appdelegate, il existe trois fonctions importantes pour les notifications:
- (void)application:(UIApplication *)app didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
    NSString *str = [NSString stringWithFormat:@"Device Token=%@",deviceToken];
    NSLog(str);
}
- (void)application:(UIApplication *)app didFailToRegisterForRemoteNotificationsWithError:(NSError *)err {
    NSString *str = [NSString stringWithFormat: @"Error: %@", err];
    NSLog(str); 
}
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
    for (id key in userInfo) {
        NSLog(@"key: %@, value: %@", key, [userInfo objectForKey:key]);
    }    
}
La première fonction permet de récupérer le token du device qui est un identifiant chiffré pour identifié le device ou les devices qui demandent à recevoir les notifications auprès du serveur APNS.
Attention: le token du device change quand on est en mode developpement ou distribution.
La seconde gère le cas d’une erreur lors de l’inscription au service de notification.
La troisième fonction permet de récupérer les informations d’une notification reçue.

Voilà tout est en place pour recevoir des notifications dans notre application.
Qu’en est il pour l’envoi des notifications maintenant….
Pour envoyer des notifications d’un device vers un autre ou d’un serveur vers un device, il faut connaître le token du device qui a accepté les notifications et pour cela, il faut un serveur qui soit toujours connecté pour réceptionner à tout moment ces tokens de gens qui souhaite recevoir les notifications.
Voici une image explicative: APNS

  • 1. Notre device fait une demande vers le service d’Apple de notification pour obtenir notre token unique.
  • 2. Celui-ci nous retourne notre token.
  • 3. Il faut sauver le token sur un serveur, pour avoir la liste de tous les token voulant recevoir les notifications. Ce serveur doit donc stocké en base des données les token des devices.
  • 4. Si la demande d’envoi de notification doit se faire depuis un device, celui-ci doit faire une demande via Webservice par exemple au serveur, pour envoyer la notification.
  • 5. Le serveur Web, qui contient le certificat ssl chiffré qui identifiera le développeur pour envoyer les notifications et la base de données avec tous les token, envoi les notifications au serveur APNS.
  • 6. Le serveur APNS envoi les notifications à chaque device

Pour finir il reste la création de l’application Web qui enverra les demandes de notifications auprès de l’APNS d’Apple.

Voici un exemple de code en php de serveur qui envoi des notifications push encoder en base de données:

while (true)
{
  // SI DE NOUVEAU ENREGISTREMENT EN BD
  if(sizeof($messages) > 0)
  {
     // ON SE CONNECTE
     if (!$this->connectToAPNS())
       exit;
     // POUR CHAQUE NOTIFICATIONS PUSH A ENVOYER
     foreach ($messages as $message)
     {
       // SI LE MESSAGE EST ENVOYER AVEC SUCCES
       if ($this->sendNotification($message->message_id, $message->device_token, $message->payload))
       {
          // ICI ON MET LE FLAG COMME QUOI LE MESSAGE A éTé ENVOYE
       }
       else  // failed to deliver
       {
          $this->reconnectToAPNS();
       }
     }
     $this->disconnectFromAPNS();
   }
   unset($messages);			
   sleep(5);
}

J’ai apporté des modifications au serveur PHP APNS dont je me suis inspiré pour mon projet car dans mon cas, je n’envoyais pas beaucoup de notifications en même temps et qu’il était primordial de recevoir au plus vite la notification et de la recevoir!
De temps en temps, il arrivait que je ne les reçoive pas, j’ai corrigé cela en me connectant au serveur APNS d’Apple uniquement quand il y avait des notifications push et non tout le temps comme c’était par défaut.
Ensuite, je m’assure aussi de bien fermer la connexion après l’envoi de tous les messages.Après ces modifications, je n’ai plus eu de soucis de réception de notification push.

Voici un code pour se connecter à l’APNS d’Apple:

function connectToAPNS()
{
  $ctx = stream_context_create();
  stream_context_set_option($ctx, 'ssl', 'local_cert', $this->certificate);
  stream_context_set_option($ctx, 'ssl', 'passphrase', $this->passphrase);
  $this->fp = stream_socket_client('ssl://' . $this->server, $err, $errstr, 60,
			STREAM_CLIENT_CONNECT|STREAM_CLIENT_PERSISTENT, $ctx);
  if (!$this->fp)
  {
	writeToLog("Failed to connect: $err $errstr");
	return FALSE;
  }
  writeToLog('Connection OK');
  return TRUE;
}

// Drops the connection to the APNS server.
function disconnectFromAPNS()
{
	fclose($this->fp);
	$this->fp = NULL;
}

Ici, vous voyez bien qu’on a besoin du certificat SSL qu’on a créé avec notre appId pour l’envoi des notifications push crypté en .pem précédemment.
La variable $this->server, contient l’adresse vers le serveur APNS d’Apple. celui-ci change si on a un certificat de développement ou de distribution pour les notifications push:
  • Pour le développement: gateway.sandbox.push.apple.com:2195
  • Pour la distribution:gateway.push.apple.com:2195
Voici le code pour envoyer les notifications en php
$msg = chr(0)                       // command (1 byte)
. pack('n', 32)                // token length (2 bytes)
. pack('H*', $deviceToken)     // device token (32 bytes)
. pack('n', strlen($payload))  // payload length (2 bytes)
. $payload;                    // the JSON payload

$result = @fwrite($this->fp, $msg, strlen($msg));

Vous trouverez ici un lien dont je me suis fortement inspiré pour cet article.

Astuce: Si vous n’arrivez toujours pas à recevoir des notifications alors que les logs affirment que tout c’est passé jusqu’à l’envoi chez Apple, il faut savoir que les notifications push sont envoyées via le port 5223 et s’assurer que le port n’est pas bloqué par firewall.

Tags: APNS , notification push , PHP , Xcode