Share via


Tutoriel : Envoyer des notifications Push vers des utilisateurs spécifiques à l’aide d’Azure Notification Hubs

Ce didacticiel explique comment utiliser Azure Notification Hubs pour envoyer des notifications Push à un utilisateur particulier d'une application sur un appareil spécifique. Un code WebAPI principal ASP.NET est utilisé pour authentifier les clients et pour générer les notifications, comme le présente la rubrique d’aide Inscription auprès du serveur principal de votre application.

Dans ce tutoriel, vous effectuez les étapes suivantes :

  • Création du projet WebAPI
  • Authentification des clients sur le serveur principal WebAPI
  • Inscription aux notifications à l’aide du serveur principal WebAPI
  • Envoyer des notifications depuis le serveur principal WebAPI
  • Publication du nouveau serveur principal WebAPI
  • Modification de votre application iOS
  • Test de l’application

Prérequis

Ce didacticiel repose sur l'hypothèse que vous avez créé et configuré votre hub de notification comme décrit dans Envoyer des notification Push à des applications iOS avec Azure Notification Hubs. Ce didacticiel est également un prérequis pour le didacticiel sur les notifications Push sécurisées (iOS) . Si vous souhaitez utiliser Mobile Apps comme service principal, voir l’article Mobile Apps concernant la prise en main des notifications Push.

Création du projet WebAPI

Les sections suivantes traitent de la création d’un serveur principal WebAPI ASP.NET. Ce processus a trois objectifs principaux :

  • Authentification des clients : ajoutez un gestionnaire de messages pour authentifier les demandes des clients et associer l’utilisateur à la demande.
  • Inscriptions aux notifications à l’aide du serveur principal WebAPI : ajoutez un contrôleur pour gérer les nouvelles inscriptions afin qu’un appareil client puisse recevoir des notifications. Le nom d’utilisateur authentifié est automatiquement ajouté à l’inscription en tant que balise.
  • Envoi de notifications aux clients : ajoutez un contrôleur permettant aux utilisateurs de déclencher une notification push sécurisée pour les appareils et les clients associés à la balise.

Pour créer le serveur principal ASP.NET Core 6.0, procédez comme suit :

Pour ce faire, lancez Visual Studio. Dans le menu Outils, sélectionnez Extensions et mises à jour. Recherchez Gestionnaire de package NuGet pour votre version de Visual Studio et vérifiez que vous disposez de la dernière version. Si vous ne disposez pas de la version la plus récente, désinstallez votre version actuelle et réinstallez le Gestionnaire de package NuGet.

Capture d’écran de la boîte de dialogue Extensions et mises à jour avec le package Gestionnaire de package NuGet pour Visual Studio mis en évidence.

Notes

Assurez-vous que le Kit de développement logiciel (SDK) Azure Visual Studio est installé, pour le déploiement du site web.

  1. Démarrez Visual Studio ou Visual Studio Express.

  2. Sélectionnez Explorateur de serveurs et connectez-vous à votre compte Azure. Pour créer les ressources du site web sur votre compte, vous devez être connecté.

  3. Dans Visual Studio, dans le menu Fichier, sélectionnez Nouveau>Projet.

  4. Entrez API web dans le champ de recherche.

  5. Sélectionnez le modèle de projet API web ASP.NET Core, puis Suivant.

  6. Dans la boîte de dialogue Configurer votre nouveau projet, nommez le projet AppBackend, puis sélectionnez Suivant.

  7. Dans la boîte de dialogue Informations supplémentaires :

    • Vérifiez que le framework est .NET 6.0 (prise en charge à long terme).
    • Vérifiez que la case à cocher Utiliser des contrôleurs (décocher pour utiliser les API minimales) est cochée.
    • Décochez Activer la prise en charge d’OpenAPI.
    • Sélectionnez Create (Créer).

Supprimer les fichiers de modèle WeatherForecast

  1. Supprimez les fichiers d’exemple WeatherForecast.cs et Controllers/WeatherForecastController.cs du nouveau projet AppBackend.
  2. Ouvrez Properties\launchSettings.json.
  3. Modifiez les propriétés launchUrl de weatherforcast en appbackend.

Dans la fenêtre Configurer l’application web Microsoft Azure, sélectionnez l’abonnement puis effectuez l’une des actions suivantes dans la liste Plan App Service :

  • Sélectionnez un plan Azure App Service que vous avez déjà créé.
  • Sélectionnez Créer un plan App Service, et créez-en un.

Vous n'avez pas besoin de base de données pour ce didacticiel. Une fois que vous avez sélectionné votre plan App Service, sélectionnez OK pour créer le projet.

Fenêtre Configurer l’application web Microsoft Azure

Si vous ne voyez pas la page Configurer le plan App Service, poursuivez le tutoriel. Vous pourrez le configurer plus tard, lors de la publication de l’application.

Authentification des clients sur le serveur principal WebAPI

