Tutorial: Enviar notificações por push para aplicativos Xamarin.Forms usando os Hubs de Notificação do Azure por meio de um serviço de back-end

Download Sample Baixar o exemplo

Neste tutorial, você usa os Hubs de Notificação do Azure para enviar notificações por push a um aplicativo Xamarin.Forms destinadas ao Android e ao iOS.

Um back-end de API Web do ASP.NET Core é usado para manipular o registro do dispositivo para o cliente usando a melhor e mais recente abordagem de Instalação. O serviço também enviará notificações por push de maneira multiplataforma.

Essas operações são manipuladas usando o SDK dos Hubs de Notificação para operações de back-end. Mais detalhes sobre a abordagem geral são fornecidos na documentação Registrar usando o back-end do aplicativo.

Este tutorial conduz você pelas seguintes etapas:

Pré-requisitos

Para acompanhar, você precisa de:

  • Uma assinatura do Azure na qual você possa criar e gerenciar recursos.
  • Um Mac com Visual Studio para Mac instalado ou um PC executando o Visual Studio 2019.
  • Usuários do Visual Studio 2019 também devem ter as cargas de trabalho de Desenvolvimento móvel com o .NET e ASP.NET e desenvolvimento Web instaladas.
  • A capacidade de executar o aplicativo tanto no Android (dispositivos físicos ou de emulador) quanto no iOS (somente em dispositivos físicos).

Para o Android, é preciso ter:

  • Um dispositivo físico desbloqueado para desenvolvedor ou um emulador (executando a API 26 e posterior com o Google Play Services instalado) .

Para o iOS, é preciso ter:

Observação

O simulador do iOS não é compatível com notificações remotas e, portanto, um dispositivo físico é necessário ao explorar este exemplo no iOS. No entanto, não é necessário executar o aplicativo tanto no Android quanto no iOS para concluir este tutorial.

As etapas neste exemplo de princípios essenciais não requerem experiência prévia. No entanto, você se beneficiará caso tenha familiaridade com os aspectos a seguir.

Importante

As etapas fornecidas são específicas para o Visual Studio para Mac. É possível acompanhar usando o Visual Studio 2019, mas pode haver algumas diferenças a serem adaptadas. Por exemplo, descrições da interface do usuário e de fluxos de trabalho, nomes de modelos, configuração de ambiente e assim por diante.

Configurar os Serviços de Notificação por Push nos Hubs de Notificação do Azure

Nesta seção, você configura o FCM (Firebase Cloud Messaging) e o APNS (Apple Push Notification Service) . Em seguida, você cria e configura um hub de notificação para funcionar com esses serviços.

Criar um projeto de Firebase e habilitar Firebase Cloud Messaging para Android

  1. Faça logon no console do Firebase. Crie um projeto do Firebase inserindo PushDemo como o Nome do projeto.

    Observação

    Um nome exclusivo será gerado para você. Por padrão, ele é composto de uma variante de minúsculas do nome fornecido, além de um número gerado, separado por um traço. Você pode alterar isso se desejar, desde que o nome ainda seja globalmente exclusivo.

  2. Depois de criar seu projeto, selecione Adicionar Firebase ao seu aplicativo Android.

    Add Firebase to your Android app

  3. Siga estas etapas na página Adicionar Firebase ao seu aplicativo Android.

    1. Para o nome do pacote do Android, insira um nome para o pacote. Por exemplo: com.<organization_identifier>.<package_name>.

      Specify the package name

    2. Selecione Registrar aplicativo.

    3. Selecione Baixar google-services.json. Em seguida, salve o arquivo em uma pasta local para uso posterior e selecione Avançar.

      Download google-services.json

    4. Selecione Avançar.

    5. Selecione Continuar no console

      Observação

      Se o botão Continuar no console não estiver habilitado, devido à verificação verificar instalação, escolha Ignorar esta etapa.

  4. No console do Firebase, selecione a engrenagem para seu projeto. Em seguida, selecione Configurações do Projeto.

    Select Project Settings

    Observação

    Se você ainda não baixou o arquivo google-services.json, é possível fazer isso nesta página.

  5. Mude para a guia Mensagens de Nuvem na parte superior. Copie e salve a Chave do servidor para uso posterior. Você usará esse valor para configurar o hub de notificação.

    Copy server key

Registrar seu aplicativo iOS para notificações por push

Para enviar notificações por push para um aplicativo iOS, registre seu aplicativo na Apple e também registre-se para notificações por push.

  1. Se você ainda não tiver registrado seu aplicativo, navegue até o Portal de provisionamento iOS no Apple Developer Center. Entre no portal com sua ID da Apple, navegue até Certificados, Perfis de Identificadores &e selecione Identificadores. Clique em + para registrar um novo aplicativo.

    iOS Provisioning Portal App IDs page

  2. Na tela Registrar um Novo Identificador, selecione o botão de opção IDs do Aplicativo. Depois selecione Continuar.

    iOS Provisioning Portal register new ID page

  3. Atualize os três valores a seguir para o novo aplicativo e selecione Continuar:

    • Descrição: Digite um nome descritivo para o seu aplicativo.

    • ID do pacote: insira uma ID do pacote do formulário com.organization_identifier<.<>>product_name conforme mencionado no Guia de Distribuição de Aplicativos. Na captura de tela abaixo, o valor mobcat é usado como um identificador de organização e o valor PushDemo é usado como o nome do produto.

      iOS Provisioning Portal register app ID page

    • Notificações por Push: Marque a opção Notificações por Push na seção Funcionalidades.

      Form to register a new App ID

      Essa ação gerará sua ID do Aplicativo e solicitará que você confirme as informações. Selecione Continuar e, em seguida, selecione Registrar para confirmar a nova ID do Aplicativo.

      Confirm new App ID

      Depois de selecionar Registrar, você verá a nova ID do Aplicativo como um item de linha na página Certificados, Identificadores & Perfis.

  4. Na página Certificados, Perfis de Identificadores&, em Identificadores, localize o item de linha de ID do Aplicativo que você criou. Em seguida, selecione a linha para exibir a tela Editar a configuração da ID do aplicativo.

Criar um certificado para os Hubs de Notificação

Um certificado é necessário para habilitar o hub de notificação para trabalhar com o APNs (Apple Push Notification Service) e pode ser fornecido de duas maneiras:

  1. Criando um certificado push p12 que possa ser carregado diretamente no Hub de Notificação (a abordagem original)

  2. Criando um certificado p8 que possa ser usado para autenticação baseada em token (a abordagem mais recente e recomendada)

A abordagem mais recente tem vários benefícios, conforme documentado em Autenticação baseada em token (HTTP/2) para APNs. São necessárias menos etapas, mas também são obrigatórias para cenários específicos. No entanto, as etapas foram fornecidas para ambas as abordagens, já que funcionarão para os fins deste tutorial.

OPÇÃO UM: Criação de um certificado push p12 que possa ser carregado diretamente no Hub de Notificação
  1. Em seu Mac, execute a ferramenta Acesso do Conjunto de Chaves. Ela pode ser aberta na pasta Utilitários ou na pasta Outros no Launchpad.

  2. Selecione Acesso do Conjunto de Chaves, expanda Assistente de Certificado e selecione Solicitar um Certificado de uma Autoridade de Certificação.

    Use Keychain Access to request a new certificate

    Observação

    Por padrão, o Acesso às Chaves seleciona o primeiro item na lista. Isso pode ser um problema se você estiver na categoria Certificados e a Autoridade de Certificação de Relacionamentos do Desenvolvedor Mundial da Apple não for o primeiro item da lista. Verifique se você tem um item não chave ou se a chave Autoridade de Certificação de Relacionamentos do Desenvolvedor Mundial da Apple está selecionada antes de gerar o CSR (Solicitação de Assinatura de Certificado).

  3. Selecione seu Endereço de Email do Usuário, insira o valor do seu Nome Comum, verifique se você especificou Salvo em disco e, em seguida, selecione Continuar. Deixe Endereço de Email de AC em branco, pois isso não é necessário.

    Expected certificate information

  4. Insira um nome para o arquivo CSR (Solicitação de Assinatura de Certificado) em Salvar como, selecione a localização em Onde e clique em Salvar.

    Choose a file name for the certificate

    Essa ação salva o arquivo CSR na localização selecionada. O local padrão é a área de trabalho. Lembre-se do local escolhido para o arquivo.

  5. De volta à página Certificados, Perfis de Identificadores & no Portal de Provisionamento do iOS, role para baixo até a opção Notificações por Push marcadas e, em seguida, selecione Configurar para criar o certificado.

    Edit App ID page

  6. A janela Certificados TLS/SSL do Apple Push Notification Service é exibida. Selecione o botão Criar Certificado na seção Certificado TLS/SSL de Desenvolvimento.

    Create certificate for App ID button

    A tela Criar um novo Certificado é exibida.

    Observação

    Este tutorial usa um certificado de desenvolvimento. O mesmo processo é usado para registrar um certificado de produção. Use o mesmo tipo de certificado ao enviar notificações.

  7. Selecione Escolher Arquivo, navegue até a localização em que salvou o arquivo CSR e clique duas vezes no nome do certificado para carregá-lo. Depois selecione Continuar.

  8. Depois que o portal cria o certificado, selecione o botão Baixar. Salve o certificado e lembre-se do local em que ele foi salvo.

    Generated certificate download page

    O certificado é baixado e salvo no seu computador, na pasta Downloads.

    Locate certificate file in the Downloads folder

    Observação

    Por padrão, o certificado de desenvolvimento baixado é denominado aps_development.cer.

  9. Clique duas vezes no certificado de push baixado, aps_development.cer. Essa ação instala o novo certificado no conjunto de chaves, conforme mostrado na seguinte imagem:

    Keychain access certificates list showing new certificate

    Observação

    Embora o nome em seu certificado possa ser diferente, esse nome será prefixado com Serviços de Notificação por Push do iOS para Desenvolvimento da Apple e terá o identificador de pacote apropriado associado a ele.

  10. No Acesso ao Conjunto de Chaves, Control + Clique no novo certificado push criado na categoria Certificados. Selecione Exportar, nomeie o arquivo, selecione o formato p12 e clique em Salvar.

    Export certificate as p12 format

    Você pode optar por proteger o certificado com uma senha, mas a senha é opcional. Clique em OK caso deseje ignorar a criação de senha. Anote o nome do arquivo e a localização do certificado p12 exportado. Eles são usados para habilitar a autenticação com APNs.

    Observação

    O nome e a localização do arquivo p12 podem ser diferentes do que foi mostrado neste tutorial.

OPÇÃO DOIS: Criação de um certificado p8 que possa ser usado para a autenticação baseada em token
  1. Anote os seguintes detalhes:

    • Prefixo da ID do Aplicativo (ID da Equipe)
    • ID do Pacote
  2. Novamente em Certificados, Identificadores & Perfis, clique em Chaves.

    Observação

    Caso já tenha uma chave configurada para APNs, reutilize o certificado p8 que você baixou logo depois de criá-lo. Nesse caso, você pode ignorar desde a etapa 3 até a 5.

  3. Clique no botão + (ou no botão Criar uma chave) para criar uma chave.

  4. Forneça um valor de Nome de Chave adequado, marque a opção APNs (Apple Push Notification Service) , clique em Continuar e, na próxima tela, clique em Registrar.

  5. Clique em Baixar, mova o arquivo p8 (prefixado com AuthKey_ ) para um diretório local seguro e clique em Concluído.

    Observação

    Verifique se o arquivo p8 está em um local seguro (e salve um backup). Após o download da chave, não é possível baixá-la novamente, pois a cópia do servidor é removida.

  6. Em Chaves, clique na chave que você criou (ou em uma já existente, caso tenha optado por usá-la).

  7. Anote o valor da ID da Chave.

  8. Abra o certificado p8 em um aplicativo de sua escolha, como o Visual Studio Code. Anote o valor da chave (entre -----BEGIN PRIVATE KEY----- e -----END PRIVATE KEY----- ).

    -----BEGIN PRIVATE KEY-----
    <key_value>
    -----END PRIVATE KEY-----

    Observação

    Esse é o valor do token que será usado mais tarde para configurar o Hub de Notificação.

No final dessas etapas, você terá as seguintes informações para uso posterior em Configurar o hub de notificação com informações da APNs:

  • A ID da Equipe (veja a etapa um)
  • A ID do Pacote (veja a etapa um)
  • A ID da Chave (veja a etapa sete)
  • Valor do token (o valor da chave p8 obtido na etapa 8)

Criar um perfil de provisionamento para o aplicativo

  1. Retorne ao Portal de Provisionamento do iOS, selecione Certificados, Identificadores & Perfis, selecione Perfis no menu à esquerda e selecione + para criar um novo perfil. A tela Registrar um Novo Perfil de Provisionamento é exibida.

  2. Selecione Desenvolvimento de Aplicativos do iOS em Desenvolvimento como o tipo de perfil de provisionamento e selecione Continuar.

    Provisioning profile list

  3. Em seguida, selecione a ID do aplicativo que você criou na lista suspensa ID do Aplicativo e selecione Continuar.

    Select the App ID

  4. Na janela Selecionar certificados, selecione o certificado de desenvolvimento que você usa para assinatura de código e selecione Continuar.

    Observação

    Esse certificado não é o certificado push que você criou na etapa anterior. Este é o certificado de desenvolvimento. Se não houver um, você deverá criá-lo porque este é um pré-requisito para este tutorial. Os certificados de desenvolvedor podem ser criados no Apple Developer Portal, por meio do Xcode ou no Visual Studio.

  5. Retorne à página Certificados, Identificadores & Perfis, selecione Perfis no menu à esquerda e escolha + para criar um perfil. A tela Registrar um Novo Perfil de Provisionamento é exibida.

  6. Na janela Selecionar certificados, selecione o certificado de desenvolvimento que você criou. Depois selecione Continuar.

  7. Em seguida, selecione os dispositivos a serem usados para teste e selecione Continuar.

  8. Por fim, escolha um nome para o perfil em Nome do Perfil de Provisionamento e selecione Gerar.

    Choose a provisioning profile name

  9. Quando o novo perfil de provisionamento for criado, selecione Baixar. Lembre-se do local em que ele foi salvo.

  10. Navegue até a localização do perfil de provisão e clique duas vezes nele para instalá-lo em seu computador de desenvolvimento.

Criar um hub de notificação

Nesta seção, você criará um hub de notificação e configurará a autenticação com a APNs. Você pode usar um certificado de push p12 ou a autenticação baseada em token. Se você quiser usar um hub de notificação já criado, passe diretamente à etapa 5.

  1. Entre no Azure.

  2. Clique em Criar um recurso, pesquise e escolha Hub de Notificação e clique em Criar.

  3. Atualize os seguinte campos e clique em Criar:

    DETALHES BÁSICOS

    Assinatura: Escolha a Assinatura de destino na lista suspensa
    Grupo de recursos: Crie um Grupo de recursos (ou use um grupo existente)

    DETALHES DO NAMESPACE

    Namespace do hub de notificação: Insira um nome globalmente exclusivo para o namespace do Hub de Notificação

    Observação

    Verifique se a opção Criar está selecionada para este campo.

    DETALHES DO HUB DE NOTIFICAÇÃO

    Hub de Notificação: Insira um nome para o Hub de Notificação
    Localização: Escolha uma localização adequada na lista suspensa
    Tipo de preço: Mantenha a opção Gratuita padrão

    Observação

    A menos que você tenha alcançado o número máximo de hubs na Camada gratuita.

  4. Quando o Hub de Notificação tiver sido provisionado, navegue até esse recurso.

  5. Navegue até o novo Hub de Notificação.

  6. Selecione Políticas de Acesso na lista (em GERENCIAR).

  7. Anote os valores de Nome da Política junto com os valores de Cadeia de Conexão correspondentes.

Configurar seu hub de notificação com as informações de APNS

Em Serviços de Notificação, selecione Apple e siga as etapas apropriadas com base na abordagem que você selecionou antes na seção Criar um certificado para os Hubs de Notificação.

Observação

Use Produção no Modo de Aplicativo somente caso queira enviar notificações por push aos usuários que tenham comprado seu aplicativo da loja.

OPÇÃO UM: Usando um certificado push .p12

  1. Selecione Certificado.

  2. Selecione o ícone arquivo.

  3. Selecione o arquivo .p12 que você exportou anteriormente e, em seguida, clique em Abrir.

  4. Se necessário, especifique a senha correta.

  5. Selecione o modo Sandbox.

  6. Clique em Salvar.

OPÇÃO DOIS: Usando a autenticação baseada em token

  1. Selecione Token.

  2. Insira os seguintes valores que você adquiriu antes:

    • ID da Chave
    • ID do Pacote
    • ID da Equipe
    • Token
  3. Selecione Área restrita.

  4. Clique em Salvar.

Configure seu hub de notificação com as informações de FCM

  1. Selecione Google (GCM/FCM) na seção Configurações no menu à esquerda.
  2. Insira a chave do servidor que você anotou do Google Firebase Console.
  3. Selecione Salvar na barra de ferramentas.

Criar um aplicativo de back-end de API Web do ASP.NET Core

Nesta seção, você cria o back-end da API Web do ASP.NET Core para lidar com o registro de dispositivos e o envio de notificações para o aplicativo móvel Xamarin.Forms.

Criar um projeto Web

  1. No Visual Studio, selecione Arquivo>Nova Solução.

  2. Selecione .NET Core>Aplicativo>ASP.NET Core>API>Avançar.

  3. Na caixa de diálogo Configurar a nova API Web do ASP.NET Core, selecione Estrutura de Destino como .NET Core 3.1.

  4. Insira PushDemoApi como o Nome do Projeto e selecione Criar.

  5. Inicie a depuração (Comando + Enter) para testar o aplicativo com modelo.

    Observação

    O aplicativo com modelo está configurado para usar o WeatherForecastController como a launchUrl. Isso é definido em Propriedades>launchSettings.json.

    Se você receber uma solicitação com uma mensagem Certificado de desenvolvimento inválido encontrado:

    1. Clique em Sim para concordar em executar a ferramenta "dotnet dev-certs https" para corrigir isso. A ferramenta "dotnet dev-certs https" solicita que você insira uma senha para o certificado e a senha do seu Conjunto de chaves.

    2. Clique em Sim quando solicitado a instalar e confiar no novode certificado, insira a senha para seu Conjunto de chaves.

  6. Expanda a pasta Controladores e exclua WeatherForecastController.cs.

  7. Excluir WeatherForecast.cs.

  8. Configure os valores da configuração local usando a ferramenta Gerenciador de Segredos. O desacoplamento dos segredos da solução garante que eles não acabem no controle do código-fonte. Abra o Terminal, acesse o diretório do arquivo de projeto e execute os seguintes comandos:

    dotnet user-secrets init
    dotnet user-secrets set "NotificationHub:Name" <value>
    dotnet user-secrets set "NotificationHub:ConnectionString" <value>
    

    Substitua os valores de espaço reservado pelos seus valores do nome do hub de notificação e cadeia de conexão. Você os anotou na seção criar um hub de notificação. Caso contrário, procure-os no Azure.

    NotificationsHub:Name:
    Confira Nome no resumo dos Fundamentos na parte superior de Visão geral.

    NotificationHub:ConnectionString:
    Veja DefaultFullSharedAccessSignature em Políticas de Acesso

    Observação

    Para cenários de produção, examine opções como o Azure KeyVault para armazenar a cadeia de conexão com segurança. Para simplificar, os segredos serão adicionados às configurações do aplicativo do Serviço de Aplicativo do Azure.

Autenticar clientes usando uma chave de API (opcional)

As chaves de API não são tão seguras como tokens, mas serão suficientes para os fins deste tutorial. Uma chave de API pode ser configurada facilmente por meio do Middleware do ASP.NET.

  1. Adicione a chave de API aos valores de configuração local.

    dotnet user-secrets set "Authentication:ApiKey" <value>
    

    Observação

    Você deve substituir o valor do espaço reservado pelo seu valor e anotá-lo.

  2. Control + Clique no projeto PushDemoApi, escolha Nova Pasta no menu Adicionar e clique em Adicionar usando Autenticação como o Nome da Pasta.

  3. Control + Clique na pasta Autenticação e escolha Novo Arquivo... no menu Adicionar.

  4. Selecione Geral>Classe Vazia, digite ApiKeyAuthOptions.cs como o Nome e clique em Novo, adicionando a implementação a seguir.

    using Microsoft.AspNetCore.Authentication;
    
    namespace PushDemoApi.Authentication
    {
        public class ApiKeyAuthOptions : AuthenticationSchemeOptions
        {
            public const string DefaultScheme = "ApiKey";
            public string Scheme => DefaultScheme;
            public string ApiKey { get; set; }
        }
    }
    
  5. Adicione outra Classe Vazia à pasta Autenticação chamada ApiKeyAuthHandler.cs e adicione a implementação a seguir.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Security.Claims;
    using System.Text.Encodings.Web;
    using System.Threading.Tasks;
    using Microsoft.AspNetCore.Authentication;
    using Microsoft.Extensions.Logging;
    using Microsoft.Extensions.Options;
    
    namespace PushDemoApi.Authentication
    {
        public class ApiKeyAuthHandler : AuthenticationHandler<ApiKeyAuthOptions>
        {
            const string ApiKeyIdentifier = "apikey";
    
            public ApiKeyAuthHandler(
                IOptionsMonitor<ApiKeyAuthOptions> options,
                ILoggerFactory logger,
                UrlEncoder encoder,
                ISystemClock clock)
                : base(options, logger, encoder, clock) {}
    
            protected override Task<AuthenticateResult> HandleAuthenticateAsync()
            {
                string key = string.Empty;
    
                if (Request.Headers[ApiKeyIdentifier].Any())
                {
                    key = Request.Headers[ApiKeyIdentifier].FirstOrDefault();
                }
                else if (Request.Query.ContainsKey(ApiKeyIdentifier))
                {
                    if (Request.Query.TryGetValue(ApiKeyIdentifier, out var queryKey))
                        key = queryKey;
                }
    
                if (string.IsNullOrWhiteSpace(key))
                    return Task.FromResult(AuthenticateResult.Fail("No api key provided"));
    
                if (!string.Equals(key, Options.ApiKey, StringComparison.Ordinal))
                    return Task.FromResult(AuthenticateResult.Fail("Invalid api key."));
    
                var identities = new List<ClaimsIdentity> {
                    new ClaimsIdentity("ApiKeyIdentity")
                };
    
                var ticket = new AuthenticationTicket(
                    new ClaimsPrincipal(identities), Options.Scheme);
    
                return Task.FromResult(AuthenticateResult.Success(ticket));
            }
        }
    }
    

    Observação

    Um Manipulador de Autenticação é um tipo que implementa o comportamento de um esquema, neste caso, um esquema de chave de API personalizado.

  6. Adicione outra Classe Vazia à pasta Autenticação chamada ApiKeyAuthenticationBuilderExtensions.cs e adicione a implementação a seguir.

    using System;
    using Microsoft.AspNetCore.Authentication;
    
    namespace PushDemoApi.Authentication
    {
        public static class AuthenticationBuilderExtensions
        {
            public static AuthenticationBuilder AddApiKeyAuth(
                this AuthenticationBuilder builder,
                Action<ApiKeyAuthOptions> configureOptions)
            {
                return builder
                    .AddScheme<ApiKeyAuthOptions, ApiKeyAuthHandler>(
                        ApiKeyAuthOptions.DefaultScheme,
                        configureOptions);
            }
        }
    }
    

    Observação

    Esse método de extensão simplifica o código de configuração do middleware em Startup.cs tornando-o mais legível e geralmente mais fácil de seguir.

  7. No Startup.cs, atualize o método ConfigureServices para configurar a autenticação da chave de API abaixo da chamada para ao método services.AddControllers.

    using PushDemoApi.Authentication;
    using PushDemoApi.Models;
    using PushDemoApi.Services;
    
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllers();
    
        services.AddAuthentication(options =>
        {
            options.DefaultAuthenticateScheme = ApiKeyAuthOptions.DefaultScheme;
            options.DefaultChallengeScheme = ApiKeyAuthOptions.DefaultScheme;
        }).AddApiKeyAuth(Configuration.GetSection("Authentication").Bind);
    }
    
  8. Ainda no Startup.cs, atualize o método Configure para chamar os métodos de extensão UseAuthentication e UseAuthorization no IApplicationBuilder do aplicativo. Verifique se esses métodos são chamados depois de UseRouting e antes de app.UseEndpoints.

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
    
        app.UseHttpsRedirection();
    
        app.UseRouting();
    
        app.UseAuthentication();
    
        app.UseAuthorization();
    
        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
        });
    }
    

    Observação

    Chamar UseAuthentication registra o middleware que usa os esquemas de autenticação registrados anteriormente (de ConfigureServices). Isso deve ser chamado antes de qualquer middleware que dependa de usuários serem autenticados.

