Tutorial: enviar notificações por push para usuários específicos usando os Hubs de Notificação do Azure

Este tutorial mostra como usar os Hubs de Notificação do Azure para enviar notificações por push a um usuário específico do aplicativo em um dispositivo específico. Um back-end da API Web ASP.NET é usado para autenticar clientes e gerar notificações, conforme mostrado no tópico de diretrizes Registrando-se por meio do back-end do aplicativo.

Neste tutorial, você deve executar as seguintes etapas:

  • Criar o projeto WebAPI
  • Autenticar clientes para o back-end da WebAPI
  • Registrar para receber notificações usando o back-end da WebAPI
  • Enviar notificações do back-end da WebAPI
  • Publicar o novo back-end da API Web
  • Modificar seu aplicativo iOS
  • Testar o aplicativo

Pré-requisitos

Este tutorial pressupõe que você criou e configurou seu hub de notificação como descrito em Enviar notificações por push para aplicativos iOS usando os Hubs de Notificações do Azure. Este tutorial também é um pré-requisito para o tutorial Push Seguro (iOS) . Se desejar usar os Aplicativos Móveis como seu serviço de back-end, veja Mobile Apps Get Started with Push.

Criar o projeto WebAPI

As seções a seguir abordam a criação de um novo back-end WebAPI ASP.NET. Esse processo tem três objetivos principais:

  • Autenticar clientes: adicione um manipulador de mensagens para autenticar solicitações de cliente e associar o usuário à solicitação.
  • Registrar para receber notificações usando o back-end WebAPI: adicione um controlador para lidar com novos registros para que um dispositivos clientes recebam notificações. O nome de usuário autenticado é automaticamente adicionado ao registro como uma marca.
  • Enviar notificações aos clientes: adicione um controlador para permitir aos usuários disparar um envio por push seguro para dispositivos e clientes associados à marca.

Crie o novo back-end da API Web do ASP.NET 6.0 Core executando estas ações:

Para verificar, inicie o Visual Studio. No menu Ferramentas, selecione Extensões e Atualizações. Pesquise por Gerenciador de Pacotes NuGet na sua versão do Visual Studio e verifique se a versão mais recente está instalada. Se a versão não for a versão mais recente, desinstale-a e reinstale o Gerenciador de Pacotes NuGet.

Captura de tela da caixa de diálogo Extensões e Atualizações, com o gerenciamento do Pacote NuGet para o Pacote do Visual Studio realçado.

Observação

Verifique se você instalou o SDK do Azure do Visual Studio para implantação de site.

  1. Inicie o Visual Studio ou o Visual Studio Express.

  2. Selecione Gerenciador de Servidores e entre na sua conta do Azure. Para criar os recursos de site na sua conta, você precisará estar conectado.

  3. No menu Arquivo do Visual Studio, selecione Novo>Projeto.

  4. Insira API Web na caixa de pesquisa.

  5. Selecione o modelo do projeto da API Web do ASP.NET Core e, em seguida, Avançar.

  6. Na caixa de diálogo Configurar o novo projeto, nomeie o projeto AppBackend e selecione Avançar.

  7. Na caixa de diálogo Informações adicionais:

    • Confirme se o Framework é o .NET 6.0 (suporte de longo prazo).
    • Confirme se a caixa de seleção para Usar controladores (desmarcar para usar APIs mínimas) está marcada.
    • Desmarque Habilitar suporte a OpenAPI.
    • Selecione Criar.

Remover os arquivos de modelo WeatherForecast

  1. Remova os arquivos de exemplo WeatherForecast.cs e Controllers/WeatherForecastController.cs do novo projeto AppBackend.
  2. Abra Properties\launchSettings.json.
  3. Altere as propriedades launchUrl de weatherforcast para appbackend.

Na janela Configurar o Aplicativo Web do Microsoft Azure, selecione uma assinatura e, na lista Plano do Serviço de Aplicativo, execute uma destas ações:

  • Selecione um plano do Serviço de Aplicativo do Azure que você já criou.
  • Selecione Criar um novo plano do serviço de aplicativo para criar um.