Dans cette section, vous créez une classe de gestionnaire de messages nommée AuthenticationTestHandler pour le nouveau serveur principal. Cette classe est dérivée de DelegatingHandler et ajoutée comme gestionnaire de messages afin de pouvoir traiter toutes les demandes entrantes dans le serveur principal.

  1. Dans l’Explorateur de solutions, cliquez avec le bouton droit sur le projet AppBackend, puis sélectionnez Ajouter et Classe.

  2. Nommez la nouvelle classe AuthenticationTestHandler.cs, puis sélectionnez Ajouter pour générer la classe. Par souci de simplification, cette classe permet d’authentifier les utilisateurs via l’ Authentification de base . Votre application peut utiliser n’importe quel schéma d’authentification.

  3. Dans AuthenticationTestHandler.cs, ajoutez les instructions using suivantes :

    using System.Net.Http;
    using System.Threading;
    using System.Security.Principal;
    using System.Net;
    using System.Text;
    using System.Threading.Tasks;
    
  4. Dans AuthenticationTestHandler.cs, remplacez la définition de classe AuthenticationTestHandler par le code suivant :

    Ce gestionnaire autorise la demande lorsque les trois conditions suivantes sont vraies :

    • La demande comprenait un en-tête d’autorisation.
    • La demande utilise l’authentification de base .
    • Les chaînes du nom d’utilisateur et du mot de passe sont identiques.

    Sinon, la demande est rejetée. Cette authentification ne répond pas à une véritable approche d’authentification et d’autorisation. Il s’agit uniquement d’un exemple très simple pour ce didacticiel.

    Si le message de la demande est authentifié et autorisé par AuthenticationTestHandler, l’utilisateur de l’authentification de base est attaché à la demande actuelle sur HttpContext. Les informations utilisateur dans HttpContext sont utilisées ultérieurement par un autre contrôleur (RegisterController) pour ajouter une balise à la demande d’inscription aux notifications.

    public class AuthenticationTestHandler : DelegatingHandler
    {
        protected override Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, CancellationToken cancellationToken)
        {
            var authorizationHeader = request.Headers.GetValues("Authorization").First();
    
            if (authorizationHeader != null && authorizationHeader
                .StartsWith("Basic ", StringComparison.InvariantCultureIgnoreCase))
            {
                string authorizationUserAndPwdBase64 =
                    authorizationHeader.Substring("Basic ".Length);
                string authorizationUserAndPwd = Encoding.Default
                    .GetString(Convert.FromBase64String(authorizationUserAndPwdBase64));
                string user = authorizationUserAndPwd.Split(':')[0];
                string password = authorizationUserAndPwd.Split(':')[1];
    
                if (VerifyUserAndPwd(user, password))
                {
                    // Attach the new principal object to the current HttpContext object
                    HttpContext.Current.User =
                        new GenericPrincipal(new GenericIdentity(user), new string[0]);
                    System.Threading.Thread.CurrentPrincipal =
                        System.Web.HttpContext.Current.User;
                }
                else return Unauthorized();
            }
            else return Unauthorized();
    
            return base.SendAsync(request, cancellationToken);
        }
    
        private bool VerifyUserAndPwd(string user, string password)
        {
            // This is not a real authentication scheme.
            return user == password;
        }
    
        private Task<HttpResponseMessage> Unauthorized()
        {
            var response = new HttpResponseMessage(HttpStatusCode.Forbidden);
            var tsc = new TaskCompletionSource<HttpResponseMessage>();
            tsc.SetResult(response);
            return tsc.Task;
        }
    }
    

    Notes

    Note de sécurité : la classe AuthenticationTestHandler n’offre pas de véritable authentification. Elle sert uniquement à simuler l’authentification de base et n’est pas sécurisée. Vous devez mettre en œuvre un mécanisme d’authentification sécurisé dans vos applications de production et vos services.

  5. Pour enregistrer le gestionnaire de messages, ajoutez le code suivant à la fin de la méthode Register dans le fichier Program.cs :

    config.MessageHandlers.Add(new AuthenticationTestHandler());
    
  6. Enregistrez vos modifications.

Inscription aux notifications à l’aide du serveur principal WebAPI

Dans cette section, vous ajoutez un nouveau contrôleur au serveur principal WebAPI pour gérer les demandes d’inscription d’un utilisateur et d’un appareil aux notifications à l’aide de la bibliothèque cliente pour les hubs de notification. Le contrôleur ajoute une balise d’utilisateur pour l’utilisateur qui a été authentifié et attaché à HttpContext par AuthenticationTestHandler. La balise a le format de chaîne "username:<actual username>".

  1. Dans l’Explorateur de solutions, cliquez avec le bouton droit sur le projet AppBackend, puis sélectionnez Gérer les packages NuGet.

  2. Dans le volet gauche, sélectionnez En ligne puis tapez Microsoft.Azure.NotificationHubs dans la zone Recherche.

  3. Dans la liste de résultats, sélectionnez Microsoft Azure Notification Hubs, puis Installer. Procédez à l’installation, puis fermez la fenêtre du Gestionnaire de package NuGet.

    Cette action ajoute une référence au Kit de développement logiciel (SDK) Azure Notification Hubs à l’aide du package NuGet Microsoft.Azure.Notification Hubs.

  4. Créez un nouveau fichier de classe qui représente la connexion avec le hub de notification utilisé pour envoyer des notifications. Dans l’Explorateur de solutions, cliquez avec le bouton droit sur le dossier Modèles, sélectionnez Ajouter, puis Classe. Nommez la nouvelle classe Notifications.cs, puis sélectionnez Ajouter pour générer la classe.

    Fenêtre Ajouter un nouvel élément

  5. Dans Notifications.cs, ajoutez l’instruction using suivante en haut du fichier :

    using Microsoft.Azure.NotificationHubs;
    
  6. Remplacez la définition de classe Notifications par le code suivant et les deux espaces réservés par la chaîne de connexion (avec accès complet) de votre centre Notification Hubs et le nom du hub (disponible sur le Portail Azure) :

    public class Notifications
    {
        public static Notifications Instance = new Notifications();
    
        public NotificationHubClient Hub { get; set; }
    
        private Notifications() {
            Hub = NotificationHubClient.CreateClientFromConnectionString("<your hub's DefaultFullSharedAccessSignature>",
                                                                            "<hub name>");
        }
    }
    

    Important

    Entrez le nom et la DefaultFullSharedAccessSignature de votre hub avant de continuer.

  7. Ensuite, créez un contrôleur nommé RegisterController. Dans l’Explorateur de solutions, cliquez avec le bouton droit sur le dossier Contrôleurs, sélectionnez Ajouter, puis Contrôleur.

  8. Sélectionnez Contrôleur API - Vide, puis Ajouter.

  9. Dans la boîte Nom du contrôleur, tapez RegisterController pour nommer la nouvelle classe, puis sélectionnez Ajouter.

    Fenêtre Ajouter un contrôleur.

  10. Dans RegisterController.cs, ajoutez les instructions using suivantes :

    using Microsoft.Azure.NotificationHubs;
    using Microsoft.Azure.NotificationHubs.Messaging;
    using AppBackend.Models;
    using System.Threading.Tasks;
    using System.Web;
    
  11. Ajoutez le code suivant dans la définition de classe RegisterController . Dans ce code, vous ajoutez une balise d’utilisateur pour l’utilisateur attaché à HttpContext. L’utilisateur a été authentifié et attaché à HttpContext par le filtre de messages que vous avez ajouté, AuthenticationTestHandler. Vous pouvez également ajouter des contrôles facultatifs afin de vérifier que l’utilisateur dispose des droits d’inscription nécessaires pour les balises requises.

    private NotificationHubClient hub;
    
    public RegisterController()
    {
        hub = Notifications.Instance.Hub;
    }
    
    public class DeviceRegistration
    {
        public string Platform { get; set; }
        public string Handle { get; set; }
        public string[] Tags { get; set; }
    }
    
    // POST api/register
    // This creates a registration id
    public async Task<string> Post(string handle = null)
    {
        string newRegistrationId = null;
    
        // make sure there are no existing registrations for this push handle (used for iOS and Android)
        if (handle != null)
        {
            var registrations = await hub.GetRegistrationsByChannelAsync(handle, 100);
    
            foreach (RegistrationDescription registration in registrations)
            {
                if (newRegistrationId == null)
                {
                    newRegistrationId = registration.RegistrationId;
                }
                else
                {
                    await hub.DeleteRegistrationAsync(registration);
                }
            }
        }
    
        if (newRegistrationId == null) 
            newRegistrationId = await hub.CreateRegistrationIdAsync();
    
        return newRegistrationId;
    }
    
    // PUT api/register/5
    // This creates or updates a registration (with provided channelURI) at the specified id
    public async Task<HttpResponseMessage> Put(string id, DeviceRegistration deviceUpdate)
    {
        RegistrationDescription registration = null;
        switch (deviceUpdate.Platform)
        {
            case "mpns":
                registration = new MpnsRegistrationDescription(deviceUpdate.Handle);
                break;
            case "wns":
                registration = new WindowsRegistrationDescription(deviceUpdate.Handle);
                break;
            case "apns":
                registration = new AppleRegistrationDescription(deviceUpdate.Handle);
                break;
            case "fcm":
                registration = new FcmRegistrationDescription(deviceUpdate.Handle);
                break;
            default:
                throw new HttpResponseException(HttpStatusCode.BadRequest);
        }
    
        registration.RegistrationId = id;
        var username = HttpContext.Current.User.Identity.Name;
    
        // add check if user is allowed to add these tags
        registration.Tags = new HashSet<string>(deviceUpdate.Tags);
        registration.Tags.Add("username:" + username);
    
        try
        {
            await hub.CreateOrUpdateRegistrationAsync(registration);
        }
        catch (MessagingException e)
        {
            ReturnGoneIfHubResponseIsGone(e);
        }
    
        return Request.CreateResponse(HttpStatusCode.OK);
    }
    
    // DELETE api/register/5
    public async Task<HttpResponseMessage> Delete(string id)
    {
        await hub.DeleteRegistrationAsync(id);
        return Request.CreateResponse(HttpStatusCode.OK);
    }
    
    private static void ReturnGoneIfHubResponseIsGone(MessagingException e)
    {
        var webex = e.InnerException as WebException;
        if (webex.Status == WebExceptionStatus.ProtocolError)
        {
            var response = (HttpWebResponse)webex.Response;
            if (response.StatusCode == HttpStatusCode.Gone)
                throw new HttpRequestException(HttpStatusCode.Gone.ToString());
        }
    }
    
  12. Enregistrez vos modifications.