Adicionar dependências e configurar serviços

O ASP.NET Core é compatível com o padrão de design de software de DI (injeção de dependência), que é uma técnica para alcançar a IoC (Inversão de Controle) entre classes e suas dependências.

O uso do hub de notificação e do SDK dos Hubs de Notificação para operações de back-end é encapsulado dentro de um serviço. O serviço é registrado e disponibilizado por meio de uma abstração adequada.

  1. Control + Clique na pasta Dependências e escolha Gerenciar Pacotes NuGet... .

  2. Pesquise Microsoft.Azure.NotificationHubs e verifique se ele está marcado.

  3. Clique em Adicionar Pacotes e em Aceitar quando solicitado a aceitar os termos de licença.

  4. Control + Clique no projeto PushDemoApi, escolha Nova Pasta no menu Adicionar e clique em Adicionar usando Modelos como o Nome da Pasta.

  5. Control + Clique na pasta Modelos e escolha Novo Arquivo... no menu Adicionar.

  6. Selecione Geral>Classe Vazia, digite PushTemplates.cs como o Nome e clique em Novo, adicionando a implementação a seguir.

    namespace PushDemoApi.Models
    {
        public class PushTemplates
        {
            public class Generic
            {
                public const string Android = "{ \"notification\": { \"title\" : \"PushDemo\", \"body\" : \"$(alertMessage)\"}, \"data\" : { \"action\" : \"$(alertAction)\" } }";
                public const string iOS = "{ \"aps\" : {\"alert\" : \"$(alertMessage)\"}, \"action\" : \"$(alertAction)\" }";
            }
    
            public class Silent
            {
                public const string Android = "{ \"data\" : {\"message\" : \"$(alertMessage)\", \"action\" : \"$(alertAction)\"} }";
                public const string iOS = "{ \"aps\" : {\"content-available\" : 1, \"apns-priority\": 5, \"sound\" : \"\", \"badge\" : 0}, \"message\" : \"$(alertMessage)\", \"action\" : \"$(alertAction)\" }";
            }
        }
    }
    

    Observação

    Essa classe contém o conteúdo de notificação indexado para as notificações genéricas e silenciosas exigidas por esse cenário. Os conteúdos são definidos fora da Instalação para permitir a experimentação sem a necessidade de atualizar as instalações existentes por meio do serviço. O tratamento de alterações nas instalações dessa forma está fora do escopo deste tutorial. Para produção, considere modelos personalizados.

  7. Adicione outra Classe Vazia à pasta Modelos chamada DeviceInstallation.cs e adicione a implementação a seguir.

    using System.Collections.Generic;
    using System.ComponentModel.DataAnnotations;
    
    namespace PushDemoApi.Models
    {
        public class DeviceInstallation
        {
            [Required]
            public string InstallationId { get; set; }
    
            [Required]
            public string Platform { get; set; }
    
            [Required]
            public string PushChannel { get; set; }
    
            public IList<string> Tags { get; set; } = Array.Empty<string>();
        }
    }
    
  8. Adicione outra Classe Vazia à pasta Modelos chamada NotificationRequest.cs e adicione a implementação a seguir.

    using System;
    
    namespace PushDemoApi.Models
    {
        public class NotificationRequest
        {
            public string Text { get; set; }
            public string Action { get; set; }
            public string[] Tags { get; set; } = Array.Empty<string>();
            public bool Silent { get; set; }
        }
    }
    
  9. Adicione outra Classe Vazia à pasta Modelos chamada NotificationHubOptions.cs e adicione a implementação a seguir.

    using System.ComponentModel.DataAnnotations;
    
    namespace PushDemoApi.Models
    {
        public class NotificationHubOptions
        {
            [Required]
            public string Name { get; set; }
    
            [Required]
            public string ConnectionString { get; set; }
        }
    }
    
  10. Adicione uma nova pasta ao projeto PushDemoApi chamada Serviços.

  11. Adicione uma Interface Vazia à pasta Serviços chamada INotificationService.cs e adicione a implementação a seguir.

    using System.Threading;
    using System.Threading.Tasks;
    using PushDemoApi.Models;
    
    namespace PushDemoApi.Services
    {
        public interface INotificationService
        {
            Task<bool> CreateOrUpdateInstallationAsync(DeviceInstallation deviceInstallation, CancellationToken token);
            Task<bool> DeleteInstallationByIdAsync(string installationId, CancellationToken token);
            Task<bool> RequestNotificationAsync(NotificationRequest notificationRequest, CancellationToken token);
        }
    }
    
  12. Adicione uma Classe Vazia à pasta Serviços chamada NotificationHubsService.cs e adicione o seguinte código para implementar a interface INotificationService:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading;
    using System.Threading.Tasks;
    using Microsoft.Azure.NotificationHubs;
    using Microsoft.Extensions.Logging;
    using Microsoft.Extensions.Options;
    using PushDemoApi.Models;
    
    namespace PushDemoApi.Services
    {
        public class NotificationHubService : INotificationService
        {
            readonly NotificationHubClient _hub;
            readonly Dictionary<string, NotificationPlatform> _installationPlatform;
            readonly ILogger<NotificationHubService> _logger;
    
            public NotificationHubService(IOptions<NotificationHubOptions> options, ILogger<NotificationHubService> logger)
            {
                _logger = logger;
                _hub = NotificationHubClient.CreateClientFromConnectionString(
                    options.Value.ConnectionString,
                    options.Value.Name);
    
                _installationPlatform = new Dictionary<string, NotificationPlatform>
                {
                    { nameof(NotificationPlatform.Apns).ToLower(), NotificationPlatform.Apns },
                    { nameof(NotificationPlatform.Fcm).ToLower(), NotificationPlatform.Fcm }
                };
            }
    
            public async Task<bool> CreateOrUpdateInstallationAsync(DeviceInstallation deviceInstallation, CancellationToken token)
            {
                if (string.IsNullOrWhiteSpace(deviceInstallation?.InstallationId) ||
                    string.IsNullOrWhiteSpace(deviceInstallation?.Platform) ||
                    string.IsNullOrWhiteSpace(deviceInstallation?.PushChannel))
                    return false;
    
                var installation = new Installation()
                {
                    InstallationId = deviceInstallation.InstallationId,
                    PushChannel = deviceInstallation.PushChannel,
                    Tags = deviceInstallation.Tags
                };
    
                if (_installationPlatform.TryGetValue(deviceInstallation.Platform, out var platform))
                    installation.Platform = platform;
                else
                    return false;
    
                try
                {
                    await _hub.CreateOrUpdateInstallationAsync(installation, token);
                }
                catch
                {
                    return false;
                }
    
                return true;
            }
    
            public async Task<bool> DeleteInstallationByIdAsync(string installationId, CancellationToken token)
            {
                if (string.IsNullOrWhiteSpace(installationId))
                    return false;
    
                try
                {
                    await _hub.DeleteInstallationAsync(installationId, token);
                }
                catch
                {
                    return false;
                }
    
                return true;
            }
    
            public async Task<bool> RequestNotificationAsync(NotificationRequest notificationRequest, CancellationToken token)
            {
                if ((notificationRequest.Silent &&
                    string.IsNullOrWhiteSpace(notificationRequest?.Action)) ||
                    (!notificationRequest.Silent &&
                    (string.IsNullOrWhiteSpace(notificationRequest?.Text)) ||
                    string.IsNullOrWhiteSpace(notificationRequest?.Action)))
                    return false;
    
                var androidPushTemplate = notificationRequest.Silent ?
                    PushTemplates.Silent.Android :
                    PushTemplates.Generic.Android;
    
                var iOSPushTemplate = notificationRequest.Silent ?
                    PushTemplates.Silent.iOS :
                    PushTemplates.Generic.iOS;
    
                var androidPayload = PrepareNotificationPayload(
                    androidPushTemplate,
                    notificationRequest.Text,
                    notificationRequest.Action);
    
                var iOSPayload = PrepareNotificationPayload(
                    iOSPushTemplate,
                    notificationRequest.Text,
                    notificationRequest.Action);
    
                try
                {
                    if (notificationRequest.Tags.Length == 0)
                    {
                        // This will broadcast to all users registered in the notification hub
                        await SendPlatformNotificationsAsync(androidPayload, iOSPayload, token);
                    }
                    else if (notificationRequest.Tags.Length <= 20)
                    {
                        await SendPlatformNotificationsAsync(androidPayload, iOSPayload, notificationRequest.Tags, token);
                    }
                    else
                    {
                        var notificationTasks = notificationRequest.Tags
                            .Select((value, index) => (value, index))
                            .GroupBy(g => g.index / 20, i => i.value)
                            .Select(tags => SendPlatformNotificationsAsync(androidPayload, iOSPayload, tags, token));
    
                        await Task.WhenAll(notificationTasks);
                    }
    
                    return true;
                }
                catch (Exception e)
                {
                    _logger.LogError(e, "Unexpected error sending notification");
                    return false;
                }
            }
    
            string PrepareNotificationPayload(string template, string text, string action) => template
                .Replace("$(alertMessage)", text, StringComparison.InvariantCulture)
                .Replace("$(alertAction)", action, StringComparison.InvariantCulture);
    
            Task SendPlatformNotificationsAsync(string androidPayload, string iOSPayload, CancellationToken token)
            {
                var sendTasks = new Task[]
                {
                    _hub.SendFcmNativeNotificationAsync(androidPayload, token),
                    _hub.SendAppleNativeNotificationAsync(iOSPayload, token)
                };
    
                return Task.WhenAll(sendTasks);
            }
    
            Task SendPlatformNotificationsAsync(string androidPayload, string iOSPayload, IEnumerable<string> tags, CancellationToken token)
            {
                var sendTasks = new Task[]
                {
                    _hub.SendFcmNativeNotificationAsync(androidPayload, tags, token),
                    _hub.SendAppleNativeNotificationAsync(iOSPayload, tags, token)
                };
    
                return Task.WhenAll(sendTasks);
            }
        }
    }
    

    Observação

    A expressão de marcação fornecida para SendTemplateNotificationAsync é limitada a 20 marcações. Ela é limitada a 6 para a maioria dos operadores, mas a expressão contém apenas ORs (||) nesse caso. Se houver mais de 20 marcações na solicitação, elas deverão ser divididas em várias solicitações. Confira a documentação Expressões de marca e Roteamento para obter mais detalhes.

  13. No Startup.cs, atualize o método ConfigureServices para adicionar o NotificationHubsService como uma implementação de singleton de INotificationService.

    
    using PushDemoApi.Models;
    using PushDemoApi.Services;
    
    public void ConfigureServices(IServiceCollection services)
    {
        ...
    
        services.AddSingleton<INotificationService, NotificationHubService>();
    
        services.AddOptions<NotificationHubOptions>()
            .Configure(Configuration.GetSection("NotificationHub").Bind)
            .ValidateDataAnnotations();
    }
    

Criar a API de notificações

  1. Control + Clique na pasta Controladores e escolha Novo Arquivo... no menu Adicionar.

  2. Selecione ASP.NET Core>Classe do Controlador da API Web, digite NotificationsController como o Nome e clique em Novo.

    Observação

    Se estiver acompanhando com o Visual Studio 2019, escolha o modelo do Controlador de API com ações de leitura/gravação.

  3. Adicione os namespaces a seguir à parte superior do arquivo.

    using System.ComponentModel.DataAnnotations;
    using System.Net;
    using System.Threading;
    using System.Threading.Tasks;
    using Microsoft.AspNetCore.Authorization;
    using Microsoft.AspNetCore.Mvc;
    using PushDemoApi.Models;
    using PushDemoApi.Services;
    
  4. Atualize o controlador modelo para que derive de ControllerBase e seja decorado com o atributo ApiController.

    [ApiController]
    [Route("api/[controller]")]
    public class NotificationsController : ControllerBase
    {
        // Templated methods here
    }
    

    Observação

    A classe base Controller é compatível com exibições, mas isso não é necessário nesse caso e, portanto, a ControllerBase pode ser usada. Se estiver acompanhando com o Visual Studio 2019, você poderá ignorar esta etapa.

  5. Se você optou por concluir a seção Autenticar clientes usando uma chave de API, decore a NotificationsController com o atributo Authorize também.

    [Authorize]
    
  6. Atualize o construtor para aceitar a instância registrada de INotificationService como um argumento e atribua a instância a um membro ReadOnly.

    readonly INotificationService _notificationService;
    
    public NotificationsController(INotificationService notificationService)
    {
        _notificationService = notificationService;
    }
    
  7. Em launchSettings.json (dentro da pasta Propriedades), altere o launchUrl de weatherforecast para api/notificações para corresponder à URL especificada no atributo RegistrationsControllerRoute.

  8. Inicie a depuração (Command + Enter) para validar se o aplicativo está funcionando com o novo NotificationsController e retorna um status 401 Não autorizado.

    Observação

    O Visual Studio pode não iniciar automaticamente o aplicativo no navegador. Você usará o Postman para testar a API deste ponto em diante.

  9. Em uma nova guia do Postman , defina a solicitação como GET. Insira o endereço abaixo substituindo o applicationUrl> de espaço reservado< pelo applicationUrl https encontrado em PropertieslaunchSettings.json>.

    <applicationUrl>/api/notifications
    

    Observação

    O applicationUrl deve ser 'https://localhost:5001 ' para o perfil padrão. Se estiver usando o IIS (padrão no Visual Studio 2019 no Windows), use o applicationUrl especificado no item iisSettings. Você receberá uma resposta 404 se o endereço estiver incorreto.

  10. Se você optou por concluir a seção Autenticar clientes usando uma chave de API, configure os cabeçalhos de solicitação para incluir o valor de apikey.

    Chave Valor
    apikey <your_api_key>
  11. Clique no botão Enviar.

    Observação

    Você deve receber um status 200 OK com algum conteúdo em JSON.

    Se você receber um aviso de verificação de certificado SSL, poderá desativar a configuração da solicitação de verificação de certificado SSL do Postman nas Configurações.

  12. Substitua os métodos de classe do modelo em NotificationsController.cs pelo código a seguir.

    [HttpPut]
    [Route("installations")]
    [ProducesResponseType((int)HttpStatusCode.OK)]
    [ProducesResponseType((int)HttpStatusCode.BadRequest)]
    [ProducesResponseType((int)HttpStatusCode.UnprocessableEntity)]
    public async Task<IActionResult> UpdateInstallation(
        [Required]DeviceInstallation deviceInstallation)
    {
        var success = await _notificationService
            .CreateOrUpdateInstallationAsync(deviceInstallation, HttpContext.RequestAborted);
    
        if (!success)
            return new UnprocessableEntityResult();
    
        return new OkResult();
    }
    
    [HttpDelete()]
    [Route("installations/{installationId}")]
    [ProducesResponseType((int)HttpStatusCode.OK)]
    [ProducesResponseType((int)HttpStatusCode.BadRequest)]
    [ProducesResponseType((int)HttpStatusCode.UnprocessableEntity)]
    public async Task<ActionResult> DeleteInstallation(
        [Required][FromRoute]string installationId)
    {
        var success = await _notificationService
            .DeleteInstallationByIdAsync(installationId, CancellationToken.None);
    
        if (!success)
            return new UnprocessableEntityResult();
    
        return new OkResult();
    }
    
    [HttpPost]
    [Route("requests")]
    [ProducesResponseType((int)HttpStatusCode.OK)]
    [ProducesResponseType((int)HttpStatusCode.BadRequest)]
    [ProducesResponseType((int)HttpStatusCode.UnprocessableEntity)]
    public async Task<IActionResult> RequestPush(
        [Required]NotificationRequest notificationRequest)
    {
        if ((notificationRequest.Silent &&
            string.IsNullOrWhiteSpace(notificationRequest?.Action)) ||
            (!notificationRequest.Silent &&
            string.IsNullOrWhiteSpace(notificationRequest?.Text)))
            return new BadRequestResult();
    
        var success = await _notificationService
            .RequestNotificationAsync(notificationRequest, HttpContext.RequestAborted);
    
        if (!success)
            return new UnprocessableEntityResult();
    
        return new OkResult();
    }
    

Criar o Aplicativo de API

Agora você criará um aplicativo de API no Serviço de Aplicativo do Azure para hospedar o serviço de back-end.

  1. Entre no portal do Azure.

  2. Clique em Criar um recurso, pesquise e escolha Aplicativo de API e clique em Criar.

  3. Atualize os campos a seguir e clique em Criar.

    Nome do aplicativo:
    Insira um nome globalmente exclusivo para o Aplicativo de API

    Assinatura:
    Escolha a mesma Assinatura de destino em que você criou o hub de notificação.

    Grupo de recursos:
    Escolha o mesmo Grupo de Recursos em que você criou o hub de notificação.

    Localização/Plano do Serviço de Aplicativo:
    Criar um Plano do Serviço de Aplicativo

    Observação

    Altere da opção padrão para um plano que inclua compatibilidade com SSL. Caso contrário, você precisará seguir as etapas apropriadas ao trabalhar com o aplicativo móvel para impedir que solicitações HTTP sejam bloqueadas.

    Application Insights:
    Mantenha a opção sugerida (um recurso será criado usando esse nome) ou selecione um recurso existente.

  4. Quando o Aplicativo de API tiver sido provisionado, navegue até esse recurso.

  5. Anote a propriedade URL no resumo dos Fundamentos na parte superior da Visão Geral. Essa URL é o ponto de extremidade de back-end que será usado posteriormente neste tutorial.

    Observação

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

  6. Selecione Configuração na lista (em Configurações).

  7. Para cada uma das configurações abaixo, clique em Nova configuração de aplicativo para inserir o Nome e um Valor e clique em OK.

    Nome Valor
    Authentication:ApiKey <api_key_value>
    NotificationHub:Name <hub_name_value>
    NotificationHub:ConnectionString <hub_connection_string_value>

    Observação

    Essas são as mesmas configurações definidas anteriormente nas configurações do usuário. Você deve ser capaz de copiá-las. A configuração Authentication:ApiKey será necessária somente se você optar por concluir a seção Autenticar clientes usando uma chave de API. Para cenários de produção, examine opções como o Azure KeyVault. Elas foram adicionadas como configurações de aplicativo para simplificar, nesse caso.

  8. Depois que todas as configurações do aplicativo forem adicionadas, clique em Salvar e Continuar.

Publicar o serviço de back-end

Em seguida, você implanta o aplicativo no Aplicativo de API para torná-lo acessível a todos os dispositivos.

Observação

As etapas a seguir são específicas do Visual Studio para Mac. Se você estiver acompanhando com o Visual Studio 2019 no Windows, o fluxo de publicação será diferente. Confira Publicar no Serviço de Aplicativo do Azure no Windows.

  1. Altere a configuração de Depuração para Versão se você ainda não tiver feito isso.

  2. Control + Clique no projeto PushDemoApi e escolha Publicar no Azure... no menu Publicar.

  3. Siga o fluxo de autenticação se solicitado. Use a conta que você usou na seção anterior criar o Aplicativo de API.

  4. Selecione na lista o Aplicativo de API do Serviço de Aplicativo do Azure criado anteriormente como o destino de publicação e clique em Publicar.

Ao concluir, o assistente publica o aplicativo no Azure e abre o aplicativo. Anote a URL se você ainda não tiver feito isso. Essa URL é o ponto de extremidade de back-end que é usado posteriormente neste tutorial.

Validando a API publicada

  1. No Postman , abra uma nova guia, defina a solicitação como PUT e insira o endereço abaixo. Substitua o espaço reservado pelo endereço básico que você anotou na seção anterior: publicar o serviço de back-end.

    https://<app_name>.azurewebsites.net/api/notifications/installations
    

    Observação

    O endereço básico deve estar no formato https://<app_name>.azurewebsites.net/

  2. Se você optou por concluir a seção Autenticar clientes usando uma chave de API, configure os cabeçalhos de solicitação para incluir o valor de apikey.

    Chave Valor
    apikey <your_api_key>
  3. Escolha a opção bruto para o Corpo, escolha JSON na lista de opções de formato e inclua algum conteúdo de espaço reservado JSON:

    {}
    
  4. Clique em Enviar.

    Observação

    Você deve receber um status 422 UnprocessableEntity do serviço.

  5. Execute as etapas 1 a 4 novamente, mas desta vez especificando o ponto de extremidade de solicitações para validar se você receberá a resposta 400 Solicitação Incorreta.

    https://<app_name>.azurewebsites.net/api/notifications/requests
    

Observação

Ainda não é possível testar a API usando dados de solicitação válidos, pois isso exigirá informações específicas da plataforma do aplicativo móvel cliente.

Criar um aplicativo Xamarin.Forms multiplataforma

Nesta seção, você cria um aplicativo móvel Xamarin.Forms implementando notificações por push de maneira multiplataforma.

Ele permite que você registre e cancele o registro de um hub de notificação por meio do serviço de back-end que você criou.

Um alerta é exibido quando uma ação é especificada e o aplicativo está em primeiro plano. Caso contrário, as notificações aparecem no centro de notificações.

Observação

Normalmente, você executaria as ações de registro (e cancelamento de registro) durante o ponto adequado no ciclo de vida do aplicativo (ou como parte da experiência de primeira execução, talvez) sem entradas explícitas de registro/cancelamento de registro do usuário. No entanto, este exemplo exigirá uma entrada de usuário explícita para permitir que essa funcionalidade seja explorada e testada com mais facilidade.

Criar a solução do Xamarin.Forms

  1. No Visual Studio, crie uma solução do Xamarin.Forms usando um Aplicativo de Formulários em Branco como o modelo e inserindo PushDemo como o Nome do Projeto.

    Observação

    Na caixa de diálogo Configurar o Aplicativo de Formulários em Branco, verifique se o Identificador de Organização corresponde ao valor usado anteriormente e se os destinos Android e iOS estão marcados.

  2. Control + Clique na solução PushDemo e escolha Atualizar Pacotes NuGet.

  3. Control + Clique na solução PushDemo e escolha Gerenciar Pacotes NuGet.

  4. Pesquise Newtonsoft.Json e verifique se ele está marcado.

  5. Clique em Adicionar Pacotes e em Aceitar quando solicitado a aceitar os termos de licença.

  6. Compile e execute o aplicativo em cada plataforma de destino (Command + Enter) para testar as execuções do aplicativo modelo em seus dispositivos.

Implementar os componentes multiplataforma

  1. Control + Clique no projeto PushDemo, escolha Nova Pasta no menu Adicionar e clique em Adicionar usando Modelos como o Nome da Pasta.

  2. Control + Clique na pasta Modelos e escolha Novo Arquivo... no menu Adicionar.

  3. Selecione Geral>Classe Vazia, digite DeviceInstallation.cs e adicione a implementação a seguir.

    using System.Collections.Generic;
    using Newtonsoft.Json;
    
    namespace PushDemo.Models
    {
        public class DeviceInstallation
        {
            [JsonProperty("installationId")]
            public string InstallationId { get; set; }
    
            [JsonProperty("platform")]
            public string Platform { get; set; }
    
            [JsonProperty("pushChannel")]
            public string PushChannel { get; set; }
    
            [JsonProperty("tags")]
            public List<string> Tags { get; set; } = new List<string>();
        }
    }
    
  4. Adicione uma Enumeração Vazia à pasta Modelos chamada PushDemoAction.cs com a implementação a seguir.

    namespace PushDemo.Models
    {
        public enum PushDemoAction
        {
            ActionA,
            ActionB
        }
    }
    
  5. Adicione uma nova pasta ao projeto PushDemo chamada Serviços e adicione um Classe Vazia a essa pasta, chamada ServiceContainer.cs com a implementação a seguir.

    using System;
    using System.Collections.Generic;
    
    namespace PushDemo.Services
    {
       public static class ServiceContainer
       {
           static readonly Dictionary<Type, Lazy<object>> services
               = new Dictionary<Type, Lazy<object>>();
    
           public static void Register<T>(Func<T> function)
               => services[typeof(T)] = new Lazy<object>(() => function());
    
           public static T Resolve<T>()
               => (T)Resolve(typeof(T));
    
           public static object Resolve(Type type)
           {
               {
                   if (services.TryGetValue(type, out var service))
                       return service.Value;
    
                   throw new KeyNotFoundException($"Service not found for type '{type}'");
               }
           }
       }
    }
    

    Observação

    Esta é uma versão reduzida da classe ServiceContainer do repositório XamCAT. Ela será usada como um contêiner leve de IoC (inversão de controle).

  6. Adicione uma Interface Vazia à pasta Serviços chamada IDeviceInstallationService.cs e adicione o código a seguir.

    using PushDemo.Models;
    
    namespace PushDemo.Services
    {
        public interface IDeviceInstallationService
        {
            string Token { get; set; }
            bool NotificationsSupported { get; }
            string GetDeviceId();
            DeviceInstallation GetDeviceInstallation(params string[] tags);
        }
    }
    

    Observação

    Essa interface será implementada e inicializada em cada destino posteriormente para fornecer a funcionalidade específica da plataforma e as informações de DeviceInstallation exigidas pelo serviço de back-end.

  7. Adicione outra Interface Vazia à pasta Serviços chamada INotificationRegistrationService.cs e adicione o código a seguir.

    using System.Threading.Tasks;
    
    namespace PushDemo.Services
    {
        public interface INotificationRegistrationService
        {
            Task DeregisterDeviceAsync();
            Task RegisterDeviceAsync(params string[] tags);
            Task RefreshRegistrationAsync();
        }
    }
    

    Observação

    Isso manipulará a interação entre o cliente e o serviço de back-end.

  8. Adicione outra Interface Vazia à pasta Serviços chamada INotificationActionService.cs e adicione o código a seguir.

    namespace PushDemo.Services
    {
        public interface INotificationActionService
        {
            void TriggerAction(string action);
        }
    }
    

    Observação

    Isso é usado como um mecanismo simples para centralizar a manipulação de ações de notificação.

  9. Adicione uma Interface Vazia à pasta Serviços chamada IPushDemoNotificationActionService.cs que deriva da INotificationActionService, com a implementação a seguir.

    using System;
    using PushDemo.Models;
    
    namespace PushDemo.Services
    {
        public interface IPushDemoNotificationActionService : INotificationActionService
        {
            event EventHandler<PushDemoAction> ActionTriggered;
        }
    }
    

    Observação

    Esse tipo é específico para o aplicativo PushDemo e usa a enumeração PushDemoAction para identificar a ação que está sendo disparada de maneira fortemente tipada.

  10. Adicione uma Classe Vazia à pasta Serviços chamada NotificationRegistrationService.cs implementando a INotificationRegistrationService com o código a seguir.

    using System;
    using System.Net.Http;
    using System.Text;
    using System.Threading.Tasks;
    using Newtonsoft.Json;
    using PushDemo.Models;
    using Xamarin.Essentials;
    
    namespace PushDemo.Services
    {
        public class NotificationRegistrationService : INotificationRegistrationService
        {
            const string RequestUrl = "api/notifications/installations";
            const string CachedDeviceTokenKey = "cached_device_token";
            const string CachedTagsKey = "cached_tags";
    
            string _baseApiUrl;
            HttpClient _client;
            IDeviceInstallationService _deviceInstallationService;
    
            public NotificationRegistrationService(string baseApiUri, string apiKey)
            {
                _client = new HttpClient();
                _client.DefaultRequestHeaders.Add("Accept", "application/json");
                _client.DefaultRequestHeaders.Add("apikey", apiKey);
    
                _baseApiUrl = baseApiUri;
            }
    
            IDeviceInstallationService DeviceInstallationService
                => _deviceInstallationService ??
                    (_deviceInstallationService = ServiceContainer.Resolve<IDeviceInstallationService>());
    
            public async Task DeregisterDeviceAsync()
            {
                var cachedToken = await SecureStorage.GetAsync(CachedDeviceTokenKey)
                    .ConfigureAwait(false);
    
                if (cachedToken == null)
                    return;
    
                var deviceId = DeviceInstallationService?.GetDeviceId();
    
                if (string.IsNullOrWhiteSpace(deviceId))
                    throw new Exception("Unable to resolve an ID for the device.");
    
                await SendAsync(HttpMethod.Delete, $"{RequestUrl}/{deviceId}")
                    .ConfigureAwait(false);
    
                SecureStorage.Remove(CachedDeviceTokenKey);
                SecureStorage.Remove(CachedTagsKey);
            }
    
            public async Task RegisterDeviceAsync(params string[] tags)
            {
                var deviceInstallation = DeviceInstallationService?.GetDeviceInstallation(tags);
    
                await SendAsync<DeviceInstallation>(HttpMethod.Put, RequestUrl, deviceInstallation)
                    .ConfigureAwait(false);
    
                await SecureStorage.SetAsync(CachedDeviceTokenKey, deviceInstallation.PushChannel)
                    .ConfigureAwait(false);
    
                await SecureStorage.SetAsync(CachedTagsKey, JsonConvert.SerializeObject(tags));
            }
    
            public async Task RefreshRegistrationAsync()
            {
                var cachedToken = await SecureStorage.GetAsync(CachedDeviceTokenKey)
                    .ConfigureAwait(false);
    
                var serializedTags = await SecureStorage.GetAsync(CachedTagsKey)
                    .ConfigureAwait(false);
    
                if (string.IsNullOrWhiteSpace(cachedToken) ||
                    string.IsNullOrWhiteSpace(serializedTags) ||
                    string.IsNullOrWhiteSpace(DeviceInstallationService.Token) ||
                    cachedToken == DeviceInstallationService.Token)
                    return;
    
                var tags = JsonConvert.DeserializeObject<string[]>(serializedTags);
    
                await RegisterDeviceAsync(tags);
            }
    
            async Task SendAsync<T>(HttpMethod requestType, string requestUri, T obj)
            {
                string serializedContent = null;
    
                await Task.Run(() => serializedContent = JsonConvert.SerializeObject(obj))
                    .ConfigureAwait(false);
    
                await SendAsync(requestType, requestUri, serializedContent);
            }
    
            async Task SendAsync(
                HttpMethod requestType,
                string requestUri,
                string jsonRequest = null)
            {
                var request = new HttpRequestMessage(requestType, new Uri($"{_baseApiUrl}{requestUri}"));
    
                if (jsonRequest != null)
                    request.Content = new StringContent(jsonRequest, Encoding.UTF8, "application/json");
    
                var response = await _client.SendAsync(request).ConfigureAwait(false);
    
                response.EnsureSuccessStatusCode();
            }
        }
    }
    

    Observação

    O argumento apiKey só será necessário se você optar por concluir a seção Autenticar clientes usando uma Chave de API.

  11. Adicione uma Classe Vazia à pasta Serviços chamada PushDemoNotificationActionService.cs implementando a IPushDemoNotificationActionService com o código a seguir.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using PushDemo.Models;
    
    namespace PushDemo.Services
    {
        public class PushDemoNotificationActionService : IPushDemoNotificationActionService
        {
            readonly Dictionary<string, PushDemoAction> _actionMappings = new Dictionary<string, PushDemoAction>
            {
                { "action_a", PushDemoAction.ActionA },
                { "action_b", PushDemoAction.ActionB }
            };
    
            public event EventHandler<PushDemoAction> ActionTriggered = delegate { };
    
            public void TriggerAction(string action)
            {
                if (!_actionMappings.TryGetValue(action, out var pushDemoAction))
                    return;
    
                List<Exception> exceptions = new List<Exception>();
    
                foreach (var handler in ActionTriggered?.GetInvocationList())
                {
                    try
                    {
                        handler.DynamicInvoke(this, pushDemoAction);
                    }
                    catch (Exception ex)
                    {
                        exceptions.Add(ex);
                    }
                }
    
                if (exceptions.Any())
                    throw new AggregateException(exceptions);
            }
        }
    }
    
  12. Adicione uma Classe Vazia ao projeto PushDemo chamada Config.cs com a implementação a seguir.

    namespace PushDemo
    {
        public static partial class Config
        {
            public static string ApiKey = "API_KEY";
            public static string BackendServiceEndpoint = "BACKEND_SERVICE_ENDPOINT";
        }
    }
    

    Observação

    Isso é usado como uma forma simples de manter os segredos fora do controle do código-fonte. Você pode substituir esses valores como parte de um build automatizado ou substituí-los usando uma classe parcial local. Isso será feito na próxima etapa.

    O campo ApiKey só será necessário se você optar por concluir a seção Autenticar clientes usando uma Chave de API.

  13. Adicione outra Classe Vazia ao projeto PushDemo, desta vez chamada Config.local_secrets.cs, com a implementação a seguir.

    namespace PushDemo
    {
        public static partial class Config
        {
            static Config()
            {
                ApiKey = "<your_api_key>";
                BackendServiceEndpoint = "<your_api_app_url>";
            }
        }
    }
    

    Observação

    Substitua os valores do espaço reservado pelos seus. Você deve tê-los anotado ao criar o serviço de back-end. A URL do Aplicativo de API deve ser https://<api_app_name>.azurewebsites.net/. Lembre-se de adicionar *.local_secrets.* ao arquivo gitignore para evitar confirmar esse arquivo.

    O campo ApiKey só será necessário se você optar por concluir a seção Autenticar clientes usando uma Chave de API.

  14. Adicione uma Classe Vazia ao projeto PushDemo chamada Bootstrap.cs com a implementação a seguir.

    using System;
    using PushDemo.Services;
    
    namespace PushDemo
    {
        public static class Bootstrap
        {
            public static void Begin(Func<IDeviceInstallationService> deviceInstallationService)
            {
                ServiceContainer.Register(deviceInstallationService);
    
                ServiceContainer.Register<IPushDemoNotificationActionService>(()
                    => new PushDemoNotificationActionService());
    
                ServiceContainer.Register<INotificationRegistrationService>(()
                    => new NotificationRegistrationService(
                        Config.BackendServiceEndpoint,
                        Config.ApiKey));
            }
        }
    }
    

    Observação

    O método Begin será chamado em cada plataforma quando o aplicativo iniciar a passagem em uma implementação específica da plataforma de IDeviceInstallationService.

    O argumento do construtor NotificationRegistrationServiceapiKey só será necessário se você optar por concluir os clientes authenticate usando uma seção chave de API.

