Partilhar via


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

Procurar amostra. Procurar no exemplo

As notificações por push fornecem informações de um sistema de back-end para um aplicativo cliente. Apple, Google e outras plataformas têm seu próprio PNS (Serviço de Notificação por Push). Os Hubs de Notificações do Microsoft Azure permitem centralizar notificações entre plataformas para que seu aplicativo de back-end possa se comunicar com um único hub, que cuida da distribuição de notificações para cada PNS.

Os Hubs de Notificações do Microsoft Azure exigem que os aplicativos se registrem no hub e, opcionalmente, definam modelos e/ou assinem marcas:

  • A execução de uma instalação de dispositivo vincula um identificador PNS a um identificador no Hub de Notificações do Microsoft Azure. Para obter mais informações sobre registros, consulte Gerenciamento de registro.
  • Os modelos permitem que os dispositivos especifiquem modelos de mensagem parametrizados. As mensagens de entrada podem ser personalizadas por dispositivo. Para obter mais informações, consulte Modelos de hubs de notificação.
  • As marcas podem ser usadas para assinar categorias de mensagens, como notícias, esportes e clima. Para saber mais, veja Expressões de marca e de roteamento.

Neste tutorial, você usará Hubs de Notificações do Microsoft Azure para enviar notificações por push para um aplicativo do .NET MAUI (.NET Multi-Platform App UI) direcionado ao Android e ao iOS. Um back-end da API Web do ASP.NET Core é usado para lidar com o registro do dispositivo para o cliente e para iniciar uma notificação por push. Essas operações são tratadas usando o pacote NuGet Microsoft.Azure.NotificationHubs. Para obter mais informações sobre a abordagem geral, consulte Gerenciamento de registro a partir de um back-end.

Neste tutorial, você:

  • Configurar serviços de notificação por push e o Hub de Notificações do Microsoft Azure.
  • Criar um aplicativo de back-end de API Web do ASP.NET Core.
  • Criar um aplicativo .NET.
  • Configurar o aplicativo Android para notificações por push.
  • Configurar o aplicativo iOS para notificações por push.
  • Testar o aplicativo.
  • Solucionar problemas de instalação e configuração de domínio.

Pré-requisitos

Neste tutorial, você precisará de:

  • Uma conta do Azure com uma assinatura ativa.
  • Um PC ou Mac com a versão mais recente do Visual Studio/Visual Studio Code com a carga de trabalho de desenvolvimento da .NET Multi-Platform App UI e as cargas de trabalho de desenvolvimento Web e ASP.NET instaladas.

Para o Android, é preciso ter:

  • Um dispositivo físico desbloqueado pelo desenvolvedor ou um emulador executando a API 26+ com o Google Play Services instalado.

Para o iOS, é preciso ter:

  • Uma conta de desenvolvedor ativa da Apple.
  • Um Mac executando Xcode, juntamente com um certificado de desenvolvedor válido instalado em seu conjunto de chaves.

Em seguida, no iOS, você deverá ter:

  • Um simulador do iOS 16+ que é executado no macOS 13+ em computadores Mac com processadores Apple Silicon ou T2.

    OR

  • Um dispositivo iOS físico registrado em sua conta de desenvolvedor (executando o iOS 13.0+).

  • Seu dispositivo físico registrado em sua conta de desenvolvedor da Apple e associado ao seu certificado.

Importante

O simulador iOS suporta notificações remotas no iOS 16+ quando executado no macOS 13+ em computadores Mac com processadores Apple Silicon ou T2. Se você não atender a esses requisitos de hardware, precisará de uma conta de desenvolvedor ativa da Apple e um dispositivo físico.

Para seguir este tutorial, você deve ter familiaridade com:

Embora este tutorial tenha como destino o Visual Studio, é possível segui-lo usando o Visual Studio Code em um PC ou Mac. No entanto, haverá algumas diferenças que precisam ser reconciliadas. Por exemplo, descrições de interface do usuário e fluxos de trabalho, nomes de modelo e configuração de ambiente.

Configurar serviços de notificação por push e o Hub de Notificações do Microsoft Azure

Nesta seção, você configurará o Firebase Cloud Messaging e o Apple Push Notification Service (APNS). Em seguida, você criará e configurará um Hub de Notificações do Microsoft Azure para trabalhar com esses serviços.

Criar um projeto do Firebase

Para criar um projeto do Firebase:

  1. Em um navegador da Web, entre no console do Firebase.

  2. No console do Firebase, selecione o botão Adicionar projeto e crie um novo projeto do Firebase, inserindo PushDemo como o Nome do projeto.

    Observação

    Um nome exclusivo será gerado para você. Por padrão, isso inclui uma variante minúscula do nome fornecido mais um número gerado separado por um traço. Você pode alterar isso se desejar, desde que suas edições ainda sejam globalmente exclusivas.

  3. Depois que o projeto for criado, selecione o logotipo do Android para adicionar o Firebase a um aplicativo Android:

    Captura de tela da adição do Firebase a um aplicativo Android no console do Firebase Cloud Messaging.

  4. Na página Adicionar o Firebase ao aplicativo Android, insira um nome para o pacote, opcionalmente um apelido de aplicativo e selecione o botão Registrar aplicativo:

    Captura de tela do registro do aplicativo Android no Firebase.

  5. Na página Adicionar o Firebase ao aplicativo Android, selecione o botão Baixar google-services.json e salve o arquivo em uma pasta local antes de selecionar o botão Avançar:

    Captura de tela do download do arquivo JSON do Google Services.

  6. Na página Adicionar o Firebase ao aplicativo Android, selecione o botão Avançar.

  7. Na página Adicionar o Firebase ao aplicativo Android, selecione o botão Continuar para o console.

  8. No console do Firebase, selecione o ícone Visão geral do projeto e selecione Configurações do projeto:

    Captura de tela da seleção das configurações do projeto no console do Firebase Cloud Messaging.

  9. Nas Configurações do projeto, selecione a guia Cloud Messaging. Você verá que a API do Firebase Cloud Messaging (V1) está habilitada:

    Captura de tela confirmando que o Firebase Cloud Messaging V1 está habilitado.

  10. Nas Configurações do projeto, selecione a guia Contas de Serviço e, em seguida, selecione o botão Gerar nova chave privada.

  11. Na caixa de diálogo Gerar nova chave privada, selecione o botão Gerar chave:

    Captura de tela da geração de uma nova chave privada no console do Firebase Cloud Messaging.

    Um arquivo JSON será baixado, que conterá valores que você inserirá no Hub de Notificações do Microsoft Azure.

Registrar seu aplicativo iOS para notificações por push

Para enviar notificações por push para um aplicativo iOS, você precisará registrar seu aplicativo na Apple e registrar-se para notificações por push. Isso pode ser feito executando as etapas na seguinte documentação do Hub de Notificações do Microsoft Azure:

Se você quiser receber notificações por push em um dispositivo físico, também precisará criar um perfil de provisionamento.

Importante

Para receber notificações em segundo plano no iOS, você deve adicionar o modo em segundo plano de notificações remotas ao seu aplicativo. Para obter mais informações, consulte Habilitar o recurso de notificações remotas em developer.apple.com.

Criar um Hub de Notificações do Microsoft Azure