Envoyer des notifications depuis le serveur principal WebAPI

Dans cette section, vous ajoutez un nouveau contrôleur qui montre aux appareils clients comment envoyer une notification. La notification se base sur la balise du nom d’utilisateur qui utilise la bibliothèque .NET du service d’Azure Notification Hubs dans le serveur principal WebAPI ASP.NET.

  1. Créez un nouveau contrôleur nommé NotificationsController, de la même façon que vous avez créé RegisterController dans la section précédente.

  2. Dans NotificationsController.cs, ajoutez les instructions using suivantes :

    using AppBackend.Models;
    using System.Threading.Tasks;
    using System.Web;
    
  3. Ajoutez la méthode suivante à la classe NotificationsController :

    Ce code envoie un type de notification basé sur le paramètre pns du service de notification de plateforme. La valeur de to_tag permet de définir la balise username sur le message. Cette balise doit correspondre à une balise de nom d’utilisateur d’une inscription de hub de notification active. Le message de notification est extrait du corps de la demande POST et mis en forme pour le PNS cible.

    Selon le service Platform Notification Service (PNS) que vos appareils pris en charge utilisent pour recevoir des notifications, ces dernières sont prises en charge dans plusieurs formats. Par exemple sur des appareils Windows, vous pouvez utiliser une notification toast avec WNS qui n’est pas directement prise en charge par un autre service PNS. Dans une instance de ce type, votre serveur principal doit formater la notification dans un format pris en charge pour le service PNS des appareils que vous envisagez de prendre en charge. Utilisez ensuite l’API d’envoi appropriée sur la classe NotificationHubClient.

    public async Task<HttpResponseMessage> Post(string pns, [FromBody]string message, string to_tag)
    {
        var user = HttpContext.Current.User.Identity.Name;
        string[] userTag = new string[2];
        userTag[0] = "username:" + to_tag;
        userTag[1] = "from:" + user;
    
        Microsoft.Azure.NotificationHubs.NotificationOutcome outcome = null;
        HttpStatusCode ret = HttpStatusCode.InternalServerError;
    
        switch (pns.ToLower())
        {
            case "wns":
                // Windows 8.1 / Windows Phone 8.1
                var toast = @"<toast><visual><binding template=""ToastText01""><text id=""1"">" + 
                            "From " + user + ": " + message + "</text></binding></visual></toast>";
                outcome = await Notifications.Instance.Hub.SendWindowsNativeNotificationAsync(toast, userTag);
                break;
            case "apns":
                // iOS
                var alert = "{\"aps\":{\"alert\":\"" + "From " + user + ": " + message + "\"}}";
                outcome = await Notifications.Instance.Hub.SendAppleNativeNotificationAsync(alert, userTag);
                break;
            case "fcm":
                // Android
                var notif = "{ \"data\" : {\"message\":\"" + "From " + user + ": " + message + "\"}}";
                outcome = await Notifications.Instance.Hub.SendFcmNativeNotificationAsync(notif, userTag);
                break;
        }
    
        if (outcome != null)
        {
            if (!((outcome.State == Microsoft.Azure.NotificationHubs.NotificationOutcomeState.Abandoned) ||
                (outcome.State == Microsoft.Azure.NotificationHubs.NotificationOutcomeState.Unknown)))
            {
                ret = HttpStatusCode.OK;
            }
        }
    
        return Request.CreateResponse(ret);
    }
    
  4. Appuyez sur la touche F5 pour exécuter l’application et vérifier le travail que vous avez accompli jusqu’à présent. L’application ouvre un navigateur web, qui s’affiche sur la page d’accueil ASP.NET.

Publication du nouveau serveur principal WebAPI

Ensuite, vous déployez l’application sur un site web Azure afin de la rendre accessible à tous les appareils.

  1. Cliquez avec le bouton droit sur le projet AppBackend, puis sélectionnez Publier.

  2. Sélectionnez Microsoft Azure App Service comme cible de publication, puis cliquez sur \*\*Publier. La fenêtre Créer App Service s’ouvre. Vous pouvez créer toutes les ressources Azure nécessaires pour exécuter l’application web ASP.NET dans Azure.

    Vignette Microsoft Azure App Service

  3. Dans la fenêtre Créer App Service, sélectionnez votre compte Azure. Sélectionnez Modifier le type>Application web. Conservez le nom de l’application web par défaut et sélectionnez l’abonnement, le groupe de ressources et le plan App Service.

  4. Sélectionnez Create (Créer).

  5. Notez la valeur de la propriété URL du site dans la section Résumé. Cette URL constitue le point de terminaison de votre serveur principal plus loin dans ce didacticiel.

  6. Sélectionnez Publier.

Après en avoir terminé avec l’Assistant, il publie l’application web ASP.NET dans Azure, puis ouvre l’application dans le navigateur par défaut. Votre application est visible dans Azure App Services.

L’URL utilise le nom de l’application web que vous avez spécifié précédemment, au format http://<nom_application>.azurewebsites.net.

Modification de votre application iOS

  1. Ouvrez l’application en mode page unique que vous avez créée dans le tutoriel Envoyer des notifications Push vers des applications iOS à l’aide d’Azure Notification Hubs.

    Notes

    Cette section suppose que vous avez configuré votre projet avec un nom d’organisation vide. Si ce n’est pas le cas, ajoutez le nom de votre organisation à tous les noms de classe.

  2. Dans le fichier Main.storyboard, ajoutez les composants indiqués dans la capture d’écran de la bibliothèque d’objets.

    Modifier la table de montage séquentiel dans le générateur d’interface Xcode

    • Nom d’utilisateur : champ UITextField avec du texte d’espace réservé, Entrer le nom d’utilisateur, juste en dessous de l’étiquette des résultats d’envoi et entre les marges gauche et droite, en dessous de l’étiquette des résultats d’envoi.

    • Mot de passe : champ UITextField avec du texte d’espace réservé, Entrer le mot de passe, juste en dessous du champ de texte du nom d’utilisateur et entre les marges gauche et droite, en dessous du champ de texte du nom d’utilisateur. Cochez l'option Sécuriser l'entrée de texte dans l'inspecteur d'attributs sous Touche Retour.

    • Connexion : un UIButton étiqueté immédiatement en dessous du champ de texte du mot de passe ; décochez l’option Activé dans l’inspecteur d’attributs, sous Contrôle-Contenu

    • WNS : étiquette et commutateur pour activer l’envoi de la notification du service de notification Windows, si elle a été configurée sur le hub. Consultez le didacticiel Prise en main de Windows.

    • GCM : étiquette et commutateur pour activer l’envoi de la notification à Google Cloud Messaging, si elle a été configurée sur le hub. Consultez le didacticiel Prise en main d'Android .

    • APNS : étiquette et commutateur pour activer l’envoi de la notification au service de notification de la plateforme Apple.

    • Nom d'utilisateur du destinataire: champ UITextField avec du texte d'espace réservé, Balise du nom d'utilisateur du destinataire, juste en dessous de l'étiquette GCM et entre les marges gauche et droite, en dessous de l'étiquette GCM.

      Certains composants ont été ajoutés au tutoriel Envoyer des notifications Push à des applications iOS avec Azure Notification Hubs.

  3. Faites glisser les composants de l’affichage vers ViewController.h en maintenant la touche Ctrl enfoncée, puis ajoutez ces nouvelles sorties :

    @property (weak, nonatomic) IBOutlet UITextField *UsernameField;
    @property (weak, nonatomic) IBOutlet UITextField *PasswordField;
    @property (weak, nonatomic) IBOutlet UITextField *RecipientField;
    @property (weak, nonatomic) IBOutlet UITextField *NotificationField;
    
    // Used to enable the buttons on the UI
    @property (weak, nonatomic) IBOutlet UIButton *LogInButton;
    @property (weak, nonatomic) IBOutlet UIButton *SendNotificationButton;
    
    // Used to enabled sending notifications across platforms
    @property (weak, nonatomic) IBOutlet UISwitch *WNSSwitch;
    @property (weak, nonatomic) IBOutlet UISwitch *GCMSwitch;
    @property (weak, nonatomic) IBOutlet UISwitch *APNSSwitch;
    
    - (IBAction)LogInAction:(id)sender;
    
  4. Dans ViewController.h, ajoutez le code #define suivant après vos instructions d’importation. Remplacez l’espace réservé <Your backend endpoint> par l’URL de destination que vous avez utilisée pour déployer votre serveur principal d’application dans la section précédente. Par exemple, http://your_backend.azurewebsites.net:

    #define BACKEND_ENDPOINT @"<Your backend endpoint>"
    
  5. Dans votre projet, créez une classe Cocoa Touch nommée RegisterClient pour communiquer avec le serveur principal ASP.NET que vous avez créé. Créez la classe en héritant de NSObject. Ajoutez ensuite le code suivant dans RegisterClient.h :

    @interface RegisterClient : NSObject
    
    @property (strong, nonatomic) NSString* authenticationHeader;
    
    -(void) registerWithDeviceToken:(NSData*)token tags:(NSSet*)tags
        andCompletion:(void(^)(NSError*))completion;
    
    -(instancetype) initWithEndpoint:(NSString*)Endpoint;
    
    @end
    
  6. Dans RegisterClient.m, mettez à jour la section @interface :

    @interface RegisterClient ()
    
    @property (strong, nonatomic) NSURLSession* session;
    @property (strong, nonatomic) NSURLSession* endpoint;
    
    -(void) tryToRegisterWithDeviceToken:(NSData*)token tags:(NSSet*)tags retry:(BOOL)retry
                andCompletion:(void(^)(NSError*))completion;
    -(void) retrieveOrRequestRegistrationIdWithDeviceToken:(NSString*)token
                completion:(void(^)(NSString*, NSError*))completion;
    -(void) upsertRegistrationWithRegistrationId:(NSString*)registrationId deviceToken:(NSString*)token
                tags:(NSSet*)tags andCompletion:(void(^)(NSURLResponse*, NSError*))completion;
    
    @end
    
  7. Remplacez la section @implementation dans RegisterClient.m par le code suivant :

    @implementation RegisterClient
    
    // Globals used by RegisterClient
    NSString *const RegistrationIdLocalStorageKey = @"RegistrationId";
    
    -(instancetype) initWithEndpoint:(NSString*)Endpoint
    {
        self = [super init];
        if (self) {
            NSURLSessionConfiguration* config = [NSURLSessionConfiguration defaultSessionConfiguration];
            _session = [NSURLSession sessionWithConfiguration:config delegate:nil delegateQueue:nil];
            _endpoint = Endpoint;
        }
        return self;
    }
    
    -(void) registerWithDeviceToken:(NSData*)token tags:(NSSet*)tags
                andCompletion:(void(^)(NSError*))completion
    {
        [self tryToRegisterWithDeviceToken:token tags:tags retry:YES andCompletion:completion];
    }
    
    -(void) tryToRegisterWithDeviceToken:(NSData*)token tags:(NSSet*)tags retry:(BOOL)retry
                andCompletion:(void(^)(NSError*))completion
    {
        NSSet* tagsSet = tags?tags:[[NSSet alloc] init];
    
        NSString *deviceTokenString = [[token description]
            stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"<>"]];
        deviceTokenString = [[deviceTokenString stringByReplacingOccurrencesOfString:@" " withString:@""]
                                uppercaseString];
    
        [self retrieveOrRequestRegistrationIdWithDeviceToken: deviceTokenString
            completion:^(NSString* registrationId, NSError *error) {
            NSLog(@"regId: %@", registrationId);
            if (error) {
                completion(error);
                return;
            }
    
            [self upsertRegistrationWithRegistrationId:registrationId deviceToken:deviceTokenString
                tags:tagsSet andCompletion:^(NSURLResponse * response, NSError *error) {
                if (error) {
                    completion(error);
                    return;
                }
    
                NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)response;
                if (httpResponse.statusCode == 200) {
                    completion(nil);
                } else if (httpResponse.statusCode == 410 && retry) {
                    [self tryToRegisterWithDeviceToken:token tags:tags retry:NO andCompletion:completion];
                } else {
                    NSLog(@"Registration error with response status: %ld", (long)httpResponse.statusCode);
    
                    completion([NSError errorWithDomain:@"Registration" code:httpResponse.statusCode
                                userInfo:nil]);
                }
    
            }];
        }];
    }
    
    -(void) upsertRegistrationWithRegistrationId:(NSString*)registrationId deviceToken:(NSData*)token
                tags:(NSSet*)tags andCompletion:(void(^)(NSURLResponse*, NSError*))completion
    {
        NSDictionary* deviceRegistration = @{@"Platform" : @"apns", @"Handle": token,
                                                @"Tags": [tags allObjects]};
        NSData* jsonData = [NSJSONSerialization dataWithJSONObject:deviceRegistration
                            options:NSJSONWritingPrettyPrinted error:nil];
    
        NSLog(@"JSON registration: %@", [[NSString alloc] initWithData:jsonData
                                            encoding:NSUTF8StringEncoding]);
    
        NSString* endpoint = [NSString stringWithFormat:@"%@/api/register/%@", _endpoint,
                                registrationId];
        NSURL* requestURL = [NSURL URLWithString:endpoint];
        NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:requestURL];
        [request setHTTPMethod:@"PUT"];
        [request setHTTPBody:jsonData];
        NSString* authorizationHeaderValue = [NSString stringWithFormat:@"Basic %@",
                                                self.authenticationHeader];
        [request setValue:authorizationHeaderValue forHTTPHeaderField:@"Authorization"];
        [request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
    
        NSURLSessionDataTask* dataTask = [self.session dataTaskWithRequest:request
            completionHandler:^(NSData *data, NSURLResponse *response, NSError *error)
        {
            if (!error)
            {
                completion(response, error);
            }
            else
            {
                NSLog(@"Error request: %@", error);
                completion(nil, error);
            }
        }];
        [dataTask resume];
    }
    
    -(void) retrieveOrRequestRegistrationIdWithDeviceToken:(NSString*)token
                completion:(void(^)(NSString*, NSError*))completion
    {
        NSString* registrationId = [[NSUserDefaults standardUserDefaults]
                                    objectForKey:RegistrationIdLocalStorageKey];
    
        if (registrationId)
        {
            completion(registrationId, nil);
            return;
        }
    
        // request new one & save
        NSURL* requestURL = [NSURL URLWithString:[NSString stringWithFormat:@"%@/api/register?handle=%@",
                                _endpoint, token]];
        NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:requestURL];
        [request setHTTPMethod:@"POST"];
        NSString* authorizationHeaderValue = [NSString stringWithFormat:@"Basic %@",
                                                self.authenticationHeader];
        [request setValue:authorizationHeaderValue forHTTPHeaderField:@"Authorization"];
    
        NSURLSessionDataTask* dataTask = [self.session dataTaskWithRequest:request
            completionHandler:^(NSData *data, NSURLResponse *response, NSError *error)
        {
            NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*) response;
            if (!error && httpResponse.statusCode == 200)
            {
                NSString* registrationId = [[NSString alloc] initWithData:data
                    encoding:NSUTF8StringEncoding];
    
                // remove quotes
                registrationId = [registrationId substringWithRange:NSMakeRange(1,
                                    [registrationId length]-2)];
    
                [[NSUserDefaults standardUserDefaults] setObject:registrationId
                    forKey:RegistrationIdLocalStorageKey];
                [[NSUserDefaults standardUserDefaults] synchronize];
    
                completion(registrationId, nil);
            }
            else
            {
                NSLog(@"Error status: %ld, request: %@", (long)httpResponse.statusCode, error);
                if (error)
                    completion(nil, error);
                else {
                    completion(nil, [NSError errorWithDomain:@"Registration" code:httpResponse.statusCode
                                userInfo:nil]);
                }
            }
        }];
        [dataTask resume];
    }
    
    @end
    

    Ce code implémente la logique expliquée dans l'article Inscription auprès du serveur principal de votre application , et utilise NSURLSession pour passer des appels REST à votre serveur principal d'application et NSUserDefaults pour stocker en local la valeur registrationId renvoyée par le concentrateur de notification.

    Cette classe a besoin que sa propriété authorizationHeader soit définie afin de fonctionner correctement. Cette propriété est définie par la classe ViewController après la connexion.

  8. Dans ViewController.h, ajoutez une instruction #import pour RegisterClient.h. Ensuite, ajoutez une déclaration pour le jeton de l'appareil et faites référence à une instance RegisterClient dans la section @interface :

    #import "RegisterClient.h"
    
    @property (strong, nonatomic) NSData* deviceToken;
    @property (strong, nonatomic) RegisterClient* registerClient;
    
  9. Dans ViewController.m, ajoutez une déclaration de méthode privée dans la section @interface :

    @interface ViewController () <UITextFieldDelegate, NSURLConnectionDataDelegate, NSXMLParserDelegate>
    
    // create the Authorization header to perform Basic authentication with your app back-end
    -(void) createAndSetAuthenticationHeaderWithUsername:(NSString*)username
                    AndPassword:(NSString*)password;
    
    @end
    

    Notes

    L’extrait de code suivant n’étant pas un schéma d’authentification sécurisé, vous devez remplacer l’implémentation de createAndSetAuthenticationHeaderWithUsername:AndPassword: par votre mécanisme d’authentification spécifique qui génère un jeton d’authentification devant être consommé par la classe client du registre, par exemple, OAuth, Active Directory.

  10. Ensuite, dans la section @implementation de ViewController.m, ajoutez le code suivant qui ajoute l'implémentation pour la définition du jeton de l'appareil et de l'en-tête de l'authentification.

    -(void) setDeviceToken: (NSData*) deviceToken
    {
        _deviceToken = deviceToken;
        self.LogInButton.enabled = YES;
    }
    
    -(void) createAndSetAuthenticationHeaderWithUsername:(NSString*)username
                    AndPassword:(NSString*)password;
    {
        NSString* headerValue = [NSString stringWithFormat:@"%@:%@", username, password];
    
        NSData* encodedData = [[headerValue dataUsingEncoding:NSUTF8StringEncoding] base64EncodedDataWithOptions:NSDataBase64EncodingEndLineWithCarriageReturn];
    
        self.registerClient.authenticationHeader = [[NSString alloc] initWithData:encodedData
                                                    encoding:NSUTF8StringEncoding];
    }
    
    -(BOOL)textFieldShouldReturn:(UITextField *)textField
    {
        [textField resignFirstResponder];
        return YES;
    }
    

    Remarquez la façon dont le jeton de l’appareil active le bouton Connexion. Ceci est dû au fait que dans le cadre de l’action de connexion, le contrôleur d’affichage s’inscrit aux notifications Push auprès du serveur principal d’application. Vous ne voulez pas que l’action Se connecter soit accessible tant que le jeton de l’appareil n’a pas été correctement configuré. Vous pouvez découpler la connexion de l’inscription aux notifications Push dans la mesure où la connexion se produit avant l’inscription.

  11. Dans ViewController.m, utilisez les extraits de code suivants pour implémenter la méthode d'action de votre bouton Connexion et une méthode pour envoyer le message de notification à l'aide du serveur principal ASP.NET.

    - (IBAction)LogInAction:(id)sender {
        // create authentication header and set it in register client
        NSString* username = self.UsernameField.text;
        NSString* password = self.PasswordField.text;
    
        [self createAndSetAuthenticationHeaderWithUsername:username AndPassword:password];
    
        __weak ViewController* selfie = self;
        [self.registerClient registerWithDeviceToken:self.deviceToken tags:nil
            andCompletion:^(NSError* error) {
            if (!error) {
                dispatch_async(dispatch_get_main_queue(),
                ^{
                    selfie.SendNotificationButton.enabled = YES;
                    [self MessageBox:@"Success" message:@"Registered successfully!"];
                });
            }
        }];
    }
    
    - (void)SendNotificationASPNETBackend:(NSString*)pns UsernameTag:(NSString*)usernameTag
                Message:(NSString*)message
    {
        NSURLSession* session = [NSURLSession
            sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:nil
            delegateQueue:nil];
    
        // Pass the pns and username tag as parameters with the REST URL to the ASP.NET backend
        NSURL* requestURL = [NSURL URLWithString:[NSString
            stringWithFormat:@"%@/api/notifications?pns=%@&to_tag=%@", BACKEND_ENDPOINT, pns,
            usernameTag]];
    
        NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:requestURL];
        [request setHTTPMethod:@"POST"];
    
        // Get the mock authenticationheader from the register client
        NSString* authorizationHeaderValue = [NSString stringWithFormat:@"Basic %@",
            self.registerClient.authenticationHeader];
        [request setValue:authorizationHeaderValue forHTTPHeaderField:@"Authorization"];
    
        //Add the notification message body
        [request setValue:@"application/json;charset=utf-8" forHTTPHeaderField:@"Content-Type"];
        [request setHTTPBody:[message dataUsingEncoding:NSUTF8StringEncoding]];
    
        // Execute the send notification REST API on the ASP.NET Backend
        NSURLSessionDataTask* dataTask = [session dataTaskWithRequest:request
            completionHandler:^(NSData *data, NSURLResponse *response, NSError *error)
        {
            NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*) response;
            if (error || httpResponse.statusCode != 200)
            {
                NSString* status = [NSString stringWithFormat:@"Error Status for %@: %d\nError: %@\n",
                                    pns, httpResponse.statusCode, error];
                dispatch_async(dispatch_get_main_queue(),
                ^{
                    // Append text because all 3 PNS calls may also have information to view
                    [self.sendResults setText:[self.sendResults.text stringByAppendingString:status]];
                });
                NSLog(status);
            }
    
            if (data != NULL)
            {
                xmlParser = [[NSXMLParser alloc] initWithData:data];
                [xmlParser setDelegate:self];
                [xmlParser parse];
            }
        }];
        [dataTask resume];
    }
    
  12. Mettez à jour l'action du bouton Envoyer une notification pour utiliser le serveur principal ASP.NET et effectuer l'envoi vers n'importe quel PNS activé par un commutateur.

    - (IBAction)SendNotificationMessage:(id)sender
    {
        //[self SendNotificationRESTAPI];
        [self SendToEnabledPlatforms];
    }
    
    -(void)SendToEnabledPlatforms
    {
        NSString* json = [NSString stringWithFormat:@"\"%@\"",self.notificationMessage.text];
    
        [self.sendResults setText:@""];
    
        if ([self.WNSSwitch isOn])
            [self SendNotificationASPNETBackend:@"wns" UsernameTag:self.RecipientField.text Message:json];
    
        if ([self.GCMSwitch isOn])
            [self SendNotificationASPNETBackend:@"gcm" UsernameTag:self.RecipientField.text Message:json];
    
        if ([self.APNSSwitch isOn])
            [self SendNotificationASPNETBackend:@"apns" UsernameTag:self.RecipientField.text Message:json];
    }
    
  13. Dans la fonction ViewDidLoad, ajoutez ce qui suit pour instancier l’instance de RegisterClient et définir le délégué pour vos champs de texte.

    self.UsernameField.delegate = self;
    self.PasswordField.delegate = self;
    self.RecipientField.delegate = self;
    self.registerClient = [[RegisterClient alloc] initWithEndpoint:BACKEND_ENDPOINT];
    
  14. À présent, dans AppDelegate.m, supprimez tout le contenu de la méthode application:didRegisterForPushNotificationWithDeviceToken: et remplacez-le par ce qui suit (pour que le contrôleur d’affichage contienne le dernier jeton de l’appareil extrait d’APNs) :

    // Add import to the top of the file
    #import "ViewController.h"
    
    - (void)application:(UIApplication *)application
                didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
    {
        ViewController* rvc = (ViewController*) self.window.rootViewController;
        rvc.deviceToken = deviceToken;
    }
    
  15. Enfin, assurez-vous qu’AppDelegate.m contient la méthode suivante :

    - (void)application:(UIApplication *)application didReceiveRemoteNotification: (NSDictionary *)userInfo {
        NSLog(@"%@", userInfo);
        [self MessageBox:@"Notification" message:[[userInfo objectForKey:@"aps"] valueForKey:@"alert"]];
    }
    

Test de l’application

  1. Dans XCode, exécutez l’application sur un appareil iOS physique (les notifications Push ne fonctionnent pas dans le simulateur).

  2. Dans l'interface utilisateur de l'application iOS, entrez la même valeur pour le nom d'utilisateur et le mot de passe. Cliquez ensuite sur Log In.

    Application de test iOS

  3. Une fenêtre contextuelle doit s’afficher pour vous informer que l’inscription a abouti. Cliquez sur OK.

    Notification de test iOS affichée

  4. Dans le champ de texte *Balise de nom d’utilisateur du destinataire , entrez la balise de nom d’utilisateur utilisée lors de l’enregistrement sur un autre appareil.

  5. Entrez un message de notification et cliquez sur Envoyer une notification. Seuls les appareils qui disposent d'un enregistrement avec la balise de nom d'utilisateur du destinataire reçoivent le message de notification. Il n'est envoyé qu'à ces utilisateurs.

    Notifications de test avec balises iOS

Étapes suivantes

Dans ce tutoriel, vous avez appris à envoyer des notifications Push à des utilisateurs spécifiques ayant des balises associées à leurs enregistrements. Pour savoir comment envoyer des notifications basées sur l’emplacement, passez au tutoriel suivant :