Não é necessário um banco de dados para este tutorial. Depois que você tiver selecionado o seu plano de serviço de aplicativo, selecione OK para criar o projeto.

A janela do aplicativo Web do Microsoft Azure

Se você não vir essa página para configurar o plano do serviço de aplicativo, continue com o tutorial. Você pode configurá-la ao publicar o aplicativo mais tarde.

Autenticar clientes para o back-end da WebAPI

Nesta seção, você cria uma nova classe de manipulador de mensagens denominada AuthenticationTestHandler para o novo back-end. Essa classe é derivada de DelegatingHandler e adicionada como um manipulador de mensagens para poder processar todas as solicitações que chegam ao back-end.

  1. No Gerenciador de Soluções, clique com botão direito do mouse no projeto AppBackend, selecione Adicionare selecione Classe.

  2. Nomeie a nova classe AuthenticationTestHandler.cs e selecione Adicionar para gerar a classe. Essa classe usa Autenticação Básica para manter a simplicidade na autenticação dos usuários. Seu aplicativo pode utilizar qualquer esquema de autenticação.

  3. Em AuthenticationTestHandler.cs, adicione as seguintes instruções using:

    using System.Net.Http;
    using System.Threading;
    using System.Security.Principal;
    using System.Net;
    using System.Text;
    using System.Threading.Tasks;
    
  4. Em AuthenticationTestHandler.cs, substitua a definição da classe AuthenticationTestHandler pelo código a seguir:

    Esse manipulador autoriza a solicitação quando as três seguintes condições a seguir forem verdadeiras:

    • A solicitação inclui um cabeçalho de Autorização.
    • A solicitação usa a autenticação básica .
    • A cadeia de caracteres de nome de usuário e a cadeia de caracteres de senha são iguais.

    Caso contrário, a solicitação é rejeitada. Essa não é uma abordagem de autenticação e autorização verdadeira. É apenas um exemplo simples para este tutorial.

    Se a mensagem de solicitação for autenticada e autorizada pelo AuthenticationTestHandler, o usuário de autenticação básica será anexado à solicitação atual no HttpContext. As informações do usuário no HttpContext serão usadas por outro controlador (RegisterController) posteriormente para adicionar uma marca à solicitação de registro de notificação.

    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;
        }
    }
    

    Observação

    Nota de segurança: a classe AuthenticationTestHandler não oferece autenticação verdadeira. Ela é usada somente para imitar a autenticação básica e não é segura. Você deve implementar um mecanismo de autenticação seguro em seus aplicativos e serviços de produção.

  5. Para registrar o manipulador de mensagens, adicione o seguinte código ao final do método Register no arquivo Program.cs:

    config.MessageHandlers.Add(new AuthenticationTestHandler());
    
  6. Salve suas alterações.

Registrar para receber notificações usando o back-end da WebAPI