Para criar um hub de notificações no portal do Azure:

  1. Em um navegador da Web, entre no portal do Azure.
  2. No portal do Azure, clique no botão Criar um recurso e, em seguida, pesquise e escolha Hub de Notificações antes de selecionar o botão Criar.
  3. Na página Hub de Notificações, execute as seguintes etapas:
    1. No campo Assinatura, selecione o nome da assinatura do Azure que você deseja usar e selecione um grupo de recursos existente ou crie um novo.

    2. No campo Detalhes do Namespace, insira um nome exclusivo para o novo namespace.

    3. No campo Detalhes do Hub de Notificações, digite um nome para o hub de notificações. Isso é necessário porque um namespace contém um ou mais hubs de notificação.

    4. Na lista suspensa Localização, selecione um valor que especifica o local no qual você deseja criar o hub de notificações.

    5. Revise a opção Zonas de Disponibilidade. Se você escolher uma região que tenha zonas de disponibilidade, a caixa de seleção será selecionada por padrão.

      Observação

      As zonas de disponibilidade são um recurso pago, portanto, uma taxa adicional é acrescentada à sua camada de serviço.

    6. Escolha uma opção de Recuperação de desastre: nenhuma, região de recuperação emparelhada ou região de recuperação flexível. Se você escolher Região de recuperação emparelhada, a região de failover será exibida. Se você selecionar Região de recuperação flexível, use a lista suspensa para escolher entre uma lista de regiões de recuperação.

    7. Selecione o botão Criar. O hub de notificações será criado.

  4. No portal do Azure, navegue até o hub de notificações recém-criado e, em seguida, até a folha Gerenciar > Políticas de Acesso.
  5. Na folha Políticas de Acesso, anote a cadeia de conexão da política DefaultFullSharedAccessSignature. Você precisará disso mais tarde ao criar um serviço de back-end que se comunique com o hub de notificações.

Para obter mais informações sobre como criar um hub de notificações, consulte Criar um hub de notificações do Azure no portal do Azure.

Configurar o Firebase Cloud Messaging no hub de notificações

Para configurar o hub de notificações para se comunicar com o Firebase Cloud Messaging:

  1. No portal do Azure, navegue até o hub de notificações e selecione a folha Configurações> Google (FCM v1).

  2. Na folha Google (FCM v1), insira valores para os campos Chave Privada, Email do Cliente e ID do Projeto. Esses valores podem ser encontrados no arquivo JSON de chave privada baixado do Firebase Cloud Messaging:

    Campo do Azure Chave JSON Exemplo de valor JSON
    Chave privada private_key Esse valor deve começar com -----BEGIN PRIVATE KEY-----\n e terminar com -----END PRIVATE KEY-----\n.
    Email do Cliente client_email firebase-adminsdk-55sfg@pushdemo-d6ab2.iam.gserviceaccount.com
    ID do projeto project_id pushdemo-d6ab2
  3. Na folha Google (FCM v1), selecione o botão Salvar.

Configurar o Apple Push Notification Service no hub de notificações

No portal do Azure, navegue até o hub de notificações e selecione a folha Configurações> Apple (APNS). Em seguida, siga as etapas apropriadas com base na abordagem escolhida anteriormente ao criar um certificado para o hub de notificações.

Importante

Ao configurar o Modo de aplicativo, escolha apenas Produção se quiser enviar notificações por push aos usuários que compraram seu aplicativo na loja.

Opção 1 – Usar um certificado push .p12

  1. Na folha Apple (APNS), selecione o modo de autenticação de Certificado.
  2. Na folha Apple (APNS), selecione o ícone de arquivo ao lado do campo Certificado de Carregamento. Em seguida, selecione o arquivo .p12 que você exportou anteriormente e carregue-o.
  3. Na folha Apple (APNS), insira a senha do certificado no campo Senha, se necessário.
  4. Na folha Apple (APNS), selecione o modo de autenticação de Área restrita.
  5. Na folha Apple (APNS), selecione o botão Salvar.

Opção 2 – Usar autenticação baseada em token

  1. Na folha Apple (APNS), selecione o modo de autenticação de Token.
  2. Na folha Apple (APNS), insira os valores adquiridos anteriormente para os campos ID de Chave, ID do Pacote, ID da Equipe e Token.
  3. Na folha Apple (APNS), selecione o modo de autenticação de Área restrita.
  4. Na folha Apple (APNS), selecione o botão Salvar.

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

Nesta seção, você criará um back-end da API Web do ASP.NET Core para lidar com instalação do dispositivo e enviar notificações para o aplicativo .NET MAUI.

Criar um projeto de API Web

Para criar um projeto de API Web:

  1. No Visual Studio, crie um projeto de API Web do ASP.NET Core:

    Captura de tela da criação de um novo projeto de API Web do ASP.NET Core no Visual Studio.

  2. Na caixa de diálogo Configurar seu novo projeto, nomeie o projeto PushNotificationsAPI.

  3. Na caixa de diálogo Informações adicionais verifique se as caixas de seleção Configurar para HTTPS e Usar controladores estão habilitadas:

    Captura de tela da configuração de um novo projeto de API Web do ASP.NET Core no Visual Studio.

  4. Depois que o projeto tiver sido criado, pressione F5 para executar o projeto.

    No momento, o aplicativo está configurado para usar o WeatherForecastController como o launchUrl, que é definido no arquivo Properties\launchSettings.json. O aplicativo será iniciado em um navegador da Web e exibirá alguns dados JSON.

    Importante

    Quando você executa um projeto do ASP.NET Core que usa HTTPS, o Visual Studio detectará se o certificado de desenvolvimento HTTPS do ASP.NET Core está instalado no repositório de certificados do usuário local e se oferecerá para instalá-lo e confiar nele, caso esteja ausente.

  5. Feche o navegador da Web.

  6. No Gerenciador de Soluções, expanda a pasta Controladores e exclua WeatherForecastController.cs.

  7. No Gerenciador de Soluções, na raiz do projeto, exclua WeatherForecast.cs.

  8. Abra uma janela de comando e navegue até o diretório que contém o arquivo de projeto. Em seguida, execute os seguintes comandos:

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

    Substitua os valores de espaço reservado por seus próprios valores de cadeia de conexão e nome do Hub de Notificações do Microsoft Azure. Eles podem ser encontrados nos seguintes locais no Hub de Notificações do Microsoft Azure:

    Valor de configuração Localidade
    NotificationHub:Name Consulte Nome no resumo Essenciais na parte superior da página Visão Geral.
    NotificationHub:ConnectinString Consulte DefaultFullSharedAccessSignature* na página Políticas de Acesso.

    Isso configura valores de configuração locais usando a ferramenta Gerente de Segredo. Isso separa os segredos do Hub de Notificações do Microsoft Azure da solução do Visual Studio para garantir que eles não acabem no controle do código-fonte.

    Dica

    Para cenários de produção, considere um serviço como Azure KeyVault para armazenar com segurança a cadeia de conexão.

Autenticar clientes com uma chave de API

Para autenticar clientes com uma chave de API:

  1. Abra uma janela de comando e navegue até o diretório que contém o arquivo de projeto. Em seguida, execute os seguintes comandos:

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

    Substitua o valor do espaço reservado pela chave de API, que pode ser qualquer valor.

  2. No Visual Studio, adicione uma nova pasta chamada Autenticação ao seu projeto, em seguida, adicione uma nova classe chamada ApiKeyAuthOptions à pastaAutenticação e substitua seu código pelo seguinte código:

    using Microsoft.AspNetCore.Authentication;
    
    namespace PushNotificationsAPI.Authentication;
    
    public class ApiKeyAuthOptions : AuthenticationSchemeOptions
    {
        public const string DefaultScheme = "ApiKey";
        public string Scheme => DefaultScheme;
        public string ApiKey { get; set; }
    }
    
  3. No Visual Studio, adicione uma nova classe chamada ApiKeyAuthHandler à pasta Autenticação e substitua seu código pelo seguinte código:

    using Microsoft.AspNetCore.Authentication;
    using Microsoft.Extensions.Options;
    using System.Security.Claims;
    using System.Text.Encodings.Web;
    
    namespace PushNotificationsAPI.Authentication;
    
    public class ApiKeyAuthHandler : AuthenticationHandler<ApiKeyAuthOptions>
    {
        const string ApiKeyIdentifier = "apikey";
    
        public ApiKeyAuthHandler(
            IOptionsMonitor<ApiKeyAuthOptions> options,
            ILoggerFactory logger,
            UrlEncoder encoder)
            : base(options, logger, encoder)
        {
        }
    
        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));
        }
    }
    

    Um manipulador de autenticação é um tipo que implementa o comportamento de um esquema, que nesse caso é um esquema de chave de API personalizado.

  4. No Visual Studio, adicione uma nova classe chamada AuthenticationBuilderExtensions à pasta Autenticação e substitua seu código pelo seguinte código:

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

    Esse método de extensão será usado para simplificar o código de configuração do middleware no Program.cs.

  5. No Visual Studio, abra Program.cs e atualize o código para configurar a autenticação de chave de API abaixo da chamada para o método builder.Services.AddControllers:

    using PushNotificationsAPI.Authentication;
    
    builder.Services.AddControllers();
    
    builder.Services.AddAuthentication(options =>
    {
        options.DefaultAuthenticateScheme = ApiKeyAuthOptions.DefaultScheme;
        options.DefaultChallengeScheme = ApiKeyAuthOptions.DefaultScheme;
    }).AddApiKeyAuth(builder.Configuration.GetSection("Authentication").Bind);
    
  6. Em Program.cs, atualize o código abaixo do comentário // Configure the HTTP request pipeline para chamar os métodos de extensão UseRouting, UseAuthentication e MapControllers:

    // Configure the HTTP request pipeline.
    
    app.UseHttpsRedirection();
    app.UseRouting();
    app.UseAuthentication();
    app.UseAuthorization();
    app.MapControllers();
    
    app.Run();
    

    O método de extensão UseAuthentication registra o middleware que usa o esquema de autenticação registrado anteriormente. UseAuthentication deve ser chamado antes de qualquer middleware que dependa da autenticação dos usuários.

    Observação

    Embora uma chave de API não seja tão segura quanto um token, ela será suficiente para este tutorial e será facilmente configurada por meio do ASP.NET Middleware.

Adicionar e configurar serviços

Para adicionar e configurar serviços em seu aplicativo de back-end da API Web:

  1. No Visual Studio, adicione o pacote NuGet Microsoft.Azure.NotificationHubs ao seu projeto. Esse pacote NuGet é usado para acessar o hub de notificações, encapsulado em um serviço.

  2. No Visual Studio, adicione uma nova pasta chamada Modelos ao seu projeto, em seguida, adicione uma nova classe chamada PushTemplates à pastaModelos e substitua seu código pelo seguinte código:

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

    A classe PushTemplates contém conteúdos de notificação tokenizadas para notificações por push genéricas e silenciosas. Esses conteúdos são definidos fora da instalação para permitir a experimentação sem precisar atualizar as instalações existentes por meio do serviço. O tratamento de alterações nas instalações dessa maneira está fora do escopo deste artigo. Em cenários de produto, considere o uso de modelos personalizados.

  3. No Visual Studio, adicione uma nova classe chamada DeviceInstallation à pasta Modelos e substitua seu código pelo seguinte código:

    using System.ComponentModel.DataAnnotations;
    
    namespace PushNotificationsAPI.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>();
    }
    
  4. No Visual Studio, adicione uma nova classe chamada NotificationRequest à pasta Modelos e substitua seu código pelo seguinte código:

    namespace PushNotificationsAPI.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; }
    }
    
  5. No Visual Studio, adicione uma nova classe chamada NotificationHubOptions à pasta Modelos e substitua seu código pelo seguinte código:

    using System.ComponentModel.DataAnnotations;
    
    namespace PushNotificationsAPI.Models;
    
    public class NotificationHubOptions
    {
        [Required]
        public string Name { get; set; }
    
        [Required]
        public string ConnectionString { get; set; }
    }
    
  6. No Visual Studio, adicione uma nova pasta chamada Serviços ao seu projeto, em seguida, adicione uma nova interface chamada INotificationService à pastaServiços e substitua seu código pelo seguinte código:

    using PushNotificationsAPI.Models;
    
    namespace PushNotificationsAPI.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);
    }
    
  7. No Visual Studio, adicione uma nova classe chamada NotificationHubService à pasta Serviços e substitua seu código pelo seguinte código:

    using Microsoft.Extensions.Options;
    using Microsoft.Azure.NotificationHubs;
    using PushNotificationsAPI.Models;
    
    namespace PushNotificationsAPI.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.FcmV1).ToLower(), NotificationPlatform.FcmV1 }
            };
        }
    
        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.SendFcmV1NativeNotificationAsync(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.SendFcmV1NativeNotificationAsync(androidPayload, tags, token),
                _hub.SendAppleNativeNotificationAsync(iOSPayload, tags, token)
            };
    
            return Task.WhenAll(sendTasks);
        }
    }
    

    A expressão de marca fornecida ao método SendTemplateNotificationsAsync será limitada a 20 marcas se elas contiverem apenas ORs. Caso contrário, elas serão limitadas a 6 marcas. Para saber mais, veja Expressões de marca e de roteamento.

  8. No Visual Studio, abra Program.cs e atualize o código para adicionar o NotificationHubService como uma implementação singleton de INotificationService abaixo da chamada para o método builder.Services.AddAuthentication:

    using PushNotificationsAPI.Authentication;
    using PushNotificationsAPI.Services;
    using PushNotificationsAPI.Models;
    
    var builder = WebApplication.CreateBuilder(args);
    
    // Add services to the container.
    
    builder.Services.AddControllers();
    
    builder.Services.AddAuthentication(options =>
    {
        options.DefaultAuthenticateScheme = ApiKeyAuthOptions.DefaultScheme;
        options.DefaultChallengeScheme = ApiKeyAuthOptions.DefaultScheme;
    }).AddApiKeyAuth(builder.Configuration.GetSection("Authentication").Bind);
    
    builder.Services.AddSingleton<INotificationService, NotificationHubService>();
    builder.Services.AddOptions<NotificationHubOptions>()
        .Configure(builder.Configuration.GetSection("NotificationHub").Bind)
        .ValidateDataAnnotations();
    
    var app = builder.Build();
    

Criar a API REST de notificações

Para criar a API REST de notificações:

  1. No Visual Studio, adicione um novo Controlador chamado NotificationsController à pasta Controladores.

    Dica

    Escolha o modelo Controlador de API com ações de leitura/gravação.

  2. No arquivo NotificationsController.cs, adicione as seguintes instruções using na parte superior do arquivo:

    using System.ComponentModel.DataAnnotations;
    using System.Net;
    using Microsoft.AspNetCore.Authorization;
    using Microsoft.AspNetCore.Mvc;
    using PushNotificationsAPI.Models;
    using PushNotificationsAPI.Services;
    
  3. No arquivo NotificationsController.cs, adicione o atributo Authorize à classe NotificationsController:

    [Authorize]
    [ApiController]
    [Route("api/[controller]")]
    public class NotificationsController : ControllerBase
    
  4. No arquivo NotificationsController.cs, atualize o construtor NotificationsContoller para aceitar a instância registrada de INotificationService como um argumento e a atribua a um membro somente leitura:

    readonly INotificationService _notificationService;
    
    public NotificationsController(INotificationService notificationService)
    {
        _notificationService = notificationService;
    }
    
  5. No arquivo NotificationsContoller.cs, substitua todos os métodos 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)
    {
        // Probably want to ensure deletion even if the connection is broken
        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();
    }
    
  6. No arquivo Propriedades/launchSettings.json, altere a propriedade launchUrl para cada perfil de weatherforecast para api/notifications.

Criar um aplicativo de API

Agora você criará um aplicativo de API no Serviço de Aplicativo do Azure para hospedar seu serviço de back-end. Isso pode ser feito diretamente do Visual Studio ou do Visual Studio Code, com a CLI do Azure, o Azure PowerShell, a Azure Developer CLI e por meio do Portal do Azure. Para obter mais informações, confira Publicar um aplicativo Web.

Para criar um aplicativo de API no portal do Azure:

  1. Em um navegador da Web, entre no portal do Azure.

  2. No portal do Azure, clique no botão Criar um recurso e, em seguida, pesquise e escolha Aplicativo de API antes de selecionar o botão Criar.

  3. Na página Criar Aplicativo de API, atualize os seguintes campos antes de selecionar o botão Criar:

    Campo Ação
    Assinatura Escolha a mesma assinatura de destino na qual você criou o hub de notificações.
    Grupo de recursos Escolha o mesmo grupo de recursos no qual você criou o hub de notificações.
    Nome Insira um nome globalmente exclusivo.
    Pilha de runtime Verifique se a versão mais recente do .NET está selecionada.
  4. Depois que o Aplicativo de API tiver sido provisionado, navegue até o recurso.

  5. Na página de Visão Geral, anote o valor do domínio padrão. Essa URL é o ponto de extremidade de back-end que será consumido do seu aplicativo .NET MAUI. A URL usará o nome do aplicativo de API especificado, com o formato https://<app_name>.azurewebsites.net.

  6. No portal do Azure, navegue até a folha Configurações > Variáveis de ambiente e verifique se a guia Configurações do aplicativo está selecionada. Em seguida, use o botão Adicionar para adicionar as seguintes configurações:

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

    Importante

    A configuração do aplicativo Authentication:ApiKey foi adicionada para simplificar. Para cenários de produção, considere um serviço como Azure KeyVault para armazenar com segurança a cadeia de conexão.

    Depois que todas essas configurações tiverem sido inseridas, selecione o botão Aplicar e, em seguida, o botão Confirmar.

Publicar o serviço de back-end

Para publicar seu serviço de back-end no Serviço de Aplicativo do Azure:

  1. No Visual Studio, clique com o botão direito do mouse no projeto e selecione Publicar.
  2. No assistente Publicar, selecione Azure e, em seguida, o botão Avançar.
  3. No assistente Publicar, selecione Serviço de Aplicativo do Azure (Windows) e, em seguida, o botão Avançar.
  4. No assistente Publicar, siga o fluxo de autenticação para conectar o Visual Studio à sua assinatura do Azure e publicar o aplicativo.

O Visual Studio cria, empacota e publica o aplicativo no Azure e, em seguida, inicia o aplicativo no navegador padrão. Para obter mais informações, confira Publicar um aplicativo Web no ASP.NET.

Dica

Você pode baixar um perfil de publicação para seu aplicativo na folha Visão Geral do aplicativo de API no portal do Azure e, em seguida, usar o perfil no Visual Studio para publicar seu aplicativo.

Validar a API publicada

Para verificar se o aplicativo de API foi publicado corretamente, você deve usar as ferramentas REST de sua escolha para enviar uma solicitação POST para o seguinte endereço:

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

Observação

O endereço base é https://<app_name>.azurewebsites.net.

Configure os cabeçalhos de solicitação para incluir a chave apikey e seu valor, defina o corpo como bruto e use o seguinte conteúdo JSON do espaço reservado:

{}

Você deve receber uma resposta 400 Bad Request do serviço.

Observação

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

Para obter mais informações sobre como chamar APIs REST, consulte Usar arquivos .http no Visual Studio e Testar APIs Web com o Http Repl. No Visual Studio Code, é possível usar o Cliente REST para testar APIs REST.

Criar um aplicativo .NET MAUI

Nesta seção, você criará um aplicativo .NET Multi-Platform App UI (.NET MAUI) que permite que você se registre para receber notificações por push de um hub de notificações por meio do serviço de back-end e cancele o registro.

Para criar seu aplicativo .NET MAUI:

  1. No Visual Studio, crie um novo aplicativo .NET MAUI chamado PushNotificationsDemo, usando o modelo de projeto Aplicativo .NET MAUI.

  2. No Visual Studio, adicione uma nova pasta chamada Modelos ao projeto .NET MAUI e adicione uma nova classe chamada DeviceInstallation à pasta Modelos e substitua seu código pelo seguinte código:

    using System.Text.Json.Serialization;
    
    namespace PushNotificationsDemo.Models;
    
    public class DeviceInstallation
    {
        [JsonPropertyName("installationId")]
        public string InstallationId { get; set; }
    
        [JsonPropertyName("platform")]
        public string Platform { get; set; }
    
        [JsonPropertyName("pushChannel")]
        public string PushChannel { get; set; }
    
        [JsonPropertyName("tags")]
        public List<string> Tags { get; set; } = new List<string>();
    }
    
  3. No Visual Studio, adicione uma enumeração chamada PushDemoAction à pasta Modelos e substitua seu código pelo seguinte código:

    namespace PushNotificationsDemo.Models;
    
    public enum PushDemoAction
    {
        ActionA,
        ActionB
    }
    
  4. No Visual Studio, adicione uma nova pasta chamada Serviços ao projeto .NET MAUI e adicione uma nova interface chamada IDeviceInstallationService à pasta Serviços e substitua seu código pelo seguinte código:

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

    Essa interface será implementada em cada plataforma posteriormente, para fornecer as informações de DeviceInstallation exigidas pelo serviço de back-end.

  5. No Visual Studio, adicione uma interface chamada INotificationRegistrationService à pasta Serviços e substitua seu código pelo seguinte código:

    namespace PushNotificationsDemo.Services;
    
    public interface INotificationRegistrationService
    {
        Task DeregisterDeviceAsync();
        Task RegisterDeviceAsync(params string[] tags);
        Task RefreshRegistrationAsync();
    }
    

    Essa interface lidará com a interação entre o cliente e o serviço de back-end.

  6. No Visual Studio, adicione uma interface chamada INotificationActionService à pasta Serviços e substitua seu código pelo seguinte código:

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

    Essa interface será usada como um mecanismo simples para centralizar o tratamento de ações de notificação.

  7. No Visual Studio, adicione uma interface chamada IPushDemoNotificationActionService à pasta Serviços e substitua seu código pelo seguinte código:

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

    O tipo IPushDemoNotificationActionService é específico para este aplicativo e usa a enumeração PushDemoAction para identificar a ação que está sendo disparada usando uma abordagem fortemente tipada.

  8. No Visual Studio, adicione uma classe chamada NotificationRegistrationService à pasta Serviços e substitua seu código pelo seguinte código:

    using System.Text;
    using System.Text.Json;
    using PushNotificationsDemo.Models;
    
    namespace PushNotificationsDemo.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;
    
        IDeviceInstallationService DeviceInstallationService =>
            _deviceInstallationService ?? (_deviceInstallationService = Application.Current.Windows[0].Page.Handler.MauiContext.Services.GetService<IDeviceInstallationService>());
    
        public NotificationRegistrationService(string baseApiUri, string apiKey)
        {
            _client = new HttpClient();
            _client.DefaultRequestHeaders.Add("Accept", "application/json");
            _client.DefaultRequestHeaders.Add("apikey", apiKey);
    
            _baseApiUrl = baseApiUri;
        }
    
        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, JsonSerializer.Serialize(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 = JsonSerializer.Deserialize<string[]>(serializedTags);
    
            await RegisterDeviceAsync(tags);
        }
    
        async Task SendAsync<T>(HttpMethod requestType, string requestUri, T obj)
        {
            string serializedContent = null;
    
            await Task.Run(() => serializedContent = JsonSerializer.Serialize(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();
        }
    }
    
  9. No Visual Studio, adicione uma classe chamada PushDemoNotificationActionService à pasta Serviços e substitua seu código pelo seguinte código:

    using PushNotificationsDemo.Models;
    
    namespace PushNotificationsDemo.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);
        }
    }
    
  10. No Visual Studio, adicione uma classe chamada Config à raiz do projeto e substitua seu código pelo seguinte código:

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

    A classe Config é usada como uma maneira simples de manter seus segredos fora do controle do código-fonte. Você pode substituir esses valores como parte de um build automatizado ou substituí-los usando uma classe parcial local.

    Importante

    Ao especificar o endereço base no aplicativo .NET MAUI, verifique se ele termina com um /.

  11. No Visual Studio, adicione uma classe chamada Config.local_secrets à raiz do projeto. Em seguida, substitua o código no arquivo Config.local_secrets.cs pelo seguinte código:

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

    Substitua os valores de espaço reservado pelos valores escolhidos ao criar o serviço de back-end. A URL BackendServiceEndpoint deve usar o formato https://<api_app_name>.azurewebsites.net/.

    Dica

    Lembre-se de adicionar *.local_secrets.* ao arquivo .gitignore para evitar a confirmação desse arquivo no controle do código-fonte.

Criar a interface do usuário

Para criar a interface do usuário do aplicativo:

  1. No Visual Studio, abra MainPage.xaml e substitua o VerticalStackLayout e seus filhos pelo seguinte XAML:

    <VerticalStackLayout Margin="20"
                         Spacing="6">
        <Button x:Name="registerButton"
                Text="Register"
                Clicked="OnRegisterButtonClicked" />
        <Button x:Name="deregisterButton"
                Text="Deregister"
                Clicked="OnDeregisterButtonClicked" />
    </VerticalStackLayout>
    
  2. No Visual Studio, abra MainPage.xaml.cs e adicione uma instrução using para o namespace PushNotificationsDemo.Services:

    using PushNotificationsDemo.Services;
    
  3. Em MainPage.xaml.cs, adicione um campo de suporte readonly para armazenar uma referência à implementação de INotificationRegistrationService:

    readonly INotificationRegistrationService _notificationRegistrationService;
    
  4. No construtor MainPage, resolva a implementação de INotificationRegistrationService e atribua-a ao campo de suporte _notificationRegistrationService:

    public MainPage(INotificationRegistrationService service)
    {
        InitializeComponent();
    
        _notificationRegistrationService = service;
    }
    
  5. Na classe MainPage, implemente os manipuladores de eventos OnRegisterButtonClicked e OnDeregisterButtonClicked, chamando os métodos de registro e cancelamento de registro correspondentes no objeto INotificationRegistrationService:

    void OnRegisterButtonClicked(object sender, EventArgs e)
    {
        _notificationRegistrationService.RegisterDeviceAsync()
            .ContinueWith((task) =>
            {
                ShowAlert(task.IsFaulted ? task.Exception.Message : $"Device registered");
            });
    }
    
    void OnDeregisterButtonClicked(object sender, EventArgs e)
    {
        _notificationRegistrationService.DeregisterDeviceAsync()
            .ContinueWith((task) =>
            {
                ShowAlert(task.IsFaulted ? task.Exception.Message : $"Device deregistered");
            });
    }
    
    void ShowAlert(string message)
    {
        MainThread.BeginInvokeOnMainThread(() =>
        {
            DisplayAlert("Push notifications demo", message, "OK")
                .ContinueWith((task) =>
                {
                    if (task.IsFaulted)
                        throw task.Exception;
                });
        });
    }
    

    Importante

    No aplicativo, o registro e o cancelamento de registro são executados em resposta à entrada do usuário, para permitir que essa funcionalidade seja explorada e testada com mais facilidade. Em um aplicativo de produção, você normalmente executaria as ações de registro e cancelamento de registro durante o ponto apropriado no ciclo de vida do aplicativo, sem a necessidade de entrada explícita do usuário.

  6. No Visual Studio, abra App.xaml.cs e adicione as seguintes instruções using:

    using PushNotificationsDemo.Models;
    using PushNotificationsDemo.Services;
    
  7. Em App.xaml.cs, adicione um campo de suporte readonly para armazenar uma referência à implementação do IPushDemoNotificationActionService:

    readonly IPushDemoNotificationActionService _actionService;
    
  1. No construtor App, resolva a implementação de IPushDemoNotificationActionService, atribua-a ao campo de suporte _actionService e assine o evento IPushDemoNotificationActionService.ActionTriggered:

    public App(IPushDemoNotificationActionService service)
    {
        InitializeComponent();
    
        _actionService = service;
        _actionService.ActionTriggered += NotificationActionTriggered;
    
        MainPage = new AppShell();
    }
    
  1. No construtor App, resolva a implementação de IPushDemoNotificationActionService, atribua-a ao campo de suporte _actionService e assine o evento IPushDemoNotificationActionService.ActionTriggered:

    public App(IPushDemoNotificationActionService service)
    {
        InitializeComponent();
    
        _actionService = service;
        _actionService.ActionTriggered += NotificationActionTriggered;
    }
    
  1. Na classe App, implemente o manipulador de eventos para o evento IPushDemoNotificationActionService.ActionTriggered:

    void NotificationActionTriggered(object sender, PushDemoAction e)
    {
        ShowActionAlert(e);
    }
    
    void ShowActionAlert(PushDemoAction action)
    {
        MainThread.BeginInvokeOnMainThread(() =>
        {
            Windows[0].Page?.DisplayAlert("Push notifications demo", $"{action} action received.", "OK")
                .ContinueWith((task) =>
                {
                    if (task.IsFaulted)
                        throw task.Exception;
                });
        });
    }
    

    O manipulador de eventos do evento ActionTriggered demonstra o recebimento e a propagação de ações de notificação por push. Normalmente, eles seriam tratados silenciosamente, por exemplo, navegando para uma exibição específica ou atualizando alguns dados, em vez de exibir um alerta.

Configurar o aplicativo Android

Para configurar seu aplicativo .NET MAUI no Android para receber e processar notificações por push:

  1. No Visual Studio, adicione o pacote NuGet Xamarin.Firebase.Messaging ao seu projeto de aplicativo .NET MAUI.

  2. No Visual Studio, adicione seu arquivo google-services.json à pasta Plataformas/Android do seu projeto de aplicativo .NET MAUI. Depois que o arquivo tiver sido adicionado ao seu projeto, ele deverá ter sido adicionado com uma ação de build de GoogleServicesJson:

    <ItemGroup Condition="'$(TargetFramework)' == 'net8.0-android'">
      <GoogleServicesJson Include="Platforms\Android\google-services.json" />
    </ItemGroup>
    

    Dica

    Lembre-se de adicionar google-services.json ao arquivo .gitignore para evitar a confirmação desse arquivo no controle do código-fonte.

  3. No Visual Studio, edite o arquivo de projeto (*.csproj) e defina o SupportedOSPlatformVersion para Android como 26.0:

    <SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'android'">26.0</SupportedOSPlatformVersion>
    

    O Google fez alterações nos canais de notificação do Android na API 26. Para obter mais informações, confira Canais de notificação em developer.android.com.

  4. Na pasta Plataformas/Android do projeto, adicione uma nova classe chamada DeviceInstallationService e substitua seu código pelo seguinte código:

    using Android.Gms.Common;
    using PushNotificationsDemo.Models;
    using PushNotificationsDemo.Services;
    using static Android.Provider.Settings;
    
    namespace PushNotificationsDemo.Platforms.Android;
    
    public class DeviceInstallationService : IDeviceInstallationService
    {
        public string Token { get; set; }
    
        public bool NotificationsSupported
            => GoogleApiAvailability.Instance.IsGooglePlayServicesAvailable(Platform.AppContext) == ConnectionResult.Success;
    
        public string GetDeviceId()
            => Secure.GetString(Platform.AppContext.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 FCMv1.");
    
            var installation = new DeviceInstallation
            {
                InstallationId = GetDeviceId(),
                Platform = "fcmv1",
                PushChannel = Token
            };
    
            installation.Tags.AddRange(tags);
    
            return installation;
        }
    
        string GetPlayServicesError()
        {
            int resultCode = GoogleApiAvailability.Instance.IsGooglePlayServicesAvailable(Platform.AppContext);
    
            if (resultCode != ConnectionResult.Success)
                return GoogleApiAvailability.Instance.IsUserResolvableError(resultCode) ?
                           GoogleApiAvailability.Instance.GetErrorString(resultCode) :
                           "This device isn't supported.";
    
            return "An error occurred preventing the use of push notifications.";
        }
    }
    

    Essa classe fornece uma ID exclusiva, usando o valor Secure.AndroidId e o conteúdo de registro do hub de notificações.

  5. Na pasta Plataformas/Android do projeto, adicione uma nova classe chamada PushNotificationFirebaseMessagingService e substitua seu código pelo seguinte código:

    using Android.App;
    using Firebase.Messaging;
    using PushNotificationsDemo.Services;
    
    namespace PushNotificationsDemo.Platforms.Android;
    
    [Service(Exported = false)]
    [IntentFilter(new[] { "com.google.firebase.MESSAGING_EVENT" })]
    public class PushNotificationFirebaseMessagingService : FirebaseMessagingService
    {
        IPushDemoNotificationActionService _notificationActionService;
        INotificationRegistrationService _notificationRegistrationService;
        IDeviceInstallationService _deviceInstallationService;
        int _messageId;
    
        IPushDemoNotificationActionService NotificationActionService =>
            _notificationActionService ?? (_notificationActionService = IPlatformApplication.Current.Services.GetService<IPushDemoNotificationActionService>());
    
        INotificationRegistrationService NotificationRegistrationService =>
            _notificationRegistrationService ?? (_notificationRegistrationService = IPlatformApplication.Current.Services.GetService<INotificationRegistrationService>());
    
        IDeviceInstallationService DeviceInstallationService =>
            _deviceInstallationService ?? (_deviceInstallationService = IPlatformApplication.Current.Services.GetService<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)
        {
            base.OnMessageReceived(message);
    
            if (message.Data.TryGetValue("action", out var messageAction))
                NotificationActionService.TriggerAction(messageAction);
        }
    }
    

    Essa classe tem um atributo IntentFilter que inclui o filtro com.google.firebase.MESSAGING_EVENT. Esse filtro permite que o Android passe mensagens de entrada para essa classe para processamento.

    Para obter informações sobre o formato de mensagem do Firebase Cloud Messaging, consulte Sobre mensagens FCM em developer.android.com.

  6. No Visual Studio, abra o arquivo MainActivity.cs na pasta Plataformas/Android e adicione as seguintes instruções using:

    using Android.App;
    using Android.Content;
    using Android.Content.PM;
    using Android.OS;
    using PushNotificationsDemo.Services;
    using Firebase.Messaging;
    
  7. Na classe MainActivity, defina o LaunchMode como SingleTop para que o MainActivity não seja criado novamente quando aberto:

    [Activity(
        Theme = "@style/Maui.SplashTheme",
        MainLauncher = true,
        LaunchMode = LaunchMode.SingleTop,
        ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize | ConfigChanges.Density)]
    
  8. Na classe MainActivity, adicione campos de backup para armazenar referências às implementações de IPushDemoNotificationActionService e IDeviceInstallationService:

    IPushDemoNotificationActionService _notificationActionService;
    IDeviceInstallationService _deviceInstallationService;
    
  9. Na classe MainActivity, adicione propriedades privadasNotificationActionService e DeviceInstallationService que recuperam suas implementações concretas do contêiner de injeção de dependência do aplicativo:

    IPushDemoNotificationActionService NotificationActionService =>
        _notificationActionService ?? (_notificationActionService = IPlatformApplication.Current.Services.GetService<IPushDemoNotificationActionService>());
    
    IDeviceInstallationService DeviceInstallationService =>
        _deviceInstallationService ?? (_deviceInstallationService = IPlatformApplication.Current.Services.GetService<IDeviceInstallationService>());
    
  10. Na classe MainActivity, implemente a interface Android.Gms.Tasks.IOnSuccessListener para recuperar e armazenar o token do Firebase:

    public class MainActivity : MauiAppCompatActivity, Android.Gms.Tasks.IOnSuccessListener
    {
        public void OnSuccess(Java.Lang.Object result)
        {
            DeviceInstallationService.Token = result.ToString();
        }
    }
    
  11. Na classe MainActivity, adicione o método ProcessNotificationActions que verificará se um determinado Intent tem um valor extra chamado action e, em seguida, disparará condicionalmente esse action usando a implementação de IPushDemoNotificationActionService:

    void ProcessNotificationsAction(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);
        }
    }
    
  12. Na classe MainActivity, substitua o método OnNewIntent para chamar o método ProcessNotificationActions:

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

    Como o LaunchMode do Activity está definido como SingleTop, um Intent será enviado para a instância de Activity existente por meio da substituição OnNewIntent, em vez do método OnCreate. Portanto, você deve lidar com uma intenção de entrada em OnNewIntent e OnCreate.

  13. Na classe MainActivity, substitua o método OnCreate para chamar o método ProcessNotificationActions e recupere o token do Firebase, adicionando MainActivity como o IOnSuccessListener:

    protected override void OnCreate(Bundle? savedInstanceState)
    {
        base.OnCreate(savedInstanceState);
    
        if (DeviceInstallationService.NotificationsSupported)
            FirebaseMessaging.Instance.GetToken().AddOnSuccessListener(this);
    
        ProcessNotificationsAction(Intent);
    }
    

    Observação

    O aplicativo deve ser registrado novamente sempre que for executado e interrompido em uma sessão de depuração para continuar recebendo notificações por push.

  14. No Visual Studio, adicione a permissão POST_NOTIFICATIONS ao arquivo AndroidManifest.xml na pasta Plataformas/Android:

    <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
    

    Para obter mais informações sobre essa permissão, consulte Permissão de runtime de notificação em developer.android.com.

  15. No Visual Studio, abra MainPage.xaml.cs e adicione o seguinte código à classe MainPage:

    #if ANDROID
            protected override async void OnAppearing()
            {
                base.OnAppearing();
    
                PermissionStatus status = await Permissions.RequestAsync<Permissions.PostNotifications>();
            }
    #endif
    

    Esse código é executado no Android quando o MainPage é exibido e solicita que o usuário conceda a permissão POST_NOTIFICATIONS. Para obter mais informações sobre as permissões do .NET MAUI, confira Permissões.

Configurar o aplicativo iOS

O simulador iOS suporta notificações remotas no iOS 16+ quando executado no macOS 13+ em computadores Mac com processadores Apple Silicon ou T2. Cada simulador gera tokens de registro exclusivos para a combinação desse simulador e o hardware Mac em que estiver sendo executado.

Importante

O simulador dá suporte ao ambiente de área restrita do Apple Push Notification Service.

As instruções a seguir pressupõem que você esteja usando hardware que dá suporte ao recebimento de notificações remotas em um simulador do iOS. Se esse não for o caso, você precisará executar o aplicativo iOS em um dispositivo físico, o que exigirá que você crie um perfil de provisionamento para seu aplicativo que inclua a funcionalidade de Notificações por Push. Em seguida, você precisará garantir que seu aplicativo seja criado usando seu certificado e perfil de provisionamento. Para obter mais informações sobre como fazer isso, consulte Configurar seu aplicativo iOS para trabalhar com os Hubs de Notificações do Microsoft Azure e siga as instruções abaixo.

Para configurar seu aplicativo .NET MAUI no iOS para receber e processar notificações por push:

  1. No Visual Studio, edite o arquivo de projeto (*.csproj) e defina o SupportedOSPlatformVersion para iOS como 13.0:

    <SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'">13.0</SupportedOSPlatformVersion>
    

    A Apple fez alterações em seu serviço de push no iOS 13. Para obter mais informações, consulte Atualizações dos Hubs de Notificações do Microsoft Azure para iOS 13.

  2. No Visual Studio, adicione um arquivo Entitlements.plist à pasta Plataformas/iOS do projeto e adicione o seguinte XML ao arquivo:

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
    <plist version="1.0">
    <dict>
      <key>aps-environment</key>
      <string>development</string>
    </dict>
    </plist>
    

    Isso define o direito do ambiente APS e especifica o uso do ambiente de desenvolvimento do serviço Apple Push Notification. Em aplicativos de produção, esse valor de direito deve ser definido como production. Para obter mais informações sobre esse direito, consulte Direitos de ambiente do APS em developer.apple.com.

    Para mais informações sobre como adicionar um arquivo de direitos, consulte Direitos do iOS.

  3. No Visual Studio, adicione uma nova classe chamada DeviceInstallationService à pasta Plataformas/iOS do projeto e adicione o seguinte código ao arquivo:

    using PushNotificationsDemo.Services;
    using PushNotificationsDemo.Models;
    using UIKit;
    
    namespace PushNotificationsDemo.Platforms.iOS;
    
    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";
        }
    }
    

    Essa classe fornece uma ID exclusiva, usando o valor UIDevice.IdentifierForVendor e o conteúdo de registro do hub de notificações.

  4. No Visual Studio, adicione uma nova classe chamada NSDataExtensions à pasta Plataformas/iOS do projeto e adicione o seguinte código ao arquivo:

    using Foundation;
    using System.Text;
    
    namespace PushNotificationsDemo.Platforms.iOS;
    
    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();
        }
    }
    

    O método de extensão ToHexString será consumido pelo código que você adicionará que analisa o token de dispositivo recuperado.

  5. No Visual Studio, abra o arquivo AppDelegate.cs na pasta Plataformas/iOS e adicione as seguintes instruções using:

    using System.Diagnostics;
    using Foundation;
    using PushNotificationsDemo.Platforms.iOS;
    using PushNotificationsDemo.Services;
    using UIKit;
    using UserNotifications;
    
  6. Na classe AppDelegate, adicione campos de backup para armazenar referências às implementações de IPushDemoNotificationActionService, INotificationRegistrationService e IDeviceInstallationService:

    IPushDemoNotificationActionService _notificationActionService;
    INotificationRegistrationService _notificationRegistrationService;
    IDeviceInstallationService _deviceInstallationService;
    
  7. Na classe AppDelegate, adicione propriedades privadasNotificationActionService, NotificationRegistrationService e DeviceInstallationService que recuperam suas implementações concretas do contêiner de injeção de dependência do aplicativo:

    IPushDemoNotificationActionService NotificationActionService =>
        _notificationActionService ?? (_notificationActionService = IPlatformApplication.Current.Services.GetService<IPushDemoNotificationActionService>());
    
    INotificationRegistrationService NotificationRegistrationService =>
        _notificationRegistrationService ?? (_notificationRegistrationService = IPlatformApplication.Current.Services.GetService<INotificationRegistrationService>());
    
    IDeviceInstallationService DeviceInstallationService =>
        _deviceInstallationService ?? (_deviceInstallationService = IPlatformApplication.Current.Services.GetService<IDeviceInstallationService>());
    
  8. Na classe AppDelegate, adicione o método CompleteRegistrationAsync para definir o valor da propriedade IDeviceInstallationService.Token:

    Task CompleteRegistrationAsync(NSData deviceToken)
    {
        DeviceInstallationService.Token = deviceToken.ToHexString();
        return NotificationRegistrationService.RefreshRegistrationAsync();
    }
    

    Esse método também atualiza o registro e armazena em cache o token do dispositivo se ele tiver sido atualizado desde a última vez que foi armazenado.

  9. Na classe AppDelegate, adicione o método ProcessNotificationActions para processar os dados de notificação NSDictionary e chamar condicionalmente NotificationActionService.TriggerAction:

    void ProcessNotificationActions(NSDictionary userInfo)
    {
        if (userInfo == null)
            return;
    
        try
        {
            // If your app isn't in the foreground, the notification goes to Notification Center.
            // If your app is in the foreground, the notification goes directly to your app and you
            // need to process the notification payload yourself.
            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. Na classe AppDelegate, adicione o método RegisteredForRemoteNotifications passando o argumento deviceToken ao método CompleteRegistrationAsync:

    [Export("application:didRegisterForRemoteNotificationsWithDeviceToken:")]
    public void RegisteredForRemoteNotifications(UIApplication application, NSData deviceToken)
    {
        CompleteRegistrationAsync(deviceToken)
            .ContinueWith((task) =>
            {
                if (task.IsFaulted)
                    throw task.Exception;
            });
    }
    

    Esse método será chamado quando o aplicativo for registrado para receber notificação remota e é usado para solicitar o token exclusivo do dispositivo, que é efetivamente o endereço do seu aplicativo no dispositivo.

  11. Na classe AppDelegate, adicione o método ReceivedRemoteNotification passando o argumento userInfo ao método ProcessNotificationActions:

    [Export("application:didReceiveRemoteNotification:")]
    public void ReceivedRemoteNotification(UIApplication application, NSDictionary userInfo)
    {
        ProcessNotificationActions(userInfo);
    }
    

    Esse método será chamado quando o aplicativo receber uma notificação remota e for usado para processar a notificação.

  12. Na classe AppDelegate, adicione o método FailedToRegisterForRemoteNotifications para registrar os erros em log:

    [Export("application:didFailToRegisterForRemoteNotificationsWithError:")]
    public void FailedToRegisterForRemoteNotifications(UIApplication application, NSError error)
    {
        Debug.WriteLine(error.Description);
    }
    

    Esse método será chamado quando o aplicativo não se registrar para receber notificações remotas. O registro poderá falhar se o dispositivo não estiver conectado à rede, se o servidor APNS estiver inacessível ou se o aplicativo estiver configurado incorretamente.

    Observação

    Para cenários de produção, é necessário implementar o registro adequado e o tratamento de erros no método FailedToRegisterForRemoteNotifications.

  13. Na classe AppDelegate, adicione o método FinishedLaunching para solicitar condicionalmente permissão para usar notificações e registrar-se para notificações remotas:

    [Export("application:didFinishLaunchingWithOptions:")]
    public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions)
    {
        if (DeviceInstallationService.NotificationsSupported)
        {
            UNUserNotificationCenter.Current.RequestAuthorization(
                UNAuthorizationOptions.Alert |
                UNAuthorizationOptions.Badge |
                UNAuthorizationOptions.Sound,
                (approvalGranted, error) =>
                {
                    if (approvalGranted && error == null)
                    {
                        MainThread.BeginInvokeOnMainThread(() =>
                        {
                            UIApplication.SharedApplication.RegisterForRemoteNotifications();
                        });
                    }
                });
        }
    
        using (var userInfo = launchOptions?.ObjectForKey(UIApplication.LaunchOptionsRemoteNotificationKey) as NSDictionary)
        {
            ProcessNotificationActions(userInfo);
        }
    
        return base.FinishedLaunching(application, launchOptions);
    }
    

    Para obter informações sobre como solicitar permissão para usar notificações, consulte Solicitar permissão para usar notificações em developer.apple.com.

Para obter informações sobre notificações no iOS, consulte Notificações do Usuário em developer.apple.com.

Registrar tipos com o contêiner de injeção de dependência do aplicativo

  1. No Visual Studio, abra MauiProgram.cs e adicione uma instrução using para o namespace PushNotificationsDemo.Services:

    using PushNotificationsDemo.Services;
    
  2. Na classe MauiProgram, adicione código para o método de extensão RegisterServices que registra os serviços DeviceInstallationService em cada plataforma e PushDemoNotificationActionService e NotificationRegistrationService multiplataforma e que retorna um objeto MauiAppBuilder:

    public static MauiAppBuilder RegisterServices(this MauiAppBuilder builder)
    {
    #if IOS
        builder.Services.AddSingleton<IDeviceInstallationService, PushNotificationsDemo.Platforms.iOS.DeviceInstallationService>();
    #elif ANDROID
        builder.Services.AddSingleton<IDeviceInstallationService, PushNotificationsDemo.Platforms.Android.DeviceInstallationService>();
    #endif
    
        builder.Services.AddSingleton<IPushDemoNotificationActionService, PushDemoNotificationActionService>();
        builder.Services.AddSingleton<INotificationRegistrationService>(new NotificationRegistrationService(Config.BackendServiceEndpoint, Config.ApiKey));
    
        return builder;
    }
    
  3. Na classe MauiProgram, adicione código para o método de extensão RegisterViews que registra o tipo MainPage como um singleton e que retorna um objeto MauiAppBuilder:

    public static MauiAppBuilder RegisterViews(this MauiAppBuilder builder)
    {
        builder.Services.AddSingleton<MainPage>();
        return builder;
    }
    

    O tipo MainPage é registrado porque requer uma dependência INotificationRegistrationService e todos os tipos que exigem uma dependência devem ser registrados com o contêiner de injeção de dependência.

  4. Na classe MauiProgram, modifique o método CreateMauiApp para que ele chame os métodos de extensão RegisterServices e RegisterViews:

    public static MauiApp CreateMauiApp()
    {
        var builder = MauiApp.CreateBuilder();
        builder
            .UseMauiApp<App>()
            .ConfigureFonts(fonts =>
            {
                fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
                fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
            })
            .RegisterServices()
            .RegisterViews();
    
    #if DEBUG
          builder.Logging.AddDebug();
    #endif
          return builder.Build();
    }
    

Para obter mais informações sobre a injeção de dependência em aplicativos .NET MAUI, confira Injeção de dependência.

Testar o aplicativo

Você pode testar seu aplicativo enviando notificações por push para o aplicativo usando o serviço de back-end ou por meio do portal do Azure.

O simulador iOS suporta notificações remotas no iOS 16+ quando executado no macOS 13+ em computadores Mac com processadores Apple Silicon ou T2. Se você não atender a esses requisitos de hardware, precisará testar seu aplicativo iOS em um dispositivo físico. No Android, você pode testar seu aplicativo em um dispositivo físico desbloqueado pelo desenvolvedor ou em um emulador.

Android e iOS exibem notificações por push em nome do aplicativo quando ele está em execução em segundo plano. Se o aplicativo estiver em execução em primeiro plano quando a notificação for recebida, o código do aplicativo determinará o comportamento. Por exemplo, você pode atualizar a interface do aplicativo para refletir as novas informações contidas na notificação.

Testar usando o serviço de back-end

Para enviar uma notificação por push de teste para seu aplicativo por meio do serviço de back-end publicado no Serviço de Aplicativo do Azure:

  1. No Visual Studio, execute o aplicativo PushNotificationsDemo no Android ou iOS e selecione o botão Registrar.

    Observação

    Se você estiver testando no Android, verifique se não está em execução usando a configuração de depuração. Como alternativa, se o aplicativo tiver sido implantado anteriormente, certifique-se de que ele tenha sido forçado a fechar e inicie-o novamente no inicializador.

  2. Na ferramenta REST de sua escolha, envie uma solicitação POST para o seguinte endereço:

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

    Configure os cabeçalhos de solicitação para incluir a chave apikey e seu valor, defina o corpo como bruto e use o seguinte conteúdo JSON do espaço reservado:

    {
        "text": "Message from REST tooling!",
        "action": "action_a"
    }
    

    A solicitação geral deve ser semelhante ao seguinte exemplo:

    POST /api/notifications/requests HTTP/1.1
    Host: https://<app_name>.azurewebsites.net
    apikey: <your_api_key>
    Content-Type: application/json
    
    {
        "text": "Message from REST tooling!",
        "action": "action_a"
    }
    
  3. Na ferramenta REST de sua escolha, valide se você receber uma resposta 200 OK.

  4. No aplicativo no Android ou iOS, um alerta deve aparecer mostrando Ação ActionA recebida.

Para obter mais informações sobre como chamar APIs REST, consulte Usar arquivos .http no Visual Studio e Testar APIs Web com o Http Repl. No Visual Studio Code, é possível usar o Cliente REST para testar APIs REST.

Testar usando o portal do Azure

Os Hubs de Notificações do Microsoft Azure permitem verificar se seu aplicativo pode receber notificações por push.

Para enviar uma notificação por push de teste para seu aplicativo por meio do portal do Azure:

  1. No Visual Studio, execute o aplicativo PushNotificationsDemo no Android ou iOS e selecione o botão Registrar.

    Observação

    Se você estiver testando no Android, verifique se não está em execução usando a configuração de depuração. Como alternativa, se o aplicativo tiver sido implantado anteriormente, certifique-se de que ele tenha sido forçado a fechar e inicie-o novamente no inicializador.

  2. No portal do Azure, navegue até o hub de notificações e selecione o botão Enviar teste na folha Visão Geral.

  3. Na folha Enviar teste, selecione a Plataforma necessária e modifique o conteúdo.

    Para Apple, use o seguinte conteúdo:

    {
      "aps": {
        "alert": "Message from Notification Hub!"
      },
      "action": "action_a"
    }
    

    Para Android, use o seguinte conteúdo:

    {
      "message": {
        "notification": {
          "title": "PushDemo",
          "body": "Message from Notification Hub!"
        },
        "data": {
          "action": "action_a"
        }
      }
    }
    

    O portal do Azure deve indicar que o envio da notificação foi bem-sucedido.

    Para obter informações sobre o formato de mensagem do Firebase Cloud Messaging, consulte Sobre mensagens FCM em developer.android.com.

  4. No aplicativo no Android ou iOS, um alerta deve aparecer mostrando Ação ActionA recebida.

Solução de problemas

As seções a seguir discutem os problemas comuns encontrados ao tentar consumir notificações por push em um aplicativo cliente.

Sem resposta do serviço de back-end

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

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

Verifique se você especificou o endereço base corretamente em suas ferramentas REST ou na configuração do aplicativo .NET MAUI. O endereço base deve ser https://<api_name>.azurewebsites.net ou https://localhost:7020 ao testar localmente.

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

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

Se você receber esse erro ao testar localmente, verifique se o valor da chave definido em seu aplicativo .NET MAUI corresponde ao valor de segredos do usuário Authentication:ApiKey usado pelo serviço de back-end.

Se você estiver testando com um aplicativo de API do Azure, verifique se o valor da chave definido em seu aplicativo .NET MAUI corresponde ao valor de configuração de aplicativo Authentication:ApiKey definido no portal do Azure. Se você criou ou alterou essa configuração de aplicativo depois de implantar o serviço de back-end, será necessário reiniciar o serviço para que o valor entre em vigor.

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

Valide se o ponto de extremidade e o método de solicitação HTTP estão corretos:

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

Ou, ao testar localmente:

  • PUT - https://localhost:7020/api/notifications/installations
  • DELETE - https://localhost:7020/api/notifications/installations/<installation_id>
  • POST - https://localhost:7020/api/notifications/requests

Importante

Ao especificar o endereço base no aplicativo .NET MAUI, verifique se ele termina com um /. O endereço base deve ser https://<api_name>.azurewebsites.net ou https://localhost:7020/ ao testar localmente.

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

Certifique-se de registrar-se sempre que iniciar uma sessão de depuração. O depurador fará com que um novo token do Firebase seja gerado e, portanto, a instalação do hub de notificações deve ser atualizada.

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

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

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

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

Valide se o serviço de back-end está funcionando conforme o esperado pelas ferramentas REST de sua escolha e use o conteúdo criado pelo aplicativo .NET MAUI para sua plataforma escolhida.

Examine as seções de configuração específicas da plataforma para garantir que nenhuma etapa tenha sido perdida. Verifique se os valores adequados estão sendo resolvidos para variáveis InstallationId e Token para sua plataforma escolhida.

Não é possível resolver uma ID para o dispositivo a mensagem de erro do dispositivo

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