Implementar a interface do usuário multiplataforma

  1. No projeto PushDemo, abra MainPage.xaml e substitua o controle StackLayout pelo seguinte.

    <StackLayout VerticalOptions="EndAndExpand"  
                 HorizontalOptions="FillAndExpand"
                 Padding="20,40">
        <Button x:Name="RegisterButton"
                Text="Register"
                Clicked="RegisterButtonClicked" />
        <Button x:Name="DeregisterButton"
                Text="Deregister"
                Clicked="DeregisterButtonClicked" />
    </StackLayout>
    
  2. Agora, em MainPage.xaml.cs, adicione um campo de suporte readonly para armazenar uma referência à implementação INotificationRegistrationService.

    readonly INotificationRegistrationService _notificationRegistrationService;
    
  3. No construtor MainPage, resolva a implementação de INotificationRegistrationService usando o ServiceContainer e atribua-o ao campo de suporte notificationRegistrationService.

    public MainPage()
    {
        InitializeComponent();
    
        _notificationRegistrationService =
            ServiceContainer.Resolve<INotificationRegistrationService>();
    }
    
  4. Implemente os manipuladores de eventos para os botões RegisterButton e DeregisterButton dos eventos Clicados chamando os métodos Register/Deregister correspondentes.

    void RegisterButtonClicked(object sender, EventArgs e)
        => _notificationRegistrationService.RegisterDeviceAsync().ContinueWith((task)
            => { ShowAlert(task.IsFaulted ?
                    task.Exception.Message :
                    $"Device registered"); });
    
    void DeregisterButtonClicked(object sender, EventArgs e)
        => _notificationRegistrationService.DeregisterDeviceAsync().ContinueWith((task)
            => { ShowAlert(task.IsFaulted ?
                    task.Exception.Message :
                    $"Device deregistered"); });
    
    void ShowAlert(string message)
        => MainThread.BeginInvokeOnMainThread(()
            => DisplayAlert("PushDemo", message, "OK").ContinueWith((task)
                => { if (task.IsFaulted) throw task.Exception; }));
    
  5. Agora, em App.xaml.cs, verifique se os namespaces a seguir estão referenciados.

    using PushDemo.Models;
    using PushDemo.Services;
    using Xamarin.Essentials;
    using Xamarin.Forms;
    
  6. Implemente o manipulador de eventos para o evento IPushDemoNotificationActionServiceActionTriggered.

    void NotificationActionTriggered(object sender, PushDemoAction e)
        => ShowActionAlert(e);
    
    void ShowActionAlert(PushDemoAction action)
        => MainThread.BeginInvokeOnMainThread(()
            => MainPage?.DisplayAlert("PushDemo", $"{action} action received", "OK")
                .ContinueWith((task) => { if (task.IsFaulted) throw task.Exception; }));
    
  7. No construtor de aplicativos, resolva a implementação IPushNotificationActionService usando o ServiceContainer e assine o evento IPushDemoNotificationActionServiceActionTriggered.

    public App()
    {
        InitializeComponent();
    
        ServiceContainer.Resolve<IPushDemoNotificationActionService>()
            .ActionTriggered += NotificationActionTriggered;
    
        MainPage = new MainPage();
    }
    

    Observação

    Isso serve simplesmente para demonstrar o recebimento e a propagação de ações de notificação por push. Normalmente, elas seriam manipuladas silenciosamente, por exemplo, navegando para uma exibição específica ou atualizando alguns dados em vez de exibir um alerta por meio da Página raiz, MainPage nesse caso.

Configurar o projeto nativo do Android para notificações por push

Validar o nome e as permissões do pacote

  1. No PushDemo.Android, abra as Opções do Projeto e Aplicativo Android na seção Build.

  2. Verifique se o nome do pacote corresponde ao valor usado no projeto FirebaseConsolePushDemo. O Nome do pacote estava no formato com.<organization>.pushdemo.

  3. Defina a Versão Mínima do Android como Android 8.0 (nível da API 26) e a Versão de Destino do Android para o nível da API mais recente.

    Observação

    Somente os dispositivos que executam Nível da API 26 e superior são compatíveis com os fins deste tutorial, no entanto, você pode estendê-lo para compatibilidade com dispositivos que executam versões mais antigas.

  4. Verifique se as permissões INTERNET e READ_PHONE_STATE estão habilitadas em Permissões necessárias.

  5. Clique em OK

Adicionar os pacotes Xamarin Google Play Services Base e Xamarin.Firebase.Messaging

  1. Em PushDemo.Android, Control + Clique na pasta Pacotes e escolha Gerenciar Pacotes NuGet... .

  2. Pesquise Xamarin.GooglePlayServices.Base (não Basement) e verifique se ele está marcado.

  3. Pesquise Xamarin.Firebase.Messaging e verifique se ele está marcado.

  4. Clique em Adicionar Pacotes e em Aceitar quando solicitado a aceitar os termos de licença.

Adicione o arquivo JSON de Serviços do Google

  1. Control + Clique no projeto PushDemo.Android e escolha Arquivo existente... no menu Adicionar.

  2. Escolha o arquivo google-services.json que você baixou anteriormente ao configurar o projeto PushDemo no Console do Firebase e clique em Abrir.

  3. Quando solicitado, escolha Copiar o arquivo no diretório.

  4. Control + Clique no arquivo google-services.json dentro do projeto PushDemo.Android e verifique se GoogleServicesJson está definido como Ação de Build.