Nesta seção, você adiciona um novo controlador ao back-end WebAPI para manipular solicitações e registrar um usuário e um dispositivo para notificações usando a biblioteca de cliente dos hubs de notificação. O controlador adiciona uma marca de usuário ao usuário que foi autenticado e anexado a HttpContext pelo AuthenticationTestHandler. A marca tem o formato de cadeia de caracteres, "username:<actual username>".

  1. No Gerenciador de Soluções, clique com o botão direito do mouse no projeto AppBackend e selecione Gerenciar Pacotes NuGet.

  2. No painel esquerdo, selecione Online e, na caixa Pesquisa, digite Microsoft.Azure.NotificationHubs.

  3. Na lista de resultados, selecione Hubs de Notificação do Microsoft Azure e selecione Instalar. Conclua a instalação e, por fim, feche a janela Gerenciador de Pacotes NuGet.

    Essa ação adiciona uma referência ao SDK dos Hubs de Notificação do Azure usando o pacote NuGet Microsoft.Azure.Notification Hubs.

  4. Crie um novo arquivo de classe que representa a conexão com o hub de notificação usado para enviar notificações. No Gerenciador de Soluções, clique com o botão direito do mouse na pasta Modelos, selecione Adicionar e Classe. Nomeie a nova classe como Notifications.cs e selecione Adicionar para gerar a classe.

    A janela Adicionar Novo Item

  5. Em Notifications.cs, adicione a seguinte instrução using à parte superior do arquivo:

    using Microsoft.Azure.NotificationHubs;
    
  6. Substitua a definição da classe Notifications pelo seguinte e substitua os dois espaços reservados pela cadeia de conexão (com acesso completo) para o hub de notificação e o nome do hub (disponível no portal do 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>");
        }
    }
    

    Importante

    Insira o nome e a DefaultFullSharedAccessSignature do seu hub antes de prosseguir.

  7. Em seguida, crie um novo controlador chamado RegisterController. No Gerenciador de Soluções, clique com o botão direito do mouse na pasta Controladores, selecione Adicionar e Controlador.

  8. Selecione Controlador da API – Vazio e Adicionar.

  9. Na caixa Nome do controlador, digite RegisterController para nomear a nova classe e selecione Adicionar.

    A janela Adicionar Controlador.

  10. Em RegisterController.cs, adicione as seguintes instruções using :

    using Microsoft.Azure.NotificationHubs;
    using Microsoft.Azure.NotificationHubs.Messaging;
    using AppBackend.Models;
    using System.Threading.Tasks;
    using System.Web;
    
  11. Adicione o código a seguir à definição de classe RegisterController . Nesse código, você adiciona uma marca de usuário para o usuário anexado a HttpContext. O usuário foi autenticado e anexado a HttpContext pelo filtro de mensagens que você adicionou, AuthenticationTestHandler. Você também pode adicionar verificações opcionais para conferir se o usuário tem direitos para registro das tags requeridas.

    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. Salve suas alterações.

Enviar notificações do back-end da WebAPI

Nesta seção, você adiciona um novo controlador que expõe uma maneira de os dispositivos clientes enviarem uma notificação. A notificação se baseia na marca de nome de usuário que usa a Biblioteca .NET dos Hubs de Notificação do Azure no back-end WebAPI ASP.NET.

  1. Crie outro novo controlador chamado NotificationsController da mesma maneira que você criou RegisterController na seção anterior.

  2. Em NotificationsController.cs, adicione as seguintes instruções using :

    using AppBackend.Models;
    using System.Threading.Tasks;
    using System.Web;
    
  3. Adicione o seguinte método à classe NotificationsController:

    Esse código envia um tipo de notificação com base no parâmetro pns do PNS (Platform Notification Service). O valor de to_tag é usado para definir a marca username na mensagem. Essa marca deve corresponder a uma marca de nome de usuário de um registro de hub de notificação ativo. A mensagem de notificação é recuperada do corpo da solicitação POST e formatada para o PNS de destino.

    Dependendo do PNS que seus dispositivos com suporte usam para receber notificações, as notificações têm suporte por vários formatos diferentes. Por exemplo, em dispositivos do Windows, você pode usar uma notificação do sistema com WNS que não tenha suporte direto de outro PNS. Nesse caso, o back-end precisa formatar a notificação em uma notificação com suporte para o PNS de dispositivos aos quais você planeja dar suporte. Em seguida, use a API de envio apropriada na 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. Para executar o aplicativo e garantir a precisão de seu trabalho até aqui, selecione a tecla F5. O aplicativo abre um navegador da Web e será exibido na home page do ASP.NET.

Publicar o novo back-end da API Web

