Envoi de notifications Push sur terminaux iOS avec notnoop/java-apns

Il vous est sûrement arrivé de vouloir envoyer des notifications à un terminal iOS et d’ajouter cette fonctionnalité à votre application cliente. Le but de cet article est de vous présenter dans un premier temps le système de notification APNS d’Apple (Apple Push Notification Service). Puis, dans un second temps de vous présenter la librairie notnoop/java-apns qui nous permettra d’envoyer des notifications à un terminal iOS. Enfin nous verrons comment traiter cette notification côté client iOS.

APNS comment ça marche ?

Les systèmes de notifications fonctionnent tous sur le même principe.

apns-notification-diagramme

Un terminal s’enregistre auprès d’un serveur de gestion de notifications Push (GCM chez Google, APNS chez Apple, MPNS pour Microsoft). Le serveur de gestion de notifications envoie au terminal un numéro unique appelé “push token”. L’application cliente communique, via WebService par exemple, le “push token” à l’application serveur. Ensuite l’application serveur utilisera le push token afin d’identifier le destinataire de la notification lorsqu’il l’enverra au service de notification APNS. Ce dernier notifiera le terminal. Si le terminal est connecté au réseau, alors la notification lui sera envoyée immédiatement. Si le terminal n’est pas connecté au réseau, alors APNS stockera la notification et l’enverra lorsque le terminal se connectera. Une seule notification est sauvegardée et pour une durée maximale de 28 jours.

La transmission de données

La transmission des données entre l’application serveur, APNS et le terminal se fait par des flux JSON sur des connexions sockets TCP. Apple attache beaucoup d’importance à la sécurité. Cependant, la livraison des notifications n’est pas garantie.

Les flux JSON sont cryptés via le protocole TLS. De plus, l’application serveur s’authentifie grâce à un certificat. Ce dernier est attaché à l’application et à un environnement (de développement OU de production). Si votre application serveur crée trop de sockets en peu de temps, cela peut être considéré par Apple comme une attaque de type DoS.

Le flux JSON se compose de trois parties : l’identifiant du destinataire (le push token), la date d’expiration de la notification et le payload. Le payload est un dictionnaire JSON contenant les informations à transmettre. La taille du payload ne peut dépasser les 256 octets. Ce qui contraste avec le système de Google qui a une limite de 4 kilo-octets pour son payload.

Les types de notifications

Il y a différents types de notifications :

  • la bannière : la notification s’affiche dans la barre des notifications

notif_ctr_banner_2x

  • l’alerte : la notification s’affiche dans une popup

notif_ctr_alert_2x

  • le badge : une pastille s’affiche sur l’icône de l’application

notif_ctr_badge_2x

  • le signal sonore : un son est émis à la réception de la notification sous réserve que le fichier audio soit présent sur le terminal

Le choix du type de la notification est défini par le développeur et peut être modifié par l’utilisateur dans le menu “Réglages/Notifications”.

Dans la pratique comment ça se passe ?

Pour la pratique, nous allons voir dans un premier temps comment envoyer des notifications push à l’APNS, puis nous verrons comment gérer la réception de la notification push sur un terminal iOS.

Notre exemple va permettre de prévenir un utilisateur d’un nouveau message à lire. Nous partirons du principe que le message est stocké dans une base. Ce message, représenté par une classe “Message”, se compose d’un identifiant, d’un titre et d’un texte. L’accès à la base et la récupération du message via WebService ne seront pas traités ici. Dans notre base nous avons aussi la liste des utilisateurs avec le “push token” de leur terminal. Un utilisateur est représenté par une classe “Contact”. Côté client, nous afficherons une alerte et un badge.

Pré-requis

Lors de la réalisation du serveur de notification push, il vous faudra :

  • un compte développeur Apple pour pouvoir développer le client iOS et générer les certificats nécessaires
  • un certificat pour l’utilisation du “Apple Push Notification Service” au format “PKCS12” (extension “.p12”) avec son mot de passe

Envoyer une notification à APNS

Notre écriture du serveur de notification push sera faite grâce à la librairie “notnoop/java-apns”. Dans le même style de librairie, vous avez “javaPNS“. Le choix de “notnoop/java-apns” s’est fait par sa simplicité d’utilisation.

Pour utiliser cette librairie, soit vous importez le jar dans votre projet, soit vous modifiez le “pom.xml” de votre projet pour y intégrer la dépendance vers la librairie. Dans le premier cas, vous pourrez télécharger le jar ici. Si vous souhaitez modifier votre “pom.xml”, il faudra y ajouter :

    <dependencies>
        ...
        <dependency>
            <groupId>com.notnoop.apns</groupId>
            <artifactId>apns</artifactId>
            <version>0.2.3</version>
        </dependency>
        ...
    </dependencies>

Le fonctionnement de notre serveur de notifications va être le suivant :

  1. Récupération du message et de ses destinataires en base
  2. Création de la connexion au service APNS
  3. Construction de la notification push en se basant sur le message à envoyer
  4. Envoi de la notification push

Récupération de notre message et des destinataires

Là, c’est vraiment pour l’exemple. Nous utilisons une méthode “find” prenant en paramètre l’identifiant du message à notifier. Cette méthode nous retourne une “HashMap<Message, List>” :

HashMap<Message, List> hmapMessage = messageDa.find(idMessage);

Création de la connexion au service APNS

Ensuite nous allons créer une connexion vers le service APNS. Nous allons utiliser les classes com.notnoop.apns.APNS et com.notnoop.apns.ApnsService. Il y a une chose à savoir lors de la création de la connexion au service APNS, c’est qu’Apple a deux environnements, l’un pour le développement, l’autre pour la production. Il vous faudra un certificat “.p12” et un mot de passe pour chaque environnement.

La connexion au service APNS se fait de la manière suivante :

  • En mode développement :

ApnsService apnsService = APNS.newService() .withCert("le_chemin_vers_le/certificat.p12", "mot_de_passe_du_certificat")     .withSandboxDestination()     .build();

  • En mode production :

ApnsService apnsService  = APNS.newService()     .withCert("le_chemin_vers_le/certificat.p12", "mot_de_passe_du_certificat")     .withProductionDestination()     .build();

La différence est faite par .withProductionDestination() et .withSandboxDestination(). Le premier va permettre d’accéder à l’APNS à l’adresse gateway.push.apple.com sur le port “2195”, et le second utilisera l’APNS de l’adresse gateway.sandbox.push.apple.com sur le même port.

Vous pouvez ensuite appeler la méthode testConnection() de la classe com.notnoop.apns.ApnsService pour contrôler la connexion. En cas de problème, une exception de type com.notnoop.exceptions.NetworkIOException sera remontée.

Construction de la notification push

Maintenant que nous sommes connecté à l’APNS, nous allons construire notre notification. Comme vu dans la partie sur la transmission des données, la notification se compose de trois éléments : le token du destinataire, la date d’expiration et le payload. Ce que nous allons créer est le payload. Pour cela, nous utiliserons un objet com.notnoop.apns.PayloadBuilder dont nous récupérons une instance à partir de la méthode APNS.newPayload(). La création se fait de la manière suivante :

// Constructeur du payload. PayloadBuilder payloadBuilder = APNS.newPayload(); // Nous créons l'alerte en y mettant le texte du message. payloadBuilder.alertBody(message.getTitre()); // Nous ajoutons un bouton "Lire" qui sera en bas à droite de la popup d'alerte. payloadBuilder.actionKey("Lire"); // Nous ajoutons un champs personnalisé pour ajouter l'identifiant de notre message. payloadBuilder.customField("idMessage", message.getId()); // On ajoute le badge. payloadBuilder.badge(1);

Attardons nous un moment sur la structure du payload. Ce dernier est un dictionnaire JSON référencé dans le flux de la notification par la clé “aps”. Il peut contenir les informations suivantes :

  • alert : Chaîne de caractères ou dictionnaire JSON. Si cette propriété est présente, alors le système affichera une popup d’alerte standard. Si la valeur spécifiée est une chaîne de caractères, alors cette dernière sera utilisée comme texte dans la popup. Il est possible d’utiliser un dictionnaire JSON afin de paramétrer la popup d’alerte. Ce dictionnaire pourra comprendre les clés suivantes :
    • body : permettra de définir le texte à afficher.
  • action-loc-key : si une chaîne de caractères est spécifiée, alors 2 boutons sont affichés dans la popup d’alerte.
  • loc-key : clé d’un message dans un fichier “Localizable.string” permettant d’afficher un message en fonction de la langue de l’utilisateur. La clé peut contenir “%@” et “%n$@” afin d’afficher dans le message les variables spécifiées dans loc-args.
  • loc-args : tableau de chaînes de caractères qui seront les arguments du loc-key.
  • launch-image : chemin du fichier de l’image à afficher sur la notification.
  • badge : numéro qui sera affiché dans le badge de l’icône de l’application.
  • sound : chaîne de caractères permettant de définir le nom du fichier contenant le son qui sera joué à la réception de la notification.
  • content-available : utilisé avec la valeur 1, permet d’indiquer la disponibilité de nouveau contenu.

Nous pouvons ajouter au dictionnaire du payload des champs personnalisés. C’est l’utilité du “customFields”. Dans notre payload, nous avons donc :

  • le dictionnaire “alert” avec un “body” qui contiendra le titre de notre message, et un “action-loc-key” qui affichera le bouton “Lire”.
  • un badge à 1
  • un champs personnalisé contenant l’identifiant de notre message

Le flux JSON produit sera :

{ "idMessage": "12", "aps": { "alert": { "body": "Le titre du message à afficher", "action-loc-key": "Lire" }, "badge": 1 } }

Pour rappel, le payload ne peut excéder 256 octets. Nous contrôlons donc sa taille ainsi :

// Si le payload dans sa totalité dépasse 256 octets if (payloadBuilder.isTooLong()) {     // Réduction du message afin de rentrer dans les 256 octets     payloadBuilder = payloadBuilder.shrinkBody(); }

La méthode “shrinkBody()” va réduire le champs “body” afin d’obtenir une taille inférieure ou égale à 256 octets.

Nous validons la construction de notre payload et le récupérons dans une String :

String payload = payloadBuilder.build();

Envoi de la notification push

L’envoi de la notification push se fait par la méthode push(...) de la classe com.notnoop.apns.ApnsService``. Les différents prototypes de cette méthode sont :

ApnsNotification push(String deviceToken, String payload) throws NetworkIOException;

EnhancedApnsNotification push(String deviceToken, String payload, Date expiry) throws NetworkIOException;

Collection push(Collection deviceTokens, String payload) throws NetworkIOException;

Collection<? extends EnhancedApnsNotification> push(Collection deviceTokens, String payload, Date expiry) throws NetworkIOException;

Les deux premières méthodes permettent d’envoyer une notification à l’APNS à destination d’un seul terminal. La deuxième spécifie la date d’expiration.

Les deux dernières méthodes permettent d’envoyer une notification à destination de plusieurs terminaux.

Dans tous les cas, si un problème est détecté, une exception sera levée. De plus, chacune des méthodes retourne un objet de type ApnsNotification ou EnhancedApnsNotification qui représente la notification envoyé à l’APNS.

La librairie “notnoop/java-apns” a l’avantage de nous abolir de tout ce qui est gestion de file d’attente du service APNS, et gestion d’erreurs. Pour chaque notification, on va chercher à récupérer le socket TCP. S’il n’existe pas, il sera créé et, si la création n’est pas possible, une exception est levée. Lorsque le socket est ouvert, on envoie la notification. Si l’envoi échoue, on recommence trois fois. Au bout de trois échecs, on ferme le socket et on passe à la notification suivante. La librairie permet une gestion simplifiée du feedback.