Manipular notificações por push para o Android

  1. Control + Clique no projeto PushDemo.Android, escolha Nova Pasta no menu Adicionar e clique em Adicionar usando Serviços como o Nome da Pasta.

  2. Control + Clique na pasta Serviços e escolha Novo Arquivo... no menu Adicionar.

  3. Selecione Geral>Classe Vazia, digite DeviceInstallationService.cs como o Nome e clique em Novo, adicionando a implementação a seguir.

    using System;
    using Android.App;
    using Android.Gms.Common;
    using PushDemo.Models;
    using PushDemo.Services;
    using static Android.Provider.Settings;
    
    namespace PushDemo.Droid.Services
    {
        public class DeviceInstallationService : IDeviceInstallationService
        {
            public string Token { get; set; }
    
            public bool NotificationsSupported
                => GoogleApiAvailability.Instance
                    .IsGooglePlayServicesAvailable(Application.Context) == ConnectionResult.Success;
    
            public string GetDeviceId()
                => Secure.GetString(Application.Context.ContentResolver, Secure.AndroidId);
    
            public DeviceInstallation GetDeviceInstallation(params string[] tags)
            {
                if (!NotificationsSupported)
                    throw new Exception(GetPlayServicesError());
    
                if (string.IsNullOrWhiteSpace(Token))
                    throw new Exception("Unable to resolve token for FCM");
    
                var installation = new DeviceInstallation
                {
                    InstallationId = GetDeviceId(),
                    Platform = "fcm",
                    PushChannel = Token
                };
    
                installation.Tags.AddRange(tags);
    
                return installation;
            }
    
            string GetPlayServicesError()
            {
                int resultCode = GoogleApiAvailability.Instance.IsGooglePlayServicesAvailable(Application.Context);
    
                if (resultCode != ConnectionResult.Success)
                    return GoogleApiAvailability.Instance.IsUserResolvableError(resultCode) ?
                               GoogleApiAvailability.Instance.GetErrorString(resultCode) :
                               "This device is not supported";
    
                return "An error occurred preventing the use of push notifications";
            }
        }
    }
    

    Observação

    Essa classe fornece uma ID exclusiva (usando Secure.AndroidId) como parte do conteúdo de registro do hub de notificação.

  4. Adicione outra Classe Vazia à pasta Serviços chamada PushNotificationFirebaseMessagingService.cs e adicione a implementação a seguir.

    using Android.App;
    using Android.Content;
    using Firebase.Messaging;
    using PushDemo.Services;
    
    namespace PushDemo.Droid.Services
    {
        [Service]
        [IntentFilter(new[] { "com.google.firebase.MESSAGING_EVENT" })]
        public class PushNotificationFirebaseMessagingService : FirebaseMessagingService
        {
            IPushDemoNotificationActionService _notificationActionService;
            INotificationRegistrationService _notificationRegistrationService;
            IDeviceInstallationService _deviceInstallationService;
    
            IPushDemoNotificationActionService NotificationActionService
                => _notificationActionService ??
                    (_notificationActionService =
                    ServiceContainer.Resolve<IPushDemoNotificationActionService>());
    
            INotificationRegistrationService NotificationRegistrationService
                => _notificationRegistrationService ??
                    (_notificationRegistrationService =
                    ServiceContainer.Resolve<INotificationRegistrationService>());
    
            IDeviceInstallationService DeviceInstallationService
                => _deviceInstallationService ??
                    (_deviceInstallationService =
                    ServiceContainer.Resolve<IDeviceInstallationService>());
    
            public override void OnNewToken(string token)
            {
                DeviceInstallationService.Token = token;
    
                NotificationRegistrationService.RefreshRegistrationAsync()
                    .ContinueWith((task) => { if (task.IsFaulted) throw task.Exception; });
            }
    
            public override void OnMessageReceived(RemoteMessage message)
            {
                if(message.Data.TryGetValue("action", out var messageAction))
                    NotificationActionService.TriggerAction(messageAction);
            }
        }
    }
    
  5. Em MainActivity.cs, verifique se os namespaces a seguir foram adicionados à parte superior do arquivo.

    using System;
    using Android.App;
    using Android.Content;
    using Android.Content.PM;
    using Android.OS;
    using Android.Runtime;
    using Firebase.Iid;
    using PushDemo.Droid.Services;
    using PushDemo.Services;
    
  6. Em MainActivity.cs, defina a LaunchMode como SingleTop para que MainActivity não seja criada novamente quando aberta.

    [Activity(
        Label = "PushDemo",
        LaunchMode = LaunchMode.SingleTop,
        Icon = "@mipmap/icon",
        Theme = "@style/MainTheme",
        MainLauncher = true,
        ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
    
  7. Adicione propriedades privadas e os campos de suporte correspondentes para armazenar uma referência para as implementações IPushNotificationActionService e IDeviceInstallationService.

    IPushDemoNotificationActionService _notificationActionService;
    IDeviceInstallationService _deviceInstallationService;
    
    IPushDemoNotificationActionService NotificationActionService
        => _notificationActionService ??
            (_notificationActionService =
            ServiceContainer.Resolve<IPushDemoNotificationActionService>());
    
    IDeviceInstallationService DeviceInstallationService
        => _deviceInstallationService ??
            (_deviceInstallationService =
            ServiceContainer.Resolve<IDeviceInstallationService>());
    
  8. Implemente a interface IOnSuccessListener para recuperar e armazenar o token do Firebase.

    public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity, Android.Gms.Tasks.IOnSuccessListener
    {
        ...
    
        public void OnSuccess(Java.Lang.Object result)
            => DeviceInstallationService.Token =
                result.Class.GetMethod("getToken").Invoke(result).ToString();
    }
    
  9. Adicione um novo método chamado ProcessNotificationActions que verificará se uma determinada Intenção tem um valor extra chamado ação. Dispare de maneira condicional essa ação usando a implementação IPushDemoNotificationActionService.

    void ProcessNotificationActions(Intent intent)
    {
        try
        {
            if (intent?.HasExtra("action") == true)
            {
                var action = intent.GetStringExtra("action");
    
                if (!string.IsNullOrEmpty(action))
                    NotificationActionService.TriggerAction(action);
            }
        }
        catch (Exception ex)
        {
            System.Diagnostics.Debug.WriteLine(ex.Message);
        }
    }
    
  10. Substitua o método OnNewIntent para chamar o método de ProcessNotificationActions.

    protected override void OnNewIntent(Intent intent)
    {
        base.OnNewIntent(intent);
        ProcessNotificationActions(intent);
    }
    

    Observação

    Como LaunchMode para a Atividade está definida como SingleTop, uma Intenção será enviada para a instância existente da Atividade por meio do método OnNewIntent em vez do método OnCreate e, portanto, você deverá manipular uma intenção de entrada nos métodos OnCreate e OnNewIntent.

  11. Atualize o método OnCreate para chamar Bootstrap.Begin logo após a chamada para base.OnCreate, passando a implementação específica da plataforma do IDeviceInstallationService.

    Bootstrap.Begin(() => new DeviceInstallationService());
    
  12. No mesmo método, chame de maneira condicional GetInstanceId na instância do FirebaseApp, logo após a chamada para Bootstrap.Begin, adicionando MainActivity como IOnSuccessListener.

    if (DeviceInstallationService.NotificationsSupported)
    {
        FirebaseInstanceId.GetInstance(Firebase.FirebaseApp.Instance)
            .GetInstanceId()
            .AddOnSuccessListener(this);
    }
    
  13. Ainda em OnCreate, chame ProcessNotificationActions imediatamente após a chamada para LoadApplication, passando a Intenção atual.

    ...
    
    LoadApplication(new App());
    
    ProcessNotificationActions(Intent);
    

Observação

Você deve registrar novamente o aplicativo sempre que executá-lo e interrompê-lo em uma sessão de depuração para continuar a receber notificações por push.

Configurar o projeto nativo do iOS para notificações por push

Configurar Info.plist and Entitlements.plist

  1. Verifique se você entrou na Conta de Desenvolvedor da Apple em Visual Studio>Preferências...>Publicação>Contas de Desenvolvedor da Apple e se o Certificado apropriado e o Perfil de Provisão foram baixados. Você deve ter criado esses ativos como parte das etapas anteriores.

  2. No PushDemo.iOS, abra Info.plist e verifique se o BundleIdentifier corresponde ao valor usado para o respectivo perfil de provisionamento no Portal do Desenvolvedor da Apple. O BundleIdentifier estava no formato com.<organization>.PushDemo.

  3. No mesmo arquivo, defina a Versão mínima do sistema como 13.0.

    Observação

    Somente os dispositivos que executam iOS 13.0 e superior são compatíveis com os fins deste tutorial, no entanto, você pode estendê-lo para compatibilidade com dispositivos que executam versões mais antigas.

  4. Abra as Opções do Projeto para PushDemo.iOS (clique duas vezes no projeto).

  5. Em opções de Project, em Build > iOS Bundle Signing, verifique se sua conta de Desenvolvedor está selecionada em Equipe. Em seguida, verifique se "Gerenciar assinatura automaticamente" está selecionado e se seu Certificado de autenticação e Perfil de Provisão são selecionados automaticamente.

    Observação

    Se o Certificado de autenticação e o Perfil de Provisão não tiverem sido selecionados automaticamente, escolha Provisionamento Manual e clique em Opções de Assinatura de Pacote. Verifique se sua Equipe está selecionada em Identidade de Assinatura e seu perfil de provisão específico do PushDemo está selecionado para Perfil de Provisão para as configurações de Depuração e Versão, garantindo que iPhone esteja selecionado como a Plataforma em ambos os casos.

  6. Em PushDemo.iOS, abra Entitlements.plist e verifique se a opção Habilitar Notificações por Push está marcada quando exibida na guia Direitos. Em seguida, verifique se a configuração Ambiente da APS está definida como desenvolvimento quando exibida na guia Origem.

Manipular notificações por push para iOS

  1. Control + Clique no projeto PushDemo.iOS, escolha Nova Pasta no menu Adicionar e clique em Adicionar usando Serviços como o Nome da Pasta.

  2. Control + Clique na pasta Serviços e escolha Novo Arquivo... no menu Adicionar.

  3. Selecione Geral>Classe Vazia, digite DeviceInstallationService.cs como o Nome e clique em Novo, adicionando a implementação a seguir.

    using System;
    using PushDemo.Models;
    using PushDemo.Services;
    using UIKit;
    
    namespace PushDemo.iOS.Services
    {
        public class DeviceInstallationService : IDeviceInstallationService
        {
            const int SupportedVersionMajor = 13;
            const int SupportedVersionMinor = 0;
    
            public string Token { get; set; }
    
            public bool NotificationsSupported
                => UIDevice.CurrentDevice.CheckSystemVersion(SupportedVersionMajor, SupportedVersionMinor);
    
            public string GetDeviceId()
                => UIDevice.CurrentDevice.IdentifierForVendor.ToString();
    
            public DeviceInstallation GetDeviceInstallation(params string[] tags)
            {
                if (!NotificationsSupported)
                    throw new Exception(GetNotificationsSupportError());
    
                if (string.IsNullOrWhiteSpace(Token))
                    throw new Exception("Unable to resolve token for APNS");
    
                var installation = new DeviceInstallation
                {
                    InstallationId = GetDeviceId(),
                    Platform = "apns",
                    PushChannel = Token
                };
    
                installation.Tags.AddRange(tags);
    
                return installation;
            }
    
            string GetNotificationsSupportError()
            {
                if (!NotificationsSupported)
                    return $"This app only supports notifications on iOS {SupportedVersionMajor}.{SupportedVersionMinor} and above. You are running {UIDevice.CurrentDevice.SystemVersion}.";
    
                if (Token == null)
                    return $"This app can support notifications but you must enable this in your settings.";
    
    
                return "An error occurred preventing the use of push notifications";
            }
        }
    }
    

    Observação

    Essa classe fornece uma ID exclusiva (usando o valor UIDevice.IdentifierForVendor) e o conteúdo de registro do hub de notificação.

  4. Adicione uma nova pasta ao projeto PushDemo.iOS chamada Extensões e uma Classe Vazia a essa pasta, chamada NSDataExtensions.cs com a implementação a seguir.

    using System.Text;
    using Foundation;
    
    namespace PushDemo.iOS.Extensions
    {
        internal static class NSDataExtensions
        {
            internal static string ToHexString(this NSData data)
            {
                var bytes = data.ToArray();
    
                if (bytes == null)
                    return null;
    
                StringBuilder sb = new StringBuilder(bytes.Length * 2);
    
                foreach (byte b in bytes)
                    sb.AppendFormat("{0:x2}", b);
    
                return sb.ToString().ToUpperInvariant();
            }
        }
    }
    
  5. Em AppDelegate.cs, verifique se os namespaces a seguir foram adicionados à parte superior do arquivo.

    using System;
    using System.Diagnostics;
    using System.Threading.Tasks;
    using Foundation;
    using PushDemo.iOS.Extensions;
    using PushDemo.iOS.Services;
    using PushDemo.Services;
    using UIKit;
    using UserNotifications;
    using Xamarin.Essentials;
    
  6. Adicione propriedades privadas e seus respectivos campos de suporte para armazenar uma referência para as implementações IPushDemoNotificationActionService, INotificationRegistrationService e IDeviceInstallationService.

    IPushDemoNotificationActionService _notificationActionService;
    INotificationRegistrationService _notificationRegistrationService;
    IDeviceInstallationService _deviceInstallationService;
    
    IPushDemoNotificationActionService NotificationActionService
        => _notificationActionService ??
            (_notificationActionService =
            ServiceContainer.Resolve<IPushDemoNotificationActionService>());
    
    INotificationRegistrationService NotificationRegistrationService
        => _notificationRegistrationService ??
            (_notificationRegistrationService =
            ServiceContainer.Resolve<INotificationRegistrationService>());
    
    IDeviceInstallationService DeviceInstallationService
        => _deviceInstallationService ??
            (_deviceInstallationService =
            ServiceContainer.Resolve<IDeviceInstallationService>());
    
  7. Adicione o método RegisterForRemoteNotifications para registrar configurações de notificação do usuário e para notificações remotas com a APNs.

    void RegisterForRemoteNotifications()
    {
        MainThread.BeginInvokeOnMainThread(() =>
        {
            var pushSettings = UIUserNotificationSettings.GetSettingsForTypes(
                UIUserNotificationType.Alert |
                UIUserNotificationType.Badge |
                UIUserNotificationType.Sound,
                new NSSet());
    
            UIApplication.SharedApplication.RegisterUserNotificationSettings(pushSettings);
            UIApplication.SharedApplication.RegisterForRemoteNotifications();
        });
    }
    
  8. Adicione o método CompleteRegistrationAsync para definir o valor da propriedade IDeviceInstallationService.Token. Atualize o registro e armazene em cache o token do dispositivo se ele tiver sido atualizado desde a última vez em que foi armazenado.

    Task CompleteRegistrationAsync(NSData deviceToken)
    {
        DeviceInstallationService.Token = deviceToken.ToHexString();
        return NotificationRegistrationService.RefreshRegistrationAsync();
    }
    
  9. Adicione o método ProcessNotificationActions para processar os dados da notificação do NSDictionary, chamando condicionalmente NotificationActionService.TriggerAction.

    void ProcessNotificationActions(NSDictionary userInfo)
    {
        if (userInfo == null)
            return;
    
        try
        {
            var actionValue = userInfo.ObjectForKey(new NSString("action")) as NSString;
    
            if (!string.IsNullOrWhiteSpace(actionValue?.Description))
                NotificationActionService.TriggerAction(actionValue.Description);
        }
        catch (Exception ex)
        {
            Debug.WriteLine(ex.Message);
        }
    }
    
  10. Substitua o método RegisteredForRemoteNotifications passando o argumento deviceToken para o método CompleteRegistrationAsync.

    public override void RegisteredForRemoteNotifications(
        UIApplication application,
        NSData deviceToken)
        => CompleteRegistrationAsync(deviceToken).ContinueWith((task)
            => { if (task.IsFaulted) throw task.Exception; });
    
  11. Substitua o método ReceivedRemoteNotification passando o argumento userInfo para o método ProcessNotificationActions.

    public override void ReceivedRemoteNotification(
        UIApplication application,
        NSDictionary userInfo)
        => ProcessNotificationActions(userInfo);
    
  12. Substitua o método FailedToRegisterForRemoteNotifications para registrar o erro.

    public override void FailedToRegisterForRemoteNotifications(
        UIApplication application,
        NSError error)
        => Debug.WriteLine(error.Description);
    

    Observação

    Esse é um espaço reservado. Você vai querer implementar o registro em log e o tratamento de erro apropriados para cenários de produção.

  13. Atualize o método FinishedLaunching para chamar Bootstrap.Begin logo após a chamada para Forms.Init, passando a implementação específica da plataforma do IDeviceInstallationService.

    Bootstrap.Begin(() => new DeviceInstallationService());
    
  14. No mesmo método, solicite a autorização de maneira condicional e registre-se para notificações remotas imediatamente após Bootstrap.Begin.

    if (DeviceInstallationService.NotificationsSupported)
    {
        UNUserNotificationCenter.Current.RequestAuthorization(
                UNAuthorizationOptions.Alert |
                UNAuthorizationOptions.Badge |
                UNAuthorizationOptions.Sound,
                (approvalGranted, error) =>
                {
                    if (approvalGranted && error == null)
                        RegisterForRemoteNotifications();
                });
    }
    
  15. Ainda em FinishedLaunching, chame ProcessNotificationActions imediatamente após a chamada para LoadApplication se o argumento options contiver o UIApplication.LaunchOptionsRemoteNotificationKey passando o objeto userInfo resultante.

    using (var userInfo = options?.ObjectForKey(
        UIApplication.LaunchOptionsRemoteNotificationKey) as NSDictionary)
            ProcessNotificationActions(userInfo);
    

Testar a solução

Agora é possível testar o envio de notificações por meio do serviço de back-end.

Enviar uma notificação de teste

  1. Abra uma nova guia no Postman.

  2. Defina a solicitação como POST e insira o seguinte endereço:

    https://<app_name>.azurewebsites.net/api/notifications/requests
    
  3. Se você optou por concluir a seção Autenticar clientes usando uma chave de API, configure os cabeçalhos de solicitação para incluir o valor de apikey.

    Chave Valor
    apikey <your_api_key>
  4. Escolha a opção bruto para o Corpo, escolha JSON na lista de opções de formato e inclua algum conteúdo de espaço reservado JSON:

    {
        "text": "Message from Postman!",
        "action": "action_a"
    }
    
  5. Selecione o botão Código, que está abaixo do botão Salvar no canto superior direito da janela. A solicitação deve ser semelhante ao seguinte exemplo quando exibida para HTML (dependendo se você incluiu um cabeçalho apiKey):

    POST /api/notifications/requests HTTP/1.1
    Host: https://<app_name>.azurewebsites.net
    apikey: <your_api_key>
    Content-Type: application/json
    
    {
        "text": "Message from backend service",
        "action": "action_a"
    }
    
  6. Execute o aplicativo PushDemo em uma ou ambas as plataformas de destino (Android e iOS).

    Observação

    Se você estiver testando no Android não execute em Depuração ou, se o aplicativo tiver sido implantado executando o aplicativo, force o fechamento do aplicativo e inicie-o novamente com o inicializador.

  7. No aplicativo PushDemo, toque no botão Registrar.

  8. De volta ao Postman , feche a janela Gerar Trechos de Código (se você ainda não tiver feito isso), e clique no botão Enviar.

  9. Valide se você recebeu uma resposta 200 OK no Postman e se o alerta aparece no aplicativo mostrando Ação ActionA recebida.

  10. Feche o aplicativo PushDemo e clique no botão Enviar novamente no Postman .

  11. Valide se você recebe uma resposta 200 OK no Postman novamente. Verifique se aparece uma notificação na área de notificação do aplicativo PushDemo com a mensagem correta.

  12. Toque na notificação para confirmar que ela abre o aplicativo e exibe o alerta Ação ActionA recebida.

  13. De volta ao Postman , modifique o corpo da solicitação anterior para enviar uma notificação silenciosa especificando action_b em vez de action_a como o valor da ação.

    {
        "action": "action_b",
        "silent": true
    }
    
  14. Com o aplicativo ainda aberto, clique no botão Enviar no Postman .

  15. Valide se você recebeu uma resposta 200 OK no Postman e se o alerta aparece no aplicativo mostrando Ação ActionB recebida em vez de Ação ActionA recebida.

  16. Feche o aplicativo PushDemo e clique no botão Enviar novamente no Postman .

  17. Valide se você obtém uma resposta 200 OK no Postman e se a notificação silenciosa não aparece na área de notificação.

Solução de problemas

Sem resposta do serviço de back-end

Ao testar localmente, verifique se o serviço de back-end está em execução e está usando a porta correta.

Se estiver testando no Aplicativo de API do Azure, verifique se o serviço está em execução e foi implantado e se foi iniciado sem erros.

Verifique se você especificou corretamente o endereço básico no Postman ou na configuração de aplicativos móveis durante o teste por meio do cliente. O endereço básico deve ser indicativo de https://<api_name>.azurewebsites.net/ ou https://localhost:5001/ ao testar localmente.

Não recebimento de notificações no Android após iniciar ou parar uma sessão de depuração

Registre-se novamente depois de iniciar ou parar uma sessão de depuração. O depurador fará com que um novo token do Firebase seja gerado. A instalação do hub de notificação também deve ser atualizada.

Recebimento de um código de status 401 do serviço de back-end

Valide se você está definindo o cabeçalho de solicitação apikey e se esse valor corresponde ao que você configurou para o serviço de back-end.

Se você receber esse erro ao testar localmente, verifique se o valor de chave definido na configuração do cliente corresponde ao valor de configuração de usuário Authentication:ApiKey usado pela API.

Se você estiver testando com um Aplicativo de API, verifique se o valor da chave no arquivo de configuração do cliente corresponde à configuração de aplicativo de Authentication:ApiKey que você está usando no Aplicativo de API.

Observação

Se você tiver criado ou alterado essa configuração depois de ter implantado o serviço de back-end, deverá reiniciar o serviço para que isso entre em vigor.

Se você optou por não concluir a seção Autenticar clientes usando uma Chave de API, verifique se você não aplicou o atributo Authorize à classe NotificationsController.

Recebimento de um código de status 404 do serviço de back-end

Valide se o ponto de extremidade e o método de solicitação HTTP estão corretos. Por exemplo, os pontos de extremidade devem ser:

  • [PUT]https://<api_name>.azurewebsites.net/api/notifications/installations
  • [DELETE]https://<api_name>.azurewebsites.net/api/notifications/installations/<installation_id>
  • [POST]https://<api_name>.azurewebsites.net/api/notifications/requests

Ou, ao testar localmente:

  • [PUT]https://localhost:5001/api/notifications/installations
  • [DELETE]https://localhost:5001/api/notifications/installations/<installation_id>
  • [POST]https://localhost:5001/api/notifications/requests

Ao especificar o endereço básico no aplicativo cliente, verifique se ele termina com um /. O endereço básico deve ser indicativo de https://<api_name>.azurewebsites.net/ ou https://localhost:5001/ ao testar localmente.

Não é possível registrar e uma mensagem de erro do hub de notificação é exibida

Verifique se o dispositivo de teste tem conectividade de rede. Em seguida, determine o código de status de resposta HTTP definindo um ponto de interrupção para inspecionar o valor da propriedade StatusCode no HttpResponse.

Examine as sugestões de solução de problemas anteriores, quando aplicável com base no código de status.

Defina um ponto de interrupção nas linhas que retornam esses códigos de status específicos para a respectiva API. Em seguida, tente chamar o serviço de back-end ao depurar localmente.

Valide se o serviço de back-end está funcionando conforme o esperado via Postman usando o conteúdo apropriado. Use o conteúdo real criado pelo código do cliente para a plataforma em questão.

Examine as seções de configuração específicas da plataforma para garantir que nenhuma etapa tenha sido perdida. Verifique se os valores adequados estão sendo resolvidos para as variáveis installation id e token para a plataforma correta.

Mensagem de erro Não é possível resolver uma ID para o dispositivo é exibida

Examine as seções de configuração específicas da plataforma para garantir que nenhuma etapa tenha sido perdida.

Próximas etapas

Agora você tem um aplicativo Xamarin.Forms básico conectado a um hub de notificação por meio de um serviço de back-end e pode enviar e receber notificações.

Provavelmente, você precisará adaptar o exemplo usado neste tutorial para se adequar ao seu cenário. Também é recomendável implementar o tratamento de erro, a lógica de repetição e o registro em log mais robustos.

O Visual Studio App Center pode ser rapidamente incorporado a aplicativos móveis, fornecendo análise e diagnóstico para auxiliar na solução de problemas.