Em seguida, implante o aplicativo em um site do Azure para poder ser acessado por todos os dispositivos.

  1. Clique com o botão direito do mouse no projeto AppBackend e selecione Publicar.

  2. Escolha Serviço de Aplicativo do Microsoft Azure como destino de publicação e selecione **Publicar. A janela Criar Serviço de Aplicativo é aberta. Aqui, você pode criar todos os recursos do Azure necessários para executar o aplicativo Web ASP.NET no Azure.

    O bloco Serviço de Aplicativo do Azure

  3. Na janela Criar Serviço de Aplicativo, selecione sua conta do Azure. Selecione Alterar Tipo>Aplicativo Web. Mantenha o Nome do Aplicativo Web padrão e selecione a Assinatura, o Grupo de Recursos e o Plano do Serviço de Aplicativo.

  4. Selecione Criar.

  5. Anote a propriedade URL do Site na seção Resumo. Essa URL será seu ponto de extremidade de back-end mais adiante no tutorial.

  6. Selecione Publicar.

Depois de concluir o assistente, ele publica o aplicativo Web ASP.NET no Azure e abre o aplicativo no navegador padrão. Seu aplicativo pode ser exibido nos Serviços de Aplicativo do Azure.

A URL usa o nome do aplicativo Web especificado anteriormente, com o formato http://<app_name>.azurewebsites.net.