C’est quoi le feedback ?

Le feedback permet de savoir pourquoi l’envoi d’une notification a échoué. Si une date d’expiration est spécifiée lors de l’envoi de la notification, son dépassement n’est pas considéré comme un échec et n’est pas pris en compte par le feedback. Les erreurs de livraison sont, en général, dues aux terminaux sur lesquels l’application a été supprimée.

Lors du développement d’un serveur de notification push, de manière classique, on ouvre un socket dans lequel est écrit un message et un push token. Lorsqu’il y a plusieurs terminaux à notifier, on effectue une boucle. Si un terminal n’est plus enregistré, suite à la suppression de l’application par exemple, APNS s’arrêtera sur le push token invalide et refusera de continuer. Le “Feedback Service” permet de connaître les “push token” invalides avant d’effectuer l’envoi.

Lorsque l’on crée notre connexion à l’APNS avec la librairie “notnoop/java-apns”, la gestion du feedback est activée. Il est possible de récupérer la liste des push token des terminaux qui ne sont plus valides. Cela se fera ainsi :

ApnsService apnsService = APNS.newService()     .withCert("le_chemin_vers_le/certificat.p12", "mot_de_passe_du_certificat")     .withSandboxDestination()     .build(); Map<String, Date> listeTerminauxInvalides = apnsService.getInactiveDevices();

La méthode getInactiveDevices() retourne une liste de clé/valeur. La clé correspond au token du terminal. La valeur correspond à la date à partir de laquelle l’application est considérée comme supprimée du terminal. Il est facile d’effectuer la suppression des terminaux inactifs de la liste des terminaux à notifier.

Et côté client ?

Côté client iOS, nous allons faire deux choses. La première est de faire en sorte que le terminal s’enregistre auprès de l’APNS. La seconde est la gestion de la réception de la notification. Lorsque le terminal est actif, on va recevoir la notification et elle sera traitée en fonction de l’état de l’application. Le traitement de la notification n’est pas effectué par les mêmes méthodes si l’application est lancée, tourne en tâche de fond, ou bien fermée. Les principales modifications de code vont se faire dans le fichier “AppDelegate.m”.

Enregistrement du terminal

Dans un premier temps, nous allons enregistrer le terminal. Lorsqu’un terminal s’enregistre auprès de l’APNS, ce dernier lui fournit un numéro unique, le fameux “push token”. Ce numéro ne lui est pas attribué ad vitam aeternam. Il peut être modifié suite à un changement de l’application ou bien lorsque cette dernière est supprimée puis réinstallée, par exemple.

L’enregistrement se fait via l’appel de la méthode registerForRemoteNotificationTypes de la classe UIApplication. Cela nous donne la ligne de code suivante :

[[UIApplication sharedApplication] registerForRemoteNotificationTypes:(UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeSound)];

Les constantes “UIRemoteNotificationTypeAlert” et “UIRemoteNotificationTypeSound” qui sont passées en paramètre de la méthode précisent le type des notifications attendues. Ici, nous inscrivons notre terminal pour la réception des notifications de type “alert” et “sound”. La notification que nous avons créée précedemment avec le serveur de notification est justement de type “alert”.

Lorsque l’enregistrement s’effectue avec succès, l’APNS retourne le “push token” à notre application par la méthode didRegisterForRemoteNotificationsWithDeviceToken. Nous implémenterons cette méthode afin d’effectuer la sauvegarde du “push token” et sa transmission en base de données via un WebService. Ainsi, dans notre exemple, le serveur de notifications pourra le récupérer dans les objets “Contact”. En cas d’échec de l’enregistrement, la méthode didFailToRegisterForRemoteNotificationsWithError sera appelée. Elle fait donc partie des méthodes à implémenter.

Nous faisons en sorte que la méthode registerForRemoteNotificationTypes soit appelée au lancement de l’application. Son appel se fera dans la méthode didFinishLaunchingWithOptions. Nous allons avoir un code ressemblant à :

    (BOOL) application: (UIApplication) application didFinishLaunchingWithOptions: (NSDictionary) launchOptions { // On appelle la méthode "startApp" effectuant les traitements au démarrage 
    // dont l'enregistrement de notre terminal     
    [self startApp];
    if (launchOptions != nil) { // Si l'on passe ici c'est que l'application est lancé suite à un "tap" ou "slide" 
        // sur la notification. Du coup la notification est passée en paramètre de la méthode. 
        // On récupère l'identifiant du message         
        self.messageId = [
            [launchOptions objectForKey: UIApplicationLaunchOptionsRemoteNotificationKey] objectForKey: @ "idMessage"
        ];
        // On va rechercher le message en base de données via un appel à un WebService. 
        // Vous utiliserez peut-être une autre manière de faire en fonction de votre projet.         [self downloadAndSaveMessageWithId:self.messageId]; 
        // Ensuite on effectue une redirection vers l'affichage du message         
        [self displayMessageDetailWhenNotificationRecieved];
    }
    return YES;
}
/*Méthode appelée lors du démarrage de l'application afin d'effectuer l'enregistrement du terminal. */
(void) startApp {
    // On enregistre le terminal sur les notifications de type "alert" et "sound"     
    [
        [UIApplication sharedApplication] registerForRemoteNotificationTypes: (UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeSound)
    ];
    self.window = [
        [UIWindow alloc] initWithFrame: UIScreen.mainScreen.bounds
    ];
    // Dans notre cas nous effectuons un traitement particulier en fonction du modèle de l'IPhone     
    self.storyboard = [
        [UIStoryboard alloc] init
    ];
    if (IS_IPHONE_5) {
        self.storyboard = [UIStoryboard storyboardWithName: @ "Main_iPhone5"
            bundle: nil
        ];

    } else {
        self.storyboard = [UIStoryboard storyboardWithName: @ "Main"
            bundle: nil
        ];
    }
    UIViewController viewController;
    // Ici nous effectuons un contrôle sur le fait que l'utilisateur s'est enregistré 
    // ou pas sur l'application. J'ai laissé ce code pour vous montrer comment vous 
    // rediriger vers un écran particulier.     
    BOOL isAuthentified = [
        [Sauvegarde instance] isAuthentified
    ];
    if (isAuthentified) {
        viewController = [self.storyboard instantiateViewControllerWithIdentifier: @ "ExpertTabBarControllerId"];

    } else {
        viewController = [self.storyboard instantiateViewControllerWithIdentifier: @ "NavFirstUseViewControllerID"];
    }
    self.window.rootViewController = viewController;
    [self.window makeKeyAndVisible];
}
/* Méthode permettant de récupérer le push token retourné par le service * Apple Push Notification Service. * C'est dans cette méthode que vous enregistrez le push token */
(void) application: (UIApplication * ) app didRegisterForRemoteNotificationsWithDeviceToken: (NSData * ) devToken {
        const void devTokenBytes = [devToken bytes];
        // Récupération du push token     
        NSString pushToken = [
            [devToken description] stringByTrimmingCharactersInSet: [NSCharacterSet characterSetWithCharactersInString: @ "<>"]
        ];
        pushToken = [pushToken stringByReplacingOccurrencesOfString: @ " "
            withString: @ ""
        ];
        // Le push token est ensuite sauvegardé dans un objet "Sauvegarde" 
        // singleton permettant de sauvegarder des données au sein de l'application     
        [
            [Sauvegarde instance] addPushToken: pushToken
        ];
        [
            [Sauvegarde instance] savePushToken
        ];
        NSLog(@ "pushToken = %@", pushToken);
        // Ici c'est à vous de jouer en fonction de votre projet 
        // pour envoyer le push token dans une base de données.     
        //[self sendProviderDeviceToken:devTokenBytes]; } 
        /* Cette méthode est appelée en cas d'échec de l'enregistrement du terminal. * Ici nous ne faisons que logguer l'erreur, mais nous pourrions afficher un * message à l'utilisateur ou effectuer un traitement particulier. */
        (void) application: (UIApplication * ) app didFailToRegisterForRemoteNotificationsWithError: (NSError * ) err {
            NSLog(@ "Error in registration. Error: %@", err);
        }

Réception de la notification Push

Maintenant que nous avons enregistré notre terminal, nous allons gérer la réception de notre notification. Comme nous l’avons vu précédemment, il y a trois cas à prendre en compte :

  1. nous recevons la notification alors que l’application est fermée
  2. nous recevons la notification alors que l’application est en cours d’utilisation
  3. nous recevons la notification alors que l’application est en tâche de fond

Ces trois cas d’utilisations sont gérés par deux méthodes : didFinishLaunchingWithOptions et didReceiveRemoteNotification. La première méthode permet la gestion du cas où l’application est fermée. La seconde méthode permet la gestion des deux autres cas.

Traitons le cas lorsque l’application est fermée

Dans ce cas, lorsque le terminal de l’utilisateur reçoit une notification, elle s’affiche dans le “Notification center” et dans la barre des notifications. Lorsque l’utilisateur “tap” ou “slide” la notification, cela va lancer notre application. Un appel à la méthode didFinishLaunchingWithOptions sera fait avec en paramètre le dictionnaire JSON de notre notification. Nous récupérons l’identifiant du nouveau message. Nous l’utilisons pour récupérer le message en base de données via un appel WebService. Puis nous redirigeons l’utilisateur vers l’écran d’affichage du message. Notre code ressemble à :

(BOOL) application: (UIApplication) application didFinishLaunchingWithOptions: (NSDictionary) launchOptions {
    // On appelle la méthode "startApp" effectuant les traitements au démarrage 
    // dont l'enregistrement de notre terminal     
    [self startApp];
    if (launchOptions != nil) {
        // Si l'on passe ici c'est que l'application est lancé suite à un "tap" ou "slide" 
        // sur la notification. Du coup la notification est passée en paramètre de la méthode. 
        // On récupère l'identifiant du message         
        self.messageId = [
            [launchOptions objectForKey: UIApplicationLaunchOptionsRemoteNotificationKey] objectForKey: @ "idMessage"
        ];
        // On va rechercher le message en base de données via un appel à un WebService. 
        // Vous utiliserez peut être une autre manière de faire en fonction de votre projet.         
        [self downloadAndSaveMessageWithId: self.messageId];
        // Ensuite on effectue une redirection vers l'affichage du message         
        [self displayMessageDetailWhenNotificationRecieved];
    }
    return YES;
}
/* Cette méthode récupère le message à afficher de la base de données * puis le sauvegarde dans notre singleton "Sauvegarde". */
(void) downloadAndSaveMessageWithId: (NSString) idMessage {
    NSString * token = [
        [Sauvegarde instance] getToken
    ];
    [
        [MessageManager sharedManager] loadListeMessagesWithToken: (NSString * ) token withMessageId: (NSString * ) idMessage withSuccess: ^ (NSMutableArray * messages) {
            for (ListeMessages * lMessage in messages) {
                for (Message * message in lMessage.listeMessages) {
                    [Utils downloadAndSaveMessage: message];
                }
            }
            [
                [Sauvegarde instance] saveMessages
            ];
            NSLog(@ "Message id: %@ sauvegardé", idMessage);
        }
        failure: ^ (RKObjectRequestOperation operation, NSError error) {
            NSLog(@ "Echec de la sauvegarde du message id: %@", idMessage);
        }
    ];
}
/* Cette méthode récupère les données du message afin * de valoriser les champs pour l'affichage. On est ensuite * redirigé vers le ViewController gérant cet affichage.  */
(void) displayMessageDetailWhenNotificationRecieved {
    // La notification est considérée comme consommée. 
    // On peut donc décrémenter l'indicateur du badge.     
    [UIApplication sharedApplication].applicationIconBadgeNumber--;
    // On récupère notre message     
    NSDictionary messages = [NSDictionary dictionaryWithDictionary: [
        [Sauvegarde instance] messages
    ]];
    // Notre application utilise une tabBar permettant à l'utilisateur de sélectionner 
    // l'affichage des message.     
    UITabBarController * tabb = (UITabBarController * ) self.window.rootViewController;
    // On récupère le contrôleur affichant le message.     
    DetailMsgExpertControllerViewController * detail = [self.storyboard instantiateViewControllerWithIdentifier: @ "detailMsgViewController"];
    // On valorise les champs avec les données du message     
    NSString * idMessage = self.messageId;
    detail.mTitre = messages[idMessage][@ "titre"];
    detail.mTexte = messages[idMessage][@ "texte"];
    NSDate * date = messages[idMessage][@ "dateMessage"];
    // On formatte la date     
    detail.mDate = [Utils getDateMessageLabel: date];
    // On se dirige vers l'affichage du message à partir de notre tabBar.     
    [(UINavigationController * ) tabb.selectedViewController pushViewController: detail animated: YES];
}

Traitons les cas où l’application est active ou en tâche de fond

Lorsque l’application est active, la notification envoyée au terminal est reçue par la méthode didReceiveRemoteNotification. Dans cette méthode, nous allons créer la popup d’alerte et l’afficher. De plus, nous contrôlerons l’état de l’activité de l’application pour savoir si elle tourne en tâche de fond.

/** * Méthode permettant de gérer la réception d'une notification 
push lorsque l'application est active. * La méthode reçoit en entrée le flux JSON constituant la * notification Push. */
(void) application: (UIApplication * ) application didReceiveRemoteNotification: (NSDictionary * ) notificationInfo {
    NSLog(@ "remote notification: %@", [notificationInfo description]);
    // Récupération du payload     
    NSDictionary * apsInfo = [notificationInfo objectForKey: @ "aps"];
    // Récupération du dictionnaire décrivant l'alerte     
    NSDictionary alert = [apsInfo objectForKey: @ "alert"];
    NSLog(@ "Received Push Alert: %@", alert);
    // On récupère et on sauvegarde notre message     
    self.messageId = [notificationInfo objectForKey: @ "idMessage"];
    [self downloadAndSaveMessageWithId: self.messageId];
    NSLog(@ "Message id : %@", self.messageId);
    // On fera vibrer le terminal     
    AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);
    // On incrémente le badge de 1.     
    NSString badge = [apsInfo objectForKey: @ "badge"];
    NSLog(@ "Received Push Badge: %@", badge);
    [UIApplication sharedApplication].applicationIconBadgeNumber++;
    // On récupère le texte à afficher dans le body de l'alert     
    NSString message = [alert objectForKey: @ "body"];
    // On récupère le libellé du bouton de droite     
    NSString libelleButton = [alert objectForKey: @ "action-loc-key"];
    // On teste si l'état de l'application est actif ou pas     
    if (application.applicationState == UIApplicationStateActive) {
        // L'application est en cours d'utilisation on affiche la popup         
        UIAlertView * alertView = [
            [UIAlertView alloc] initWithTitle: @ "Notification"
            message: message delegate: self cancelButtonTitle: @ "Annuler"
            otherButtonTitles: libelleButton, nil
        ];
        alertView.alertViewStyle = UIAlertViewStyleDefault;
        [alertView show];
    } else {
        // Si l'application tourne en tâche de fond on redirige directement 
        // vers l'écran d'affichage du message.         
        [self displayMessageDetailWhenNotificationRecieved];
    }
}

En conclusion

Nous avons pu voir que l’envoi d’une notification Push grâce à la librairie “notnoop/java-apns” se fait de manière simple. La connexion au service “feedback” est transparente et la récupération des terminaux invalides est très simple. La réception et la gestion de cette notification sur un terminal iOS, sans être triviales pour un débutant, n’est pas compliquée en soi.  “notnoop/java-apns” offre une API simple d’utilisation et facile à mettre en place quelle que soit la taille du projet.

Références :