Tutorial: Enviar notificações push para aplicações Xamarin.Forms com os Hubs de Notificação do Azure através de um serviço de back-end

Transferir Exemplo Transferir o exemplo

Neste tutorial, vai utilizar os Hubs de Notificação do Azure para enviar notificações push para uma aplicação Xamarin.Forms direcionada para Android e iOS.

É utilizado um back-end da API Web ASP.NET Core para processar o registo de dispositivos do cliente com a abordagem de instalação mais recente e melhor. O serviço também enviará notificações push de forma multiplataforma.

Estas operações são processadas com o SDK dos Hubs de Notificação para operações de back-end. São fornecidos mais detalhes sobre a abordagem geral na documentação De registo a partir do back-end da aplicação .

Este tutorial orienta-o através dos seguintes passos:

Pré-requisitos

Para acompanhar, tem de:

  • Uma subscrição do Azure onde pode criar e gerir recursos.
  • Um Mac com Visual Studio para Mac instalado ou um PC com o Visual Studio 2019.
  • Os utilizadores do Visual Studio 2019 também têm de ter o Desenvolvimento móvel com cargas de trabalho de desenvolvimento .NET e ASP.NET e Web instaladas.
  • A capacidade de executar a aplicação em dispositivos Android (dispositivos físicos ou emuladores) ou iOS (apenas dispositivos físicos).

Para Android, tem de ter:

  • Um programador desbloqueou um dispositivo físico ou um emulador (com a API 26 ou superior com o Google Play Services instalado).

Para iOS, tem de ter:

Nota

O Simulador de iOS não suporta notificações remotas, pelo que é necessário um dispositivo físico ao explorar este exemplo no iOS. No entanto, não precisa de executar a aplicação no Android e no iOS para concluir este tutorial.

Pode seguir os passos neste exemplo de princípios inicialmente sem experiência anterior. No entanto, irá beneficiar de estar familiarizado com os seguintes aspetos.

Importante

Os passos fornecidos são específicos para Visual Studio para Mac. É possível acompanhar a utilização do Visual Studio 2019 , mas podem existir algumas diferenças para reconciliar. Por exemplo, descrições da interface de utilizador e fluxos de trabalho, nomes de modelos, configuração do ambiente, etc.

Configurar os Serviços de Notificação Push e o Hub de Notificação do Azure

Nesta secção, vai configurar o Firebase Cloud Messaging (FCM) e oApple Push Notification Services (APNS). Em seguida, vai criar e configurar um hub de notificação para trabalhar com esses serviços.

Criar um projeto do Firebase e ativar o Firebase Cloud Messaging para Android

  1. Inicie sessão na consola do Firebase. Crie um novo projeto firebase ao introduzir PushDemo como o Nome do projeto.

    Nota

    Será gerado um nome exclusivo para si. Por predefinição, isto é composto por uma variante minúscula do nome que forneceu e um número gerado separado por um traço. Pode alterá-lo se quiser desde que ainda seja globalmente exclusivo.

  2. Depois de criar o projeto, selecione Adicionar Firebase à sua aplicação Android.

    Adicionar o Firebase à sua aplicação Android

  3. Na página Adicionar Firebase à sua aplicação Android , siga os seguintes passos.

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

      Especificar o nome do pacote

    2. Selecione Registar aplicação.

    3. Selecione Transferir google-services.json. Em seguida, guarde o ficheiro numa pasta local para ser utilizado mais tarde e selecione Seguinte.

      Transferir google-services.json

    4. Selecione Seguinte.

    5. Selecione Continuar para a consola

      Nota

      Se o botão Continuar para a consola não estiver ativado, devido à verificação de instalação de verificação e, em seguida, selecione Ignorar este passo.

  4. Na consola do Firebase, selecione a engrenagem do projeto. Em seguida, selecione Definições do Projeto.

    Selecionar Definições do Projeto

    Nota

    Se ainda não transferiu o ficheiro google-services.json , pode transferi-lo nesta página.

  5. Mude para o separador Cloud Messaging na parte superior. Copie e guarde a chave do Servidor para utilização posterior. Utilize este valor para configurar o seu hub de notificação.

    Copiar chave do servidor

Registar a sua aplicação iOS para notificações push

Para enviar notificações push para uma aplicação iOS, registe a sua aplicação na Apple e registe-se também para notificações push.

  1. Se ainda não registou a sua aplicação, navegue para o Portal de Aprovisionamento do iOS no Centro de Programadores da Apple. Inicie sessão no portal com o seu ID Apple, navegue para Certificados, Identificadores & Perfis e, em seguida, selecione Identificadores. Clique + para registar uma nova aplicação.

    Página IDs da Aplicação do Portal de Aprovisionamento do iOS

  2. No ecrã Registar um Novo Identificador , selecione o botão de opção IDs de Aplicação . Em seguida, selecione Continuar.

    Portal de Aprovisionamento do iOS regista nova página de ID

  3. Atualize os três valores seguintes para a sua nova aplicação e, em seguida, selecione Continuar:

    • Descrição: escreva um nome descritivo para a sua aplicação.

    • ID do Pacote: introduza um ID do Pacote do formulário com.organization_identifier<>.<>product_name conforme mencionado no Guia de Distribuição de Aplicações. Na captura de ecrã seguinte, o mobcat valor é utilizado como um identificador de organização e o valor PushDemo é utilizado como o nome do produto.

      Página ID da aplicação de registo do Portal de Aprovisionamento do iOS

    • Notificações Push: verifique a opção Notificações Push na secção Capacidades .

      Formulário para registar um novo ID da Aplicação

      Esta ação gera o ID da Aplicação e pede que confirme as informações. Selecione Continuar e, em seguida, selecione Registar para confirmar o novo ID da Aplicação.

      Confirmar novo ID da Aplicação

      Depois de selecionar Registar, verá o novo ID da Aplicação como um item de linha na página Certificados, Identificadores & Perfis .

  4. Na página Certificados, Identificadores & Perfis , em Identificadores, localize o item de linha ID da Aplicação que criou. Em seguida, selecione a linha para apresentar o ecrã Editar a Configuração do ID da Aplicação .

Criar um certificado para Hubs de Notificação

É necessário um certificado para permitir que o notification hub funcione com o Apple Push Notification Services (APNS) e pode ser fornecido de uma de duas formas:

  1. Criar um certificado push p12 que pode ser carregado diretamente para o Notification Hub (a abordagem original)

  2. Criar um certificado p8 que pode ser utilizado para autenticação baseada em tokens (a abordagem mais recente e recomendada)

A abordagem mais recente tem vários benefícios, conforme documentado na autenticação baseada em tokens (HTTP/2) para APNS. São necessários menos passos, mas também são mandatados para cenários específicos. No entanto, foram fornecidos passos para ambas as abordagens, uma vez que ambas irão funcionar para efeitos deste tutorial.

OPÇÃO 1: Criar um certificado push p12 que pode ser carregado diretamente para o Notification Hub
  1. No seu Mac, execute a ferramenta Acesso keychain. Pode ser aberto a partir da pasta Utilitários ou da pasta Outro no Launchpad.

  2. Selecione Acesso a Porta-chaves, expanda Assistente de Certificados e, em seguida, selecione Pedir um Certificado a partir de uma Autoridade de Certificação.

    Utilizar o Acesso keychain para pedir um novo certificado

    Nota

    Por predefinição, o Keychain Access seleciona o primeiro item na lista. Isto pode ser um problema se estiver na categoria Certificados e a Apple Worldwide Developer Relations Certification Authority não for o primeiro item na lista. Certifique-se de que tem um item não chave ou que a chave da Autoridade de Certificação de Relações com Programadores Mundiais da Apple está selecionada, antes de gerar o CSR (Pedido de Assinatura de Certificado).

  3. Selecione o Seu Endereço Email de Utilizador, introduza o seu valor de Nome Comum, certifique-se de que especifica Guardado no disco e, em seguida, selecione Continuar. Deixe a AC Email Endereço em branco, uma vez que não é necessário.

    Informações de certificado esperadas

  4. Introduza um nome para o ficheiro de Pedido de Assinatura de Certificado (CSR) em Guardar Como, selecione a localização em Onde e, em seguida, selecione Guardar.

    Escolher um nome de ficheiro para o certificado

    Esta ação guarda o ficheiro CSR na localização selecionada. A localização predefinida é Ambiente de Trabalho. Lembre-se da localização escolhida para o ficheiro.

  5. Novamente na página Certificados, Identificadores & Perfis no Portal de Aprovisionamento do iOS, desloque-se para baixo até à opção Notificações Push selecionada e, em seguida, selecione Configurar para criar o certificado.

    Página Editar ID da Aplicação

  6. É apresentada a janela Certificados TLS/SSL do serviço Apple Push Notification . Selecione o botão Criar Certificado na secção Certificado TLS/SSL de Desenvolvimento .

    Botão Criar certificado para o ID da Aplicação

    É apresentado o ecrã Criar um novo Certificado .

    Nota

    Este tutorial utiliza um certificado de desenvolvimento. O mesmo processo é utilizado ao registar um certificado de produção. Certifique-se apenas de que utiliza o mesmo tipo de certificado ao enviar notificações.

  7. Selecione Escolher Ficheiro, navegue para a localização onde guardou o ficheiro CSR e, em seguida, faça duplo clique no nome do certificado para carregá-lo. Em seguida, selecione Continuar.

  8. Depois de o portal criar o certificado, selecione o botão Transferir . Guarde o certificado e lembre-se da localização onde está guardado.

    Página de transferência do certificado gerado

    O certificado é transferido e guardado no seu computador na pasta Transferências .

    Localizar o ficheiro de certificado na pasta Transferências

    Nota

    Por predefinição, o certificado de desenvolvimento transferido tem o nome aps_development.cer.

  9. Faça duplo clique no certificado push transferido aps_development.cer. Esta ação instala o novo certificado no Keychain, conforme mostrado na imagem seguinte:

    Lista de certificados de acesso a porta-chaves a mostrar o novo certificado

    Nota

    Embora o nome no certificado possa ser diferente, o nome terá o prefixo Apple Development iOS Push Services e terá o identificador de pacote adequado associado ao mesmo.

  10. Em Acesso a Porta-chaves, cliqueemControlo + no novo certificado push que criou na categoria Certificados. Selecione Exportar, atribua um nome ao ficheiro, selecione o formato p12 e, em seguida, selecione Guardar.

    Exportar certificado como formato p12

    Pode optar por proteger o certificado com uma palavra-passe, mas uma palavra-passe é opcional. Clique em OK se quiser ignorar a criação de palavras-passe. Anote o nome do ficheiro e a localização do certificado p12 exportado. São utilizados para ativar a autenticação com APNs.

    Nota

    O nome e a localização do ficheiro p12 podem ser diferentes dos apresentados neste tutorial.

OPÇÃO 2: Criar um certificado p8 que pode ser utilizado para autenticação baseada em tokens
  1. Anote os seguintes detalhes:

    • Prefixo do ID da Aplicação (ID da Equipa)
    • ID do Pacote
  2. Novamente em Certificados, Identificadores & Perfis, clique em Chaves.

    Nota

    Se já tiver uma chave configurada para APNS, pode reutilizar o certificado p8 que transferiu logo após a sua criação. Se for o caso, pode ignorar os passos 3 a 5.

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

  4. Forneça um valor de Nome de Chave adequado e, em seguida, verifique a opção Serviço Apple Push Notifications (APNS) e, em seguida, clique em Continuar, seguido de Registar no ecrã seguinte.

  5. Clique em Transferir e, em seguida, mova o ficheiro p8 (com o prefixo AuthKey_) para um diretório local seguro e, em seguida, clique em Concluído.

    Nota

    Certifique-se de que mantém o ficheiro p8 num local seguro (e guarde uma cópia de segurança). Depois de transferir a chave, não pode ser transferida novamente à medida que a cópia do servidor é removida.

  6. Em Chaves, clique na chave que criou (ou numa chave existente, se tiver optado por utilizá-la).

  7. Anote o valor do ID da Chave .

  8. Abra o certificado p8 numa aplicação adequada à 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-----

    Nota

    Este é o valor do token que será utilizado mais tarde para configurar o Notification Hub.

No final destes passos, deverá ter as seguintes informações para utilizar mais tarde em Configurar o hub de notificação com informações do APNS:

  • ID da Equipa (consulte o passo 1)
  • ID do Pacote (veja o passo 1)
  • ID da Chave (veja o passo 7)
  • Valor do token (valor da chave p8 obtido no passo 8)

Criar um perfil de aprovisionamento para a aplicação

  1. Regresse ao Portal de Aprovisionamento do iOS, selecione Certificados, Identificadores & Perfis, selecione Perfis no menu esquerdo e, em seguida, selecione + para criar um novo perfil. É apresentado o ecrã Registar um Novo Perfil de Aprovisionamento .

  2. Selecione Desenvolvimento de Aplicações iOS em Desenvolvimento como o tipo de perfil de aprovisionamento e, em seguida, selecione Continuar.

    Lista de perfis de aprovisionamento

  3. Em seguida, selecione o ID da aplicação que criou na lista pendente ID da Aplicação e selecione Continuar.

    Selecione o ID da Aplicação

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

    Nota

    Este certificado não é o certificado push que criou no passo anterior. Este é o seu certificado de desenvolvimento. Se não existir um, tem de criá-lo, uma vez que este é um pré-requisito para este tutorial. Os certificados de programador podem ser criados no Portal do Programador da Apple, através do Xcode ou do Visual Studio.

  5. Regresse à página Certificados, Identificadores & Perfis , selecione Perfis no menu esquerdo e, em seguida, selecione + para criar um novo perfil. É apresentado o ecrã Registar um Novo Perfil de Aprovisionamento .

  6. Na janela Selecionar certificados , selecione o certificado de desenvolvimento que criou. Em seguida, selecione Continuar.

  7. Em seguida, selecione os dispositivos a utilizar para testes e selecione Continuar.

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

    Escolher um nome de perfil de aprovisionamento

  9. Quando o novo perfil de aprovisionamento for criado, selecione Transferir. Lembre-se da localização onde está guardada.

  10. Navegue para a localização do perfil de aprovisionamento e, em seguida, faça duplo clique no mesmo para instalá-lo no seu computador de desenvolvimento.

Criar um Hub de Notificação

Nesta secção, vai criar um hub de notificação e configurar a autenticação com o APNS. Pode utilizar um certificado push p12 ou uma autenticação baseada em tokens. Se quiser utilizar um hub de notificação que já criou, pode avançar para o passo 5.

  1. Inicie sessão no Azure.

  2. Clique em Criar um recurso, procure e selecione Hub de Notificação e, em seguida, clique em Criar.

  3. Atualize os seguintes campos e, em seguida, clique em Criar:

    DETALHES BÁSICOS

    Subscrição: Escolha a Subscrição de destino na lista pendente
    Grupo de Recursos: Criar um novo Grupo de Recursos (ou escolher um existente)

    DETALHES DO ESPAÇO DE NOMES

    Espaço de Nomes do Hub de Notificação: Introduza um nome globalmente exclusivo para o espaço de nomes do Hub de Notificação

    Nota

    Certifique-se de que a opção Criar novo está selecionada para este campo.

    DETALHES DO HUB DE NOTIFICAÇÃO

    Hub de Notificação: Introduza um nome para o Notification Hub
    Localização: Escolha uma localização adequada na lista pendente
    Escalão de Preço: Manter a opção Gratuita predefinida

    Nota

    A menos que tenha atingido o número máximo de hubs no escalão gratuito.

  4. Assim que o Notification Hub tiver sido aprovisionado, navegue para esse recurso.

  5. Navegue para o novo Notification Hub.

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

  7. Anote os valores do Nome da Política juntamente com os valores de Cadeia de Ligação correspondentes.

Configurar o Hub de Notificação com informações do APNS

Em Serviços de Notificação, selecione Apple e, em seguida, siga os passos adequados com base na abordagem que escolheu anteriormente na secção Criar um Certificado para Hubs de Notificação .

Nota

Utilize a Produção para o Modo de Aplicação apenas se quiser enviar notificações push aos utilizadores que compraram a sua aplicação na loja.

OPÇÃO 1: Utilizar um certificado push .p12

  1. Selecione Certificado.

  2. Selecione o ícone de ficheiro.

  3. Selecione o ficheiro .p12 que exportou anteriormente e, em seguida, selecione Abrir.

  4. Se necessário, especifique a palavra-passe correta.

  5. Selecione Modo sandbox .

  6. Selecione Guardar.

OPÇÃO 2: Utilizar a autenticação baseada em tokens

  1. Selecione Token.

  2. Introduza os seguintes valores que adquiriu anteriormente:

    • ID da Chave
    • ID do Pacote
    • ID da Equipa
    • Token
  3. Selecione Sandbox.

  4. Selecione Guardar.

Configurar o hub de notificação com informações da FCM

  1. Selecione Google (GCM/FCM) na secção Definições no menu esquerdo.
  2. Introduza a chave de servidor que anotou na Consola do Google Firebase.
  3. Selecione Guardar na barra de ferramentas.

Criar uma aplicação de back-end da API Web ASP.NET Core

Nesta secção, vai criar o back-end da API Web ASP.NET Core para processar o registo do dispositivo e o envio de notificações para a aplicação móvel Xamarin.Forms.

Criar um projeto Web

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

  2. SelecioneAplicação>.NET Core>ASP.NET Core>API> Seguinte.

  3. Na caixa de diálogo Configurar a sua nova API Web ASP.NET Core, selecione Target Framework do .NET Core 3.1.

  4. Introduza PushDemoApi para o Nome do Projeto e, em seguida, selecione Criar.

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

    Nota

    A aplicação modelada está configurada para utilizar WeatherForecastController como launchUrl. Está definido em Propriedades>launchSettings.json.

    Se lhe for pedida uma mensagem de certificado de desenvolvimento inválido :

    1. Clique em Sim para concordar em executar a ferramenta "dotnet dev-certs https" para corrigir esta situação. A ferramenta "dotnet dev-certs https" pede-lhe que introduza uma palavra-passe para o certificado e a palavra-passe do keychain.

    2. Clique em Sim quando lhe for pedido para Instalar e confiar no novo certificado e, em seguida, introduza a palavra-passe do keychain.

  6. Expanda a pasta Controladores e, em seguida, elimine WeatherForecastController.cs.

  7. Elimine WeatherForecast.cs.

  8. Configure valores de configuração local com a ferramenta Gestor de Segredos. Desassociar os segredos da solução garante que não acabam no controlo de código fonte. Abra o Terminal e, em seguida, aceda ao diretório do ficheiro 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 dos marcadores de posição pelo seu próprio nome do hub de notificação e cadeia de ligação valores. Anotou-as na secção Criar um hub de notificação . Caso contrário, pode procurá-los no Azure.

    NotificationHub:Name:
    Veja Nome no resumo do Essentials na parte superior da Descrição Geral.

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

    Nota

    Para cenários de produção, pode ver opções como o Azure KeyVault para armazenar o cadeia de ligação em segurança. Para simplificar, os segredos serão adicionados às definições da aplicação Serviço de Aplicações do Azure.

Autenticar clientes com uma Chave de API (Opcional)

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

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

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

    Nota

    Deve substituir o valor do marcador de posição pelo seu e tomar nota do mesmo.

  2. Controlo + Clique no projeto PushDemoApi , selecione Nova Pasta no menu Adicionar e, em seguida, clique em Adicionar com Autenticação como Nome da Pasta.

  3. Controlo + Clique na pasta Autenticação e, em seguida, selecione Novo Ficheiro... no menu Adicionar .

  4. SelecioneClasse Geral> Vazia, introduza ApiKeyAuthOptions.cs para o Nome e, em seguida, clique em Novo, adicionando a seguinte implementação.

    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çãodenominada ApiKeyAuthHandler.cs e, em seguida, adicione a seguinte implementação.

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

    Nota

    Um Processador 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çãodenominada ApiKeyAuthenticationBuilderExtensions.cs e, em seguida, adicione a seguinte implementação.

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

    Nota

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

  7. Em Startup.cs, atualize o método ConfigureServices para configurar a autenticação da Chave de API por baixo da chamada aos serviços. Método 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 Configurar para chamar os métodos de extensão UseAuthentication e UseAuthorization no IApplicationBuilder da aplicação. Certifique-se de que esses métodos são chamados após o UseRouting e antes da aplicação. 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();
        });
    }
    

    Nota

    Chamar UseAuthentication regista o middleware que utiliza os esquemas de autenticação registados anteriormente (a partir de ConfigurarServiços). Isto tem de ser chamado antes de qualquer middleware que dependa da autenticação dos utilizadores.

Adicionar dependências e configurar serviços

ASP.NET Core suporta o padrão de design de software de injeção de dependência (DI), que é uma técnica para alcançar a Inversão de Controlo (IoC) entre as classes e as respetivas dependências.

A utilização do hub de notificação e do SDK dos Hubs de Notificação para operações de back-end é encapsulada num serviço. O serviço é registado e disponibilizado através de uma abstração adequada.

  1. Controlo + Clique na pasta Dependências e, em seguida, selecione Gerir Pacotes NuGet....

  2. Procure Microsoft.Azure.NotificationHubs e certifique-se de que está selecionado.

  3. Clique em Adicionar Pacotes e, em seguida, clique em Aceitar quando lhe for pedido para aceitar os termos de licenciamento.

  4. Controlo + Clique no projeto PushDemoApi , selecione Nova Pasta no menu Adicionar e, em seguida, clique em Adicionar com Modelos como o Nome da Pasta.

  5. Controlo + Clique na pasta Modelos e, em seguida, selecione Novo Ficheiro... no menu Adicionar .

  6. SelecioneClasse VaziaGeral>, introduza PushTemplates.cs para o Nome e, em seguida, clique em Novo ao adicionar a seguinte implementação.

    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)\" }";
            }
        }
    }
    

    Nota

    Esta classe contém os payloads de notificação tokenizados para as notificações genéricas e silenciosas exigidas por este cenário. Os payloads são definidos fora da Instalação para permitir a experimentação sem ter de atualizar as instalações existentes através do serviço. O processamento de alterações às instalações desta forma está fora do âmbito deste tutorial. Para produção, considere modelos personalizados.

  7. Adicione outra Classe Vazia à pasta Modelos denominada DeviceInstallation.cs e, em seguida, adicione a seguinte implementação.

    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 denominada NotificationRequest.cs e, em seguida, adicione a seguinte implementação.

    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 denominada NotificationHubOptions.cs e, em seguida, adicione a seguinte implementação.

    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 denominado Serviços.

  11. Adicione uma Interface Vazia à pasta Serviçosdenominada INotificationService.cs e, em seguida, adicione a seguinte implementação.

    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 denominada NotificationHubsService.cs e, em seguida, 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);
            }
        }
    }
    

    Nota

    A expressão de etiqueta fornecida para SendTemplateNotificationAsync está limitada a 20 etiquetas. Está limitado a 6 para a maioria dos operadores, mas a expressão contém apenas ROs (||) neste caso. Se existirem mais de 20 etiquetas no pedido, têm de ser divididas em vários pedidos. Veja a documentação Expressões de Encaminhamento e Etiqueta para obter mais detalhes.

  13. No Startup.cs, atualize o método ConfigureServices para adicionar o NotificationHubsService como uma implementação 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. Controlo + Clique na pasta Controladores e, em seguida, selecione Novo Ficheiro... no menu Adicionar .

  2. Selecione ASP.NET Core>Web Classe do Controlador de API, introduza NotificationsController para o Nome e, em seguida, clique em Novo.

    Nota

    Se estiver a seguir com o Visual Studio 2019, selecione o modelo Controlador de API com ações de leitura/escrita .

  3. Adicione os seguintes espaços de nomes à parte superior do ficheiro.

    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 modelado para que seja derivado do ControllerBase e seja decorado com o atributo ApiController .

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

    Nota

    A classe base Controller fornece suporte para vistas, mas não é necessária neste caso e, por isso, o ControllerBase pode ser utilizado. Se estiver a seguir com o Visual Studio 2019, pode ignorar este passo.

  5. Se optar por concluir a secção Autenticar clientes com uma Chave de API , também deve decorar o NotificationsController com o atributo Autorizar .

    [Authorize]
    
  6. Atualize o construtor para aceitar a instância registada de INotificationService como um argumento e atribuí-la a um membro só de leitura.

    readonly INotificationService _notificationService;
    
    public NotificationsController(INotificationService notificationService)
    {
        _notificationService = notificationService;
    }
    
  7. No launchSettings.json (na pasta Propriedades ), altere o launchUrl de weatherforecast para api/notifications para corresponder ao URL especificado no atributo RegistrationsControllerRoute .

  8. Inicie a depuração (Command + Enter) para validar que a aplicação está a trabalhar com o novo NotificationsController e devolve um estado 401 Não Autorizado .

    Nota

    O Visual Studio pode não iniciar automaticamente a aplicação no browser. Irá utilizar o Postman para testar a API a partir deste ponto.

  9. Num novo separador Postman , defina o pedido como GET. Introduza o endereço abaixo ao substituir o marcador de posição <applicationUrl> pela aplicação httpsUrl encontrada em Propriedades>launchSettings.json.

    <applicationUrl>/api/notifications
    

    Nota

    O applicationUrl deve ser "https://localhost:5001" para o perfil predefinido. Se estiver a utilizar o IIS (predefinição no Visual Studio 2019 no Windows), deve utilizar o applicationUrl especificado no item iisSettings . Receberá uma resposta 404 se o endereço estiver incorreto.

  10. Se optar por concluir a secção Autenticar clientes com uma Chave de API , certifique-se de que configura os cabeçalhos do pedido para incluir o valor da chave de api .

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

    Nota

    Deverá receber um estado 200 OK com algum conteúdo JSON .

    Se receber um aviso de verificação de certificado SSL , pode desativar a definição do Postman de verificação de certificado SSL nas Definições.

  12. Substitua os métodos de classe com modelos no NotificationsController.cs pelo seguinte código.

    [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 a aplicação API

Agora, vai criar uma Aplicação de API no Serviço de Aplicações do Azure para alojar o serviço de back-end.

  1. Inicie sessão no portal do Azure.

  2. Clique em Criar um recurso, procure e selecione Aplicação API e, em seguida, clique em Criar.

  3. Atualize os seguintes campos e, em seguida, clique em Criar.

    Nome da aplicação:
    Introduza um nome globalmente exclusivo para a Aplicação API

    Subscrição:
    Escolha a mesma Subscrição de destino na qual criou o hub de notificação.

    Grupo de Recursos:
    Escolha o mesmo Grupo de Recursos no qual criou o hub de notificação.

    Serviço de Aplicações Plano/Localização:
    Criar um novo Plano de Serviço de Aplicações

    Nota

    Mude da opção predefinida para um plano que inclua suporte SSL . Caso contrário, terá de seguir os passos adequados ao trabalhar com a aplicação móvel para impedir que os pedidos http sejam bloqueados.

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

  4. Assim que a Aplicação API tiver sido aprovisionada, navegue para esse recurso.

  5. Tome nota da propriedade URL no resumo Essentials na parte superior da Descrição Geral. Este URL é o ponto final de back-end que será utilizado mais adiante neste tutorial.

    Nota

    O URL utiliza o nome da aplicação API que especificou anteriormente, com o formato https://<app_name>.azurewebsites.net.

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

  7. Para cada uma das definições abaixo, clique em Nova definição da aplicação para introduzir o Nome e um Valor e, em seguida, clique em OK.

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

    Nota

    Estas são as mesmas definições que definiu anteriormente nas definições de utilizador. Deverá conseguir copiá-los. A definição Authentication:ApiKey só é necessária se optar por concluir a secção Autenticar clientes com uma Chave de API . Para cenários de produção, pode ver opções como o Azure KeyVault. Estas foram adicionadas como definições de aplicação para simplificar neste caso.

  8. Assim que todas as definições da aplicação tiverem sido adicionadas, clique em Guardar e, em seguida, em Continuar.

Publicar o serviço de back-end

Em seguida, implemente a aplicação na Aplicação API para torná-la acessível a partir de todos os dispositivos.

Nota

Os passos seguintes são específicos para Visual Studio para Mac. Se estiver a seguir o Visual Studio 2019 no Windows, o fluxo de publicação será diferente. Consulte Publicar no Serviço de Aplicações do Azure no Windows.

  1. Altere a configuração de Depurar para Versão se ainda não o tiver feito.

  2. Controlo + Clique no projeto PushDemoApi e, em seguida, selecione Publicar no Azure... no menu Publicar .

  3. Se lhe for pedido, siga o fluxo de autenticação. Utilize a conta que utilizou na secção anterior para criar a Aplicação API .

  4. Selecione o Serviço de Aplicações do Azure Aplicação API que criou anteriormente a partir da lista como destino de publicação e, em seguida, clique em Publicar.

Depois de concluir o assistente, este publica a aplicação no Azure e, em seguida, abre a aplicação. Anote o URL se ainda não o tiver feito. Este URL é o ponto final de back-end que é utilizado mais à frente neste tutorial.

Validar a API publicada

  1. No Postman , abra um novo separador, defina o pedido como PUT e introduza o endereço abaixo. Substitua o marcador de posição pelo endereço base de que anotou na secção anterior publicar o serviço de back-end .

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

    Nota

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

  2. Se optar por concluir a secção Autenticar clientes com uma Chave de API , certifique-se de que configura os cabeçalhos do pedido para incluir o valor apikey .

    Chave Valor
    apikey <your_api_key>
  3. Escolha a opção não processada para o Corpo e, em seguida, selecione JSON na lista de opções de formato e, em seguida, inclua algum conteúdo JSON de marcador de posição:

    {}
    
  4. Clique em Enviar.

    Nota

    Deverá receber um estado 422 UnprocessableEntity do serviço.

  5. Execute novamente os passos 1 a 4, mas desta vez especifique o ponto final dos pedidos para validar que recebe uma resposta 400 Pedido Incorreto .

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

Nota

Ainda não é possível testar a API com dados de pedido válidos, uma vez que isto requer informações específicas da plataforma da aplicação móvel cliente.

Criar uma aplicação Xamarin.Forms para várias plataformas

Nesta secção, vai criar uma aplicação móvel Xamarin.Forms que implementa notificações push de forma multiplataformas.

Permite-lhe registar e anular o registo a partir de um hub de notificação através do serviço de back-end que criou.

É apresentado um alerta quando uma ação é especificada e a aplicação está em primeiro plano. Caso contrário, as notificações são apresentadas no centro de notificações.

Nota

Normalmente, efetuaria as ações de registo (e desregistração) durante o ponto adequado no ciclo de vida da aplicação (ou como parte da sua experiência de primeira execução, talvez) sem entradas explícitas de registo/registo de registo do utilizador. No entanto, este exemplo requer entradas explícitas do utilizador para permitir que esta funcionalidade seja explorada e testada mais facilmente.

Criar a solução Xamarin.Forms

  1. No Visual Studio, crie uma nova solução Xamarin.Forms com a Aplicação Formulários em Branco como modelo e introduza PushDemo para o Nome do Projeto.

    Nota

    Na caixa de diálogo Configurar a aplicação Formulários em Branco , certifique-se de que o Identificador da Organização corresponde ao valor que utilizou anteriormente e de que os destinos Android e iOS estão verificados.

  2. Controlo + Clique na solução PushDemo e, em seguida, selecione Atualizar Pacotes NuGet.

  3. Controlo + Clique na solução PushDemo e, em seguida, selecione Gerir Pacotes NuGet...

  4. Procure Newtonsoft.Json e certifique-se de que está selecionado.

  5. Clique em Adicionar Pacotes e, em seguida, clique em Aceitar quando lhe for pedido para aceitar os termos de licenciamento.

  6. Crie e execute a aplicação em cada plataforma de destino (Command + Enter) para testar as execuções de aplicações de modelo nos seus dispositivos.

Implementar os componentes entre plataformas

  1. Controlo + Clique no projeto PushDemo , selecione Nova Pasta no menu Adicionar e, em seguida, clique em Adicionar utilizando Modelos como o Nome da Pasta.

  2. Controlo + Clique na pasta Modelos e, em seguida, selecione Novo Ficheiro... no menu Adicionar .

  3. Selecione Classe Geral>Vazia, introduza DeviceInstallation.cs e, em seguida, adicione a seguinte implementação.

    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 Modelosdenominada PushDemoAction.cs com a seguinte implementação.

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

    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}'");
               }
           }
       }
    }
    

    Nota

    Esta é uma versão reduzida da classe ServiceContainer do repositório XamCAT . Será utilizado como um contentor de IoC leve (Inversão de Controlo).

  6. Adicione uma Interface Vazia à pasta Serviçosdenominada IDeviceInstallationService.cs e, em seguida, adicione o seguinte código.

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

    Nota

    Esta interface será implementada e iniciada por cada destino mais tarde para fornecer as funcionalidades específicas da plataforma e as informações de DeviceInstallation necessárias para o serviço de back-end.

  7. Adicione outra Interface Vazia à pasta Serviçosdenominada INotificationRegistrationService.cs e, em seguida, adicione o seguinte código.

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

    Nota

    Isto irá processar a interação entre o cliente e o serviço de back-end.

  8. Adicione outra Interface Vazia à pasta Serviçosdenominada INotificationActionService.cs e, em seguida, adicione o seguinte código.

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

    Nota

    Isto é utilizado como um mecanismo simples para centralizar o processamento de ações de notificação.

  9. Adicione uma Interface Vazia à pasta Serviçosdenominada IPushDemoNotificationActionService.cs que deriva do INotificationActionService, com a seguinte implementação.

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

    Nota

    Este tipo é específico da aplicação PushDemo e utiliza a enumeração PushDemoAction para identificar a ação que está a ser acionada de forma fortemente escrita.

  10. Adicione uma Classe Vazia à pasta Serviçosdenominada NotificationRegistrationService.cs implementar o INotificationRegistrationService com o seguinte código.

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

    Nota

    O argumento apiKey só é necessário se optar por concluir a secção Autenticar clientes com uma Chave de API .

  11. Adicione uma Classe Vazia à pasta Serviçosdenominada PushDemoNotificationActionService.cs implementar o IPushDemoNotificationActionService com o seguinte código.

    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 denominado Config.cs com a seguinte implementação.

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

    Nota

    Isto é utilizado como uma forma simples de manter os segredos fora do controlo de código fonte. Pode substituir estes valores como parte de uma compilação automatizada ou substituí-los através de uma classe parcial local. Irá fazê-lo no próximo passo.

    O campo ApiKey só é necessário se optar por concluir a secção Autenticar clientes com uma Chave de API .

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

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

    Nota

    Substitua os valores do marcador de posição pelos seus próprios valores. Deve ter anotar estes dados quando criou o serviço de back-end. O URL da Aplicação API deve ser https://<api_app_name>.azurewebsites.net/. Lembre-se de adicionar *.local_secrets.* ao seu ficheiro gitignore para evitar consolidar este ficheiro.

    O campo ApiKey só é necessário se optar por concluir a secção Autenticar clientes com uma Chave de API .

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

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

    Nota

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

    O argumento construtor NotificationRegistrationServiceapiKey só é necessário se optar por concluir a secção Autenticar clientes com uma Chave de API .

Implementar a IU para várias plataformas

  1. No projeto PushDemo , abra MainPage.xaml e substitua o controlo 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, no MainPage.xaml.cs, adicione um campo de apoio só de leitura para armazenar uma referência à implementação INotificationRegistrationService .

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

    public MainPage()
    {
        InitializeComponent();
    
        _notificationRegistrationService =
            ServiceContainer.Resolve<INotificationRegistrationService>();
    }
    
  4. Implemente os processadores de eventos para os botões RegisterButton e DeregisterButtonEventos clicados ao chamar os métodos de Registo/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 , no App.xaml.cs, certifique-se de que são referenciados os seguintes espaços de nomes.

    using PushDemo.Models;
    using PushDemo.Services;
    using Xamarin.Essentials;
    using Xamarin.Forms;
    
  6. Implemente o processador 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 Aplicações , resolva a implementação IPushNotificationActionService com o ServiceContainer e subscreva o evento IPushDemoNotificationActionServiceActionTriggered .

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

    Nota

    Isto é simplesmente para demonstrar o recibo e a propagação das ações de notificação push. Normalmente, estas seriam processadas silenciosamente, por exemplo, ao navegar para uma vista específica ou atualizar alguns dados em vez de apresentar um alerta através da Página raiz, Página Principal neste caso.

Configurar o projeto Android nativo para notificações push

Validar o nome e as permissões do pacote

  1. Em PushDemo.Android, abra as Opções do Projeto e, em seguida , a Aplicação Android a partir da secção Compilação .

  2. Verifique se o Nome do pacote corresponde ao valor que utilizou no projeto PushDemo da Consola do Firebase. O Nome do pacote estava no formato com.<organization>.pushdemo.

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

    Nota

    Apenas os dispositivos que executam a API de nível 26 e superior são suportados para efeitos deste tutorial. No entanto, pode expandi-lo para suportar dispositivos com versões mais antigas.

  4. Certifique-se de que as permissões internet e READ_PHONE_STATE estão ativadas em Permissões necessárias.

  5. Clique em OK

Adicione os pacotes Xamarin Google Play Services e Xamarin.Firebase.Messaging

  1. Em PushDemo.Android, Clique em Controlo + na pasta Pacotes e, em seguida, selecione Gerir Pacotes NuGet....

  2. Procure Xamarin.GooglePlayServices.Base (não Cave) e certifique-se de que está selecionado.

  3. Procure Xamarin.Firebase.Messaging e certifique-se de que está selecionado.

  4. Clique em Adicionar Pacotes e, em seguida, clique em Aceitar quando lhe for pedido para aceitar os termos de licenciamento.

Adicionar o ficheiro JSON do Google Services

  1. Controlo + Clique no PushDemo.Android projeto e, em seguida, selecione Ficheiro Existente... no menu Adicionar .

  2. Escolha o ficheiro google-services.json que transferiu anteriormente quando configurou o projeto PushDemo na Consola do Firebase e, em seguida, clique em Abrir.

  3. Quando lhe for pedido, opte por Copiar o ficheiro para o diretório.

  4. Controlo + Clique no ficheiro google-services.json a PushDemo.Android partir do projeto e, em seguida, certifique-se de que GoogleServicesJson está definido como Ação de Compilação.

Processar notificações push para Android

  1. Controlo + Clique no PushDemo.Android projeto, selecione Nova Pasta no menu Adicionar e, em seguida, clique em Adicionar utilizando Serviços como o Nome da Pasta.

  2. Controlo + Clique na pasta Serviços e, em seguida, selecione Novo Ficheiro... no menu Adicionar .

  3. SelecioneClasse Geral> Vazia, introduza DeviceInstallationService.cs para o Nome e, em seguida, clique em Novo ao adicionar a seguinte implementação.

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

    Nota

    Esta classe fornece um ID exclusivo (com Secure.AndroidId) como parte do payload de registo do hub de notificação.

  4. Adicione outra Classe Vazia à pasta Serviçosdenominada PushNotificationFirebaseMessagingService.cs e, em seguida, adicione a seguinte implementação.

    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. No MainActivity.cs, certifique-se de que os seguintes espaços de nomes foram adicionados à parte superior do ficheiro.

    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. No MainActivity.cs, defina o LaunchMode como SingleTop para que MainActivity não seja criado novamente quando for aberto.

    [Activity(
        Label = "PushDemo",
        LaunchMode = LaunchMode.SingleTop,
        Icon = "@mipmap/icon",
        Theme = "@style/MainTheme",
        MainLauncher = true,
        ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
    
  7. Adicione propriedades privadas e campos de cópia de segurança correspondentes para armazenar uma referência às 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 obter e armazenar o token 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 denominado ProcessNotificationActions que verificará se uma determinada Intenção tem um valor extra com o nome action. Acione condicionalmente essa ação com 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 ProcessNotificationActions .

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

    Nota

    Uma vez que o LaunchMode para a Atividade está definido como SingleTop, uma Intenção será enviada para a instância de Atividade existente através do método OnNewIntent em vez do método OnCreate , pelo que tem de processar 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 transmitir a implementação específica da plataforma de IDeviceInstallationService.

    Bootstrap.Begin(() => new DeviceInstallationService());
    
  12. No mesmo método, chame condicionalmente GetInstanceId na instância 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 transmitir a Intenção atual.

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

Nota

Tem de voltar a registar a aplicação sempre que a executar e impedi-la de uma sessão de depuração para continuar a receber notificações push.

Configurar o projeto iOS nativo para notificações push

Configurar Info.plist e Entitlements.plist

  1. Certifique-se de que iniciou sessão na sua Conta de Programador da Apple nasPreferências do Visual Studio>...>Publicação>As Contas de Programador da Apple e o Perfil de Certificado e Aprovisionamento adequado foram transferidos. Deverá ter criado estes recursos como parte dos passos anteriores.

  2. Em PushDemo.iOS, abra Info.plist e certifique-se de que bundleIdentifier corresponde ao valor utilizado para o respetivo perfil de aprovisionamento no Portal do Programador da Apple. O BundleIdentifier estava no formato com.<organization>.PushDemo.

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

    Nota

    Apenas os dispositivos com o iOS 13.0 e posterior são suportados para efeitos deste tutorial. No entanto, pode expandi-lo para suportar dispositivos com versões mais antigas.

  4. Abra as Opções do Projeto para PushDemo.iOS (faça duplo clique no projeto).

  5. Em Opções do Projeto, em Criar > Assinatura de Pacote iOS, certifique-se de que a sua conta de Programador está selecionada em Equipa. Em seguida, certifique-se de que a opção "Gerir automaticamente a assinatura" está selecionada e que o Certificado de Assinatura e o Perfil de Aprovisionamento são selecionados automaticamente.

    Nota

    Se o Certificado de Assinatura e o Perfil de Aprovisionamento não tiverem sido selecionados automaticamente, selecione Aprovisionamento Manual e, em seguida, clique em Opções de Assinatura do Pacote. Certifique-se de que a sua Equipa está selecionada para Identidade de Assinatura e que o perfil de aprovisionamento específico pushDemo está selecionado para o Perfil de Aprovisionamento para configurações de Depuração e Versão , garantindo que o iPhone está selecionado para a Plataforma em ambos os casos.

  6. Em PushDemo.iOS, abra Entitlements.plist e certifique-se de que Ativar Notificações Push está selecionado quando visualizado no separador Elegibilidades . Em seguida, certifique-se de que a definição Ambiente do APS está definida para desenvolvimento quando visualizada no separador Origem .

Processar notificações push para iOS

  1. Controlo + Clique no projeto PushDemo.iOS , selecione Nova Pasta no menu Adicionar e, em seguida, clique em Adicionar utilizando Serviços como o Nome da Pasta.

  2. Controlo + Clique na pasta Serviços e, em seguida, selecione Novo Ficheiro... no menu Adicionar .

  3. SelecioneClasse Geral> Vazia, introduza DeviceInstallationService.cs para o Nome e, em seguida, clique em Novo ao adicionar a seguinte implementação.

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

    Nota

    Esta classe fornece um ID exclusivo (com o valor UIDevice.IdentifierForVendor ) e o payload de registo do hub de notificação.

  4. Adicione uma nova pasta ao projeto PushDemo.iOS denominado Extensões e, em seguida, adicione uma Classe Vazia a essa pasta denominada NSDataExtensions.cs com a seguinte implementação.

    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. No AppDelegate.cs, certifique-se de que os seguintes espaços de nomes foram adicionados à parte superior do ficheiro.

    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 os respetivos campos de apoio para armazenar uma referência às 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 registar as definições de notificação do utilizador e, em seguida, para notificações remotas com 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 IDeviceInstallationService.Token propriedade. Atualize o registo e coloque o token do dispositivo em cache se tiver sido atualizado desde a última vez que foi armazenado.

    Task CompleteRegistrationAsync(NSData deviceToken)
    {
        DeviceInstallationService.Token = deviceToken.ToHexString();
        return NotificationRegistrationService.RefreshRegistrationAsync();
    }
    
  9. Adicione o método ProcessNotificationActions para processar os dados de notificação do NSDictionary e chamar 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 ao transmitir 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 ao transmitir 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 registar o erro.

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

    Nota

    Este é um marcador de posição. Vai querer implementar o registo e o processamento de erros adequados para cenários de produção.

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

    Bootstrap.Begin(() => new DeviceInstallationService());
    
  14. No mesmo método, peça condicionalmente autorização e registe-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 de opções contiver a UIApplication.LaunchOptionsRemoteNotificationKey ao transmitir o objeto userInfo resultante.

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

Testar a solução

Agora, pode testar o envio de notificações através do serviço de back-end.

Enviar uma notificação de teste

  1. Abra um novo separador no Postman.

  2. Defina o pedido como POST e introduza o seguinte endereço:

    https://<app_name>.azurewebsites.net/api/notifications/requests
    
  3. Se optar por concluir a secção Autenticar clientes com uma Chave de API , certifique-se de que configura os cabeçalhos do pedido para incluir o valor apikey .

    Chave Valor
    apikey <your_api_key>
  4. Escolha a opção não processada para o Corpo e, em seguida, selecione JSON na lista de opções de formato e, em seguida, inclua algum conteúdo JSON de marcador de posição:

    {
        "text": "Message from Postman!",
        "action": "action_a"
    }
    
  5. Selecione o botão Código , que se encontra sob o botão Guardar no canto superior direito da janela. O pedido deve ter um aspeto semelhante ao seguinte exemplo quando apresentado para HTML (consoante tenha incluído 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 a aplicação PushDemo numa ou em ambas as plataformas de destino (Android e iOS).

    Nota

    Se estiver a testar no Android , certifique-se de que não está a executar a depuração ou se a aplicação foi implementada ao executar a aplicação, force a fechar a aplicação e inicie-a novamente a partir do iniciador.

  7. Na aplicação PushDemo , toque no botão Registar .

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

  9. Confirme que obtém uma resposta 200 OK no Postman e o alerta é apresentado na aplicação que mostra a ação ActionA recebida.

  10. Feche a aplicação PushDemo e, em seguida, clique novamente no botão Enviar no Postman.

  11. Confirme que recebe novamente uma resposta 200 OK no Postman . Confirme que é apresentada uma notificação na área de notificação da aplicação PushDemo com a mensagem correta.

  12. Toque na notificação para confirmar que abre a aplicação e que a ação ActionA recebeu o alerta.

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

    {
        "action": "action_b",
        "silent": true
    }
    
  14. Com a aplicação ainda aberta, clique no botão Enviar no Postman.

  15. Confirme que obtém uma resposta 200 OK no Postman e que o alerta é apresentado na aplicação que mostra a ação ActionB recebida em vez da ação ActionA recebida.

  16. Feche a aplicação PushDemo e, em seguida, clique novamente no botão Enviar no Postman.

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

Resolução de problemas

Nenhuma resposta do serviço de back-end

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

Se testar na Aplicação API do Azure, verifique se o serviço está em execução e se foi implementado e começou sem erros.

Certifique-se de que especificou corretamente o endereço base no Postman ou na configuração da aplicação móvel ao testar através do cliente. O endereço base deve ser https://<api_name>.azurewebsites.net/ indicativamente ou https://localhost:5001/ ao testar localmente.

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

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

Receber um código de estado 401 do serviço de back-end

Confirme que está a definir o cabeçalho do pedido apikey e este valor corresponde ao que configurou para o serviço de back-end.

Se receber este erro ao testar localmente, certifique-se de que o valor de chave que definiu na configuração do cliente corresponde ao valor de definição de utilizador Authentication:ApiKey utilizado pela API.

Se estiver a testar com uma Aplicação API, certifique-se de que o valor da chave no ficheiro de configuração do cliente corresponde à definição da aplicação Authentication:ApiKey que está a utilizar na Aplicação API.

Nota

Se tiver criado ou alterado esta definição depois de ter implementado o serviço de back-end, tem de reiniciar o serviço para que entre em vigor.

Se optou por não concluir a secção Autenticar clientes com uma Chave de API , certifique-se de que não aplicou o atributo Authorize à classe NotificationsController .

Receber um código de estado 404 do serviço de back-end

Confirme que o ponto final e o método de pedido HTTP estão corretos. Por exemplo, os pontos finais devem ser indicativamente:

  • [PUT]https://<api_name>.azurewebsites.net/api/notifications/installations
  • [ELIMINAR]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
  • [ELIMINAR]https://localhost:5001/api/notifications/installations/<installation_id>
  • [POST]https://localhost:5001/api/notifications/requests

Ao especificar o endereço base na aplicação cliente, certifique-se de que termina com um /. O endereço base deve ser https://<api_name>.azurewebsites.net/ indicativamente ou https://localhost:5001/ ao testar localmente.

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

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

Reveja as sugestões de resolução de problemas anteriores, quando aplicável com base no código de estado.

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

Valide se o serviço de back-end está a funcionar conforme esperado através do Postman com o payload adequado. Utilize o payload real criado pelo código de cliente para a plataforma em questão.

Reveja as secções de configuração específicas da plataforma para garantir que não foram ignorados passos. Verifique se os valores adequados estão a ser resolvidos para installation id as variáveis e token para a plataforma adequada.

Não é possível resolver um ID da mensagem de erro do dispositivo

Reveja as secções de configuração específicas da plataforma para garantir que não foram ignorados passos.

Passos seguintes

Agora, deverá ter uma aplicação Xamarin.Forms básica ligada a um hub de notificação através de um serviço de back-end e pode enviar e receber notificações.

Provavelmente, terá de adaptar o exemplo utilizado neste tutorial para se ajustar ao seu próprio cenário. Também é recomendado implementar o processamento de erros, a lógica de repetição e o registo mais robustos.

O Visual Studio App Center pode ser rapidamente incorporado em aplicações móveis que fornecem análises e diagnósticos para ajudar na resolução de problemas.