Modificar seu aplicativo iOS

  1. Abra o aplicativo de exibição de Página Única que você criou no tutorial Enviar notificações por push para aplicativos iOS usando Hubs de Notificação do Azure.

    Observação

    Nesta seção, presumimos que você configurou seu projeto com um nome de organização vazio. Caso contrário, preceda o nome da sua organização a todos os nomes de classe.

  2. No arquivo Main.storyboard, adicione os componentes mostrados na captura de tela da biblioteca de objetos.

    Editar o storyboard no Xcode Interface Builder

    • Nome de usuário: um UITextField com texto de espaço reservado, Inserir nome de usuário, imediatamente abaixo de enviar rótulo de resultados e restrito às margens esquerda e direita e abaixo do rótulo de resultados de envio.

    • Senha: um UITextField com texto de espaço reservado, Digite a senha, imediatamente abaixo do campo de texto Nome de usuário e restrito às margens esquerda e direita e abaixo do campo de texto Nome de usuário. Verifique a opção Entrada de texto seguro no Inspetor de atributo, em Retornar chave.

    • Fazer logon: um UIButton chamado imediatamente abaixo do campo de texto de senha e desmarque a opção Habilitado no Inspetor de atributos, em Conteúdo de controle

    • WNS: o rótulo e o comutador para habilitar o envio da notificação do Serviço de Notificação do Windows, se ele tiver sido configurado no hub. Confira o tutorial Introdução ao Windows.

    • GCM: o rótulo e a opção para habilitar o envio da notificação para o Google Cloud Messaging, se ele tiver sido configurado no hub. Consulte o tutorial Introdução ao Android .

    • APNS: o rótulo e a opção para habilitar o envio da notificação ao Apple Platform Notification Service.

    • Nome do Usuário do Destinatário: um UITextField com texto de espaço reservado marca de nome de usuário do destinatário, imediatamente abaixo do rótulo GCM e restrita às margens esquerda e direita e abaixo do rótulo do GCM.

      Alguns componentes foram adicionados no tutorial Enviar notificações por push para aplicativos iOS usando Hubs de Notificação do Azure.

  3. Use Ctrl e arraste os componentes na exibição para ViewController.h e adicione essas novas saídas:

    @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. No ViewController.h, adicione o seguinte #define após as instruções de importação. Substitua o espaço reservado <Your backend endpoint> pela URL de destino que você usou para implantar o back-end do aplicativo na seção anterior. Por exemplo http://your_backend.azurewebsites.net:

    #define BACKEND_ENDPOINT @"<Your backend endpoint>"
    
  5. Em seu projeto, crie uma nova classe Cocoa Touch chamada RegisterClient para fazer a interface com o back-end ASP.NET que você criou. Criar a classe herdeira de NSObject. Em seguida, adicione o seguinte código ao 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. No RegisterClient.m, atualize a seção @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. Substitua a seção @implementation no RegisterClient.m pelo código a seguir:

    @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
    

    Este código implementa a lógica explicada no artigo de orientação Registrando-se a partir do back-end do seu aplicativo, utilizando NSURLSession para realizar chamadas de REST para o back-end do seu aplicativo e NSUserDefaults para armazenar localmente o registrationId retornado pelo hub de notificação.

    Essa classe requer que sua propriedade authorizationHeader seja configurada para funcionar adequadamente. Essa propriedade é definida pela classe ViewController após o logon.

  8. Em ViewController.h, adicione uma instrução #import a RegisterClient.h. Em seguida, adicione uma declaração para o token do dispositivo e faça referência a uma instância RegisterClient na seção @interface:

    #import "RegisterClient.h"
    
    @property (strong, nonatomic) NSData* deviceToken;
    @property (strong, nonatomic) RegisterClient* registerClient;
    
  9. No ViewController.m, adicione uma declaração de método privado à seção @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
    

    Observação

    O snippet a seguir não é um esquema de autenticação seguro, você deve substituir a implementação de createAndSetAuthenticationHeaderWithUsername:AndPassword: pelo seu mecanismo de autenticação específico que gera um token de autenticação a ser consumido pela classe de cliente do registro, por exemplo, OAuth, Active Directory.

  10. Em seguida, na seção @implementation do ViewController.m, adicione o seguinte código que adiciona a implementação para definir o cabeçalho de autenticação e o token do dispositivo.

    -(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;
    }
    

    Observe como a configuração do token do dispositivo ativa o botão Fazer logon. Isso ocorre porque, como parte da ação de logon, o controlador de exibição se registra para notificações por push no back-end do aplicativo. Não queremos que ação de Fazer logon seja acessível até que o token do dispositivo seja configurado corretamente. É possível desacoplar o login do registro push desde que a primeira opção ocorra antes da última citada.

  11. Em ViewController.m, use os trechos de código a seguir para implementar o método de ação em seu botão Fazer logon e um método para enviar a mensagem de notificação usando o back-end do 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. Atualizar a ação para o botão Enviar notificação para usar o back-end do ASP.NET e enviar a qualquer PNS habilitado por um comutador.

    - (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. Na função ViewDidLoad, adicione o código a seguir para instanciar a instância RegisterClient e definir o delegado para os seus campos de texto.

    self.UsernameField.delegate = self;
    self.PasswordField.delegate = self;
    self.RecipientField.delegate = self;
    self.registerClient = [[RegisterClient alloc] initWithEndpoint:BACKEND_ENDPOINT];
    
  14. Agora, em AppDelegate.m, remova todo o conteúdo do método application:didRegisterForPushNotificationWithDeviceToken: e substitua-o pelo seguinte (para se certificar de que o controlador de exibição contenha o token de dispositivo mais recente, recuperado de 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. Por fim, em AppDelegate.m, verifique se você tem o seguinte método:

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

Testar o aplicativo

  1. No XCode, execute o aplicativo em um dispositivo iOS físico (as notificações por push não funcionam no simulador).

  2. Na interface do usuário do aplicativo iOS, insira o mesmo valor para o nome de usuário e senha. Em seguida, clique em Fazer logon.

    Aplicativo de teste do iOS

  3. Você deverá ver um pop-up informando sobre o sucesso da inscrição. Clique em OK.

    Notificação de teste do iOS exibida

  4. No campo de texto *Marca de nome de usuário do destinatário , insira a marca de nome de usuário usada com o registro de outro dispositivo.

  5. Digite uma mensagem de notificação e clique em Enviar notificação. Somente os dispositivos que têm um registro com a marca de nome de usuário do destinatário recebem a mensagem de notificação. Isso é enviado somente aos usuários.

    Notificação marcada de teste do iOS

Próximas etapas

Neste tutorial, você aprendeu como enviar notificações por push para usuários específicos que têm tags associadas seus registros. Para saber como enviar notificações por push, vá para o tutorial a seguir: