Compartir a través de


Tutorial: Envío de notificaciones de inserción a aplicaciones MAUI de .NET mediante Azure Notification Hubs a través de un servicio back-end

Examinar ejemplo. Examinar el ejemplo

Las notificaciones de inserción envían información desde un sistema back-end a una aplicación cliente. Apple, Google y otras plataformas tienen su propio servicio de notificaciones push (PNS). Azure Notification Hubs le permite centralizar las notificaciones entre plataformas para que la aplicación back-end pueda comunicarse con un único centro, que se encarga de distribuir notificaciones a cada PNS.

Azure Notification Hubs requiere que las aplicaciones se registren con el centro y opcionalmente, defina plantillas o suscríbase a etiquetas:

  • La realización de una instalación de dispositivos vincula un identificador PNS a un identificador del Centro de notificaciones de Azure. Para obtener más información sobre los registros, consulte Administración de registros.
  • Las plantillas permiten a los dispositivos especificar plantillas de mensajes con parámetros. Los mensajes entrantes se pueden personalizar por dispositivo. Para obtener más información, consulte Plantillas de Notification Hubs.
  • Las etiquetas se pueden usar para suscribirse a categorías de mensajes, como noticias, deportes y el tiempo. Para obtener más información, consulte Expresiones de etiqueta y enrutamiento.

En este tutorial, usará Azure Notification Hubs para enviar notificaciones de inserción a una aplicación de interfaz de usuario de aplicación multiplataforma de .NET (.NET MAUI) destinada a Android e iOS. Un back-end de API web de ASP.NET Core se usa para controlar el registro de dispositivos para el cliente y para iniciar una notificación de inserción. Estas operaciones se controlan mediante el paquete NuGet Microsoft.Azure.NotificationHubs. Para obtener más información sobre el enfoque general, consulte Administración de registros desde un back-end.

En este tutorial ha:

  • Configure los servicios de notificaciones de inserción y Azure Notification Hub.
  • Cree una aplicación back-end de WebAPI de ASP.NET Core.
  • Cree una aplicación .NET MAUI.
  • Configure la aplicación Android para las notificaciones de inserción.
  • Configure la aplicación iOS para notificaciones de inserción.
  • Probar la aplicación.
  • Solucione los problemas de configuración y configuración.

Requisitos previos

Para completar este tutorial, necesitará lo siguiente:

  • Una cuenta de Azure con una suscripción activa.
  • Un equipo o Mac que ejecute la versión más reciente de Visual Studio/Visual Studio Code con la carga de trabajo desarrollo de la interfaz de usuario de aplicaciones multiplataforma de .NET y las cargas de trabajo de desarrollo web y ASP.NET instaladas.

En el caso de Android, debe tener lo siguiente:

  • Un desarrollador desbloqueó un dispositivo físico o un emulador que ejecuta la API 26+ con Google Play Services instalado.

En el caso de iOS, debe tener lo siguiente:

  • Una cuenta de desarrollador de Apple activa.
  • Mac con Xcode, junto con un certificado de desarrollador válido instalado en el Llavero.

A continuación, en iOS debe tener:

  • Un simulador de iOS 16+ que se ejecuta en macOS 13+ en equipos Mac con procesadores Apple Silicon o T2.

    O BIEN

  • Un dispositivo iOS físico registrado en la cuenta de desarrollador (con iOS 13.0 o posterior).

  • El dispositivo físico registrado en la cuenta de desarrollador de Apple y asociado al certificado.

Importante

El simulador de iOS admite notificaciones remotas en iOS 16+ cuando se ejecuta en macOS 13+ en equipos Mac con procesadores Apple Silicon o T2. Si no cumple estos requisitos de hardware, necesitará una cuenta de desarrollador de Apple activa y un dispositivo físico.

Para seguir este tutorial, debe estar familiarizado con:

Aunque este tutorial tiene como destino Visual Studio, es posible seguirlo con Visual Studio Code en un equipo o Mac. Sin embargo, habrá algunas diferencias que necesitan reconciliar. Por ejemplo, descripciones de la interfaz de usuario y los flujos de trabajo, los nombres de plantilla y la configuración del entorno.

Configuración de servicios de notificaciones de inserción y Azure Notification Hub

En esta sección, configurará Firebase Cloud Messaging y Apple Push Notification Services (APNS). Después, creará y configurará una Azure Notification Hub para trabajar con estos servicios.

Creación de un proyecto de Firebase

Para crear un proyecto de Firebase:

  1. En un explorador web, inicie sesión en la Consola Firebase.

  2. En la consola de Firebase, seleccione el botón Agregar proyecto y cree un nuevo proyecto de Firebase y escriba PushDemo como el Nombre de proyecto.

    Nota:

    Se generará un nombre único. De manera predeterminada, se compone de una variante minúscula del nombre que proporcionó más un número generado separado por un guión. Puede cambiar esto si lo desea, siempre que las ediciones sigan siendo únicas globalmente.

  3. Una vez creado el proyecto, seleccione el logotipo de Android para agregar Firebase a una aplicación Android:

    Recorte de pantalla de cómo agregar Firebase a una aplicación Android en la consola de Firebase Cloud Messaging.

  4. En la página Agregar Firebase a la aplicación Android, escriba un nombre para el paquete, opcionalmente un alias de aplicación y seleccione el botón Registrar aplicación:

    Recorte de pantalla del registro de la aplicación Android con Firebase.

  5. En la página Agregar Firebase a la aplicación Android, seleccione el botón Descargar google-services.json y guarde el archivo en una carpeta local antes de seleccionar el botón Siguiente:

    Recorte de pantalla de la descarga del archivo JSON de servicios de Google.

  6. En la página Agregar Firebase a la aplicación Android, seleccione el botón Siguiente.

  7. En la página Agregar Firebase a la aplicación Android, seleccione el botón Continuar con la consola.

  8. En la consola de Firebase, seleccione el icono Información general del proyecto y a continuación, seleccione Configuración del proyecto:

    Recorte de pantalla de la selección de la configuración del proyecto en la consola de Firebase Cloud Messaging.

  9. En Configuración del proyecto, seleccione la pestaña Cloud Messaging. Verá que Firebase Cloud Messaging API (V1) está habilitado:

    Recorte de pantalla que confirma que Firebase Cloud Messaging V1 está habilitado.

  10. En Configuración del proyecto, seleccione la pestaña Cuentas de servicio y a continuación, seleccione el botónGenerar nueva clave privada.

  11. En el cuadro de diálogo Generar nueva clave privada , seleccione el botón Generar clave:

    Recorte de pantalla de la generación de una nueva clave privada en la consola de Firebase Cloud Messaging.

    Se descargará un archivo JSON, que contendrá valores que escribirá en el Centro de notificaciones de Azure.

Registro de una aplicación iOS para notificaciones push

Para enviar notificaciones de inserción a una aplicación de iOS, deberá registrar la aplicación con Apple y registrarse para recibir notificaciones de inserción. Para ello, realice los pasos descritos en la siguiente documentación del Centro de notificaciones de Azure:

Si desea recibir notificaciones de inserción en un dispositivo físico, también deberá Crear un perfil de aprovisionamiento.

Importante

Para recibir notificaciones en segundo plano en iOS, debe agregar el modo en segundo plano de las notificaciones remotas a la aplicación. Para obtener más información, consulte Habilitación de la funcionalidad de notificaciones remotas en developer.apple.com.

Creación de un centro de notificaciones de Azure

Para crear un centro de notificaciones en Azure Portal:

  1. En un explorador web, inicie sesión en Azure Portal.
  2. En Azure Portal, haga clic en el botón Crear un recurso y después busque y elija Centro de notificaciones antes de seleccionar el botón Crear.
  3. En la página Centro de notificaciones, realice los pasos siguientes:
    1. En el campo Suscripción, seleccione el nombre de la suscripción de Azure que desea usar y a continuación, seleccione un grupo de recursos existente o cree uno nuevo.

    2. En el campo Detalles del espacio de nombres, introduzca un nombre único para el nuevo espacio de nombres.

    3. En el campo Detalles del centro de notificaciones, escriba un nombre para el centro de notificaciones. Esto es necesario porque un espacio de nombres contiene uno o varios centros de notificaciones.

    4. En la lista desplegable Ubicación, seleccione un valor que especifique la ubicación en la que desea crear el centro de notificaciones.

    5. Revise la opción Availability Zones. Si elige una región que tiene zonas de disponibilidad, la casilla está activada de manera predeterminada.

      Nota:

      Las zonas de disponibilidad son una característica de pago, por lo que se agrega una tarifa adicional al nivel.

    6. Elija una opciónRecuperación ante desastres: ninguna, región de recuperación emparejada o región de recuperación flexible. Si elige Región de recuperación emparejada, se mostrará la región de conmutación por error. Si selecciona Región de recuperación flexible, use la lista desplegable para elegir una lista de regiones de recuperación.

    7. Seleccione el botón Crear. Se creará el centro de notificaciones.

  4. En Azure Portal, vaya al centro de notificaciones recién creado y a continuación, a la hoja Administrar > Directivas de acceso.
  5. En la hoja Directivas de acceso, anote la cadena de conexión de la directiva de DefaultFullSharedAccessSignature. Lo necesitará más adelante al compilar un servicio back-end que se comunique con el centro de notificaciones.

Para más información acerca de cómo crear un centro de notificaciones, consulte Creación de un centro de notificaciones de Azure en Azure Portal.

Configuración de Firebase Cloud Messaging en el centro de notificaciones

Para configurar el centro de notificaciones para comunicarse con Firebase Cloud Messaging:

  1. En Azure Portal, vaya al centro de notificaciones y seleccione la hoja Configuración> Google (FCM v1)

  2. En la hoja Google (FCM v1), escriba los valores de los Clave privada, Correo electrónico de cliente e Id. de proyecto. Estos valores se pueden encontrar en el archivo JSON de clave privada que descargó de Firebase Cloud Messaging:

    Campo de Azure Clave JSON Ejemplo de valor JSON
    Clave privada private_key Este valor debe comenzar con -----BEGIN PRIVATE KEY-----\n y terminar con -----END PRIVATE KEY-----\n.
    Cliente de correo electrónico client_email firebase-adminsdk-55sfg@pushdemo-d6ab2.iam.gserviceaccount.com
    Id. de proyecto project_id pushdemo-d6ab2
  3. En la hoja Google (FCM v1), seleccione el botón Guardar.

Configuración de Apple Push Notification Service en el centro de notificaciones

En Azure Portal, vaya al centro de notificaciones y seleccione la hojaConfiguración> de Apple (APNS). A continuación, siga los pasos adecuados en función del enfoque que ha elegido anteriormente al crear un certificado para el centro de notificaciones.

Importante

Al establecer el Modo de aplicación, elija solo Producción si quiere enviar notificaciones de inserción a los usuarios que han comprado la aplicación desde la tienda.

Opción 1: Uso de un certificado de inserción .p12

  1. En la hoja Apple (APNS), seleccione el modo de autenticación Certificado.
  2. En la hoja Apple (APNS), seleccione el icono de archivo situado junto al campo Cargar certificado. A continuación, seleccione el archivo .p12 que exportó anteriormente y cárguelo.
  3. En la hoja Apple (APNS), escriba la contraseña del certificado en el campo Contraseña si es necesario.
  4. En la hoja Apple (APNS), seleccione el modo de aplicación Sandbox.
  5. En la hoja Apple (APNS), seleccione el botón Guardar.

Opción 2: Uso de la autenticación basada en tokens

  1. En la hoja Apple (APNS), seleccione el modo de autenticación Token.
  2. En la hoja Apple (APNS), escriba los valores que adquirió anteriormente para los campos Id. de clave, Id. de lote, Id de equipo, y Token.
  3. En la hoja Apple (APNS), seleccione el modo de aplicación Sandbox.
  4. En la hoja Apple (APNS), seleccione el botón Guardar.

Creación de una aplicación de back-end de API Web de ASP.NET Core

En esta sección creará un back-end de API web de ASP.NET Core para controlar la instalación de dispositivos y enviar notificaciones a la aplicación MAUI de .NET.

Creación de un proyecto de API web

Para crear un proyecto de API web:

  1. En Visual Studio, cree un proyecto de ASP.NET Core Web API:

    Recorte de pantalla de la creación de un nuevo proyecto de API web de ASP.NET Core en Visual Studio.

  2. En el cuadro de diálogo Configurar el nuevo proyecto, asigne al proyecto el nombre PushNotificationsAPI.

  3. En el cuadro de diálogo Información adicional asegurarse de que las casillas Configurar para HTTPS y Usar controladores están habilitadas:

    Recorte de pantalla de la configuración del proyecto de API web de ASP.NET Core en Visual Studio.

  4. Una vez creado el proyecto, presione F5 para ejecutar el proyecto.

    La aplicación está configurada actualmente para usar WeatherForecastController como launchUrl, que se establece en el archivo Properties\launchSettings.json. La aplicación se iniciará en un explorador web y mostrará algunos datos JSON.

    Importante

    Al ejecutar un proyecto de ASP.NET Core que usa HTTPS, Visual Studio detectará si el certificado de desarrollo HTTPS de ASP.NET Core está instalado en el almacén de certificados de usuario local y le ofrecerá instalarlo y confiar en él si falta.

  5. Cierre el explorador web.

  6. En Explorador de soluciones, expanda la carpeta Controladores y elimine WeatherForecastController.cs.

  7. En Explorador de soluciones, en la raíz del proyecto, elimine WeatherForecast.cs.

  8. Abra una ventana de comandos y vaya al directorio que contiene el archivo del proyecto. A continuación, ejecute los siguientes comandos:

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

    Reemplace los valores del marcador de posición por sus propios valores de nombre y cadena de conexión del Centro de notificaciones de Azure. Estos se pueden encontrar en las siguientes ubicaciones del Centro de notificaciones de Azure:

    Valor de configuración Location
    NotificationHub:Name Consulte Name en el resumen Essentials en la parte superior de la página de Información general.
    NotificationHub:ConnectinString Consulte DefaultFullSharedAccessSignature* en la página Directivas de acceso.

    Esto configura los valores de configuración local mediante la Herramienta administrador de secretos. Esto desacopla los secretos del Centro de notificaciones de Azure de la solución de Visual Studio para asegurarse de que no terminan en el control de código fuente.

    Sugerencia

    En escenarios de producción, considere la posibilidad de usar un servicio como Azure KeyVault para almacenar de forma segura la cadena de conexión.

Autenticación de clientes con una clave de API

Para autenticar clientes con una clave de API:

  1. Abra una ventana de comandos y vaya al directorio que contiene el archivo del proyecto. A continuación, ejecute los siguientes comandos:

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

    Reemplace el valor del marcador de posición por la clave de API, que puede ser cualquier valor.

  2. En Visual Studio, agregue una nueva carpeta denominada Authentication al proyecto y a continuación, agregue una nueva clase denominada ApiKeyAuthOptions a la carpeta Authentication y reemplace su código por el código siguiente:

    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. En Visual Studio, agregue una nueva clase denominada ApiKeyAuthHandler a la carpeta Authentication y reemplace su código por el código siguiente:

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

    Un controlador de autenticación es un tipo que implementa el comportamiento de un esquema, en este caso un esquema de clave de API personalizado.

  4. En Visual Studio, agregue una nueva clase denominada AuthenticationBuilderExtensions a la carpeta Authentication y reemplace su código por el código siguiente:

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

    Este método de extensión se usará para simplificar el código de configuración de middleware en Program.cs.

  5. En Visual Studio, abra Program.cs y actualice el código para configurar la autenticación de clave de API debajo de la llamada al 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. En Program.cs, actualice el código debajo del comentario // Configure the HTTP request pipeline para llamar a los métodos de extensión UseRouting, UseAuthentication, y MapControllers:

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

    El método de extensión UseAuthentication registra el middleware que usa el esquema de autenticación registrado anteriormente. UseAuthentication se debe llamar a antes de cualquier middleware que dependa de los usuarios que se autentiquen.

    Nota:

    Aunque una clave de API no es tan segura como un token, bastará para este tutorial y se configurará fácilmente a través de Middleware de ASP.NET.

Agregar y configurar servicios

Para agregar y configurar servicios en la aplicación back-end de API web:

  1. En Visual Studio, agregue el paquete NuGet de Microsoft.Azure.NotificationHubs al proyecto. Este paquete NuGet se usa para acceder al centro de notificaciones, encapsulado dentro de un servicio.

  2. En Visual Studio, agregue una nueva carpeta denominada Models al proyecto y a continuación, agregue una nueva clase denominada PushTemplates a la carpeta Models y reemplace su código por el código siguiente:

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

    La clase PushTemplates contiene cargas de notificación tokenizadas para las notificaciones de inserción genéricas y silenciosas. Estas cargas se definen fuera de la instalación para permitir la experimentación sin tener que actualizar las instalaciones existentes a través del servicio. El control de los cambios en las instalaciones de esta manera está fuera del ámbito de este artículo. En escenarios de producto, considere la posibilidad de usar plantillas personalizadas.

  3. En Visual Studio, agregue una nueva clase denominada DeviceInstallation a la carpeta Models y reemplace su código por el código siguiente:

    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. En Visual Studio, agregue una nueva clase denominada NotificationRequest a la carpeta Models y reemplace su código por el código siguiente:

    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. En Visual Studio, agregue una nueva clase denominada NotificationHubOptions a la carpeta Models y reemplace su código por el código siguiente:

    using System.ComponentModel.DataAnnotations;
    
    namespace PushNotificationsAPI.Models;
    
    public class NotificationHubOptions
    {
        [Required]
        public string Name { get; set; }
    
        [Required]
        public string ConnectionString { get; set; }
    }
    
  6. En Visual Studio, agregue una nueva carpeta denominada Services al proyecto y a continuación, agregue una nueva interfaz denominada INotificationService a la carpeta Services y reemplace su código por el código siguiente:

    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. En Visual Studio, agregue una nueva clase denominada NotificationHubService a la carpeta Services y reemplace su código por el código siguiente:

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

    La expresión de etiqueta proporcionada al método SendTemplateNotificationsAsync se limita a 20 etiquetas si solo contienen ORs. De lo contrario, se limitan a 6 etiquetas. Para obtener más información, consulte Expresiones de etiqueta y enrutamiento.

  8. En Visual Studio, abra Program.cs y actualice el código para agregar como NotificationHubService una implementación singleton de debajo de INotificationService la llamada al 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();
    

Creación de la API de REST de notificaciones

Para crear la API de REST de notificaciones:

  1. En Visual Studio, agregue un nuevo controlador denominado NotificationsController a la carpeta Controladores.

    Sugerencia

    Elija la plantilla Controlador de API con acciones de lectura.

  2. En el archivo NotificationsController.cs, agregue las siguientes instrucciones using en la parte superior del archivo:

    using System.ComponentModel.DataAnnotations;
    using System.Net;
    using Microsoft.AspNetCore.Authorization;
    using Microsoft.AspNetCore.Mvc;
    using PushNotificationsAPI.Models;
    using PushNotificationsAPI.Services;
    
  3. En el archivo NotificationsController.cs, agregue el atributo Authorize a la clase NotificationsController:

    [Authorize]
    [ApiController]
    [Route("api/[controller]")]
    public class NotificationsController : ControllerBase
    
  4. En el archivo NotificationsController.cs, actualice el constructor NotificationsContoller para aceptar la instancia registrada INotificationService como argumento y asígnela a un miembro de solo lectura:

    readonly INotificationService _notificationService;
    
    public NotificationsController(INotificationService notificationService)
    {
        _notificationService = notificationService;
    }
    
  5. En el archivo NotificationsContoller.cs, reemplace todos los métodos por el código siguiente:

    [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. En el archivo Properties/launchSettings.json , cambie la propiedad launchUrl para cada perfil de weatherforecast a api/notifications.

Creación de una clave de API

Ahora creará una aplicación de API en Azure App Service para hospedar el servicio back-end. Esto se puede lograr directamente desde Visual Studio o Visual Studio Code, con la CLI de Azure, Azure PowerShell, la CLI para desarrolladores de Azure y a través de Azure Portal. Para obtener más información, consulte Publicar la aplicación web.

Para crear una aplicación de API en Azure Portal:

  1. En un explorador web, inicie sesión en Azure Portal.

  2. En Azure Portal, haga clic en el botón Crear un recurso y a continuación, busque y elija aplicación de API antes de seleccionar el botón Crear.

  3. En la página Crear aplicación de API, actualice los campos siguientes antes de seleccionar el botón Crear:

    Campo Acción
    Subscription Elija la misma suscripción de destino en la que ha creado el centro de notificaciones.
    Grupo de recursos Elija el mismo grupo de recursos en el que creó el centro de notificaciones.
    Nombre Escriba un nombre único global.
    Pila en tiempo de ejecución Asegúrese de que está seleccionada la versión más reciente de .NET.
  4. Una vez aprovisionada la aplicación API , vaya al recurso.

  5. En la página Información general, anote el valor de dominio predeterminado. Esta dirección URL es el punto de conexión de back-end que se consumirá desde la aplicación MAUI de .NET. La dirección URL usará el nombre de la aplicación de API que especificó, con el formato https://<app_name>.azurewebsites.net.

  6. En Azure Portal, vaya a la hoja Configuración > Variables de entorno y asegúrese de que está seleccionada la pestaña Configuración de la aplicación. A continuación, use el botón Agregar para agregar la siguiente configuración:

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

    Importante

    La configuración de la aplicación Authentication:ApiKey se ha agregado por motivos de simplicidad. En escenarios de producción, considere la posibilidad de usar un servicio como Azure KeyVault para almacenar de forma segura la cadena de conexión.

    Una vez que se hayan escrito todas estas configuraciones, seleccione el botón Aplicar y a continuación, el botónConfirmar.

Publicación del servicio back-end

Para publicar el servicio back-end en Azure App Service:

  1. En Visual Studio, haga clic con el botón derecho en el proyecto y seleccione Publicar.
  2. En el Asistente para publicación, seleccione Azure y a continuación, el botón Siguiente.
  3. En el Asistente para publicación, seleccione Azure App Service (Windows) y a continuación, el botón Siguiente.
  4. En el asistente Publicar, siga el flujo de autenticación para conectar Visual Studio a su suscripción de Azure y publicar la aplicación.

Visual Studio compila, empaqueta y publica la aplicación en Azure y a continuación, inicia la aplicación en el explorador predeterminado. Para obtener más información, consulte Publicación de una aplicación web de ASP.NET.

Sugerencia

Puede descargar un perfil de publicación para la aplicación en la hoja Información general de la aplicación de API en Azure Portal y a continuación, usar el perfil en Visual Studio para publicar la aplicación.

Validación de la API publicada

Para comprobar que la aplicación de API se ha publicado correctamente, debe usar las herramientas REST que prefiera para enviar una solicitud de POST a la siguiente dirección:

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

Nota:

La dirección base es https://<app_name>.azurewebsites.net.

Asegúrese de configurar los encabezados de solicitud para incluir la clave apikey y su valor, establecer el cuerpo en sin procesar y usar el siguiente contenido JSON de marcador de posición:

{}

Debe recibir una respuesta 400 Bad Request del servicio.

Nota:

Todavía no es posible probar la API mediante datos de solicitud válidos, ya que esto requerirá información específica de la plataforma de la aplicación MAUI de .NET.

Para obtener más información acerca de cómo llamar a las API de REST, consulte Uso de archivos .http en Visual Studio y Probar API web con Http Repl. En Visual Studio Code, Cliente REST se puede usar para probar las API de REST.

Crear una aplicación .NET MAUI

En esta sección, creará una aplicación de interfaz de usuario de aplicación multiplataforma (.NET MAUI) de .NET que le permite registrarse para recibir notificaciones de inserción desde un centro de notificaciones a través del servicio back-end y anular el registro.

Para crear la aplicación MAUI de .NET:

  1. En Visual Studio, cree una nueva aplicación MAUI de .NET denominada PushNotificationsDemomediante la Plantilla de proyecto Aplicación MAUI de .NET.

  2. En Visual Studio, agregue una nueva carpeta denominada Models al proyecto MAUI de .NET y agregue una nueva clase denominada DeviceInstallation a la carpeta Models y reemplace su código por el código siguiente:

    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. En Visual Studio, agregue una enumeración denominada PushDemoAction a la carpeta Models y reemplace su código por el código siguiente:

    namespace PushNotificationsDemo.Models;
    
    public enum PushDemoAction
    {
        ActionA,
        ActionB
    }
    
  4. En Visual Studio, agregue una nueva carpeta denominada Services al proyecto MAUI de .NET y a continuación, agregue una nueva interfaz denominada IDeviceInstallationService a la carpeta Services y reemplace su código por el código siguiente:

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

    Esta interfaz se implementará en cada plataforma más adelante para proporcionar la información DeviceInstallation requerida por el servicio back-end.

  5. En Visual Studio, agregue una interfaz denominada INotificationRegistrationService a la carpeta Services y reemplace su código por el código siguiente:

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

    Esta interfaz controlará la interacción entre el cliente y el servicio back-end.

  6. En Visual Studio, agregue una interfaz denominada INotificationActionService a la carpeta Services y reemplace su código por el código siguiente:

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

    Esta interfaz se usará como un mecanismo sencillo para centralizar el control de las acciones de notificación.

  7. En Visual Studio, agregue una interfaz denominada IPushDemoNotificationActionService a la carpeta Services y reemplace su código por el código siguiente:

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

    El tipo IPushDemoNotificationActionService es específico de esta aplicación y usa la enumeración PushDemoAction para identificar la acción que se desencadena mediante un enfoque fuertemente tipado.

  8. En Visual Studio, agregue una clase denominada NotificationRegistrationService a la carpeta Services y reemplace su código por el código siguiente:

    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. En Visual Studio, agregue una clase denominada PushDemoNotificationActionService a la carpeta Services y reemplace su código por el código siguiente:

    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. En Visual Studio, agregue una clase denominada Config a la raíz del proyecto y reemplace su código por el código siguiente:

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

    La clase Config se usa como manera sencilla de mantener los secretos fuera del control de código fuente. Puede reemplazar estos valores como parte de una compilación automatizada o invalidarlos mediante una clase parcial local.

    Importante

    Al especificar la dirección base en la aplicación MAUI de .NET, asegúrese de que finaliza con un /.

  11. En Visual Studio, agregue una clase denominada Config.local_secrets a la raíz del proyecto. A continuación, reemplace el código del archivo Config.local_secrets.cs por el código siguiente:

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

    Reemplace los valores de marcador de posición por los valores que eligió al crear el servicio back-end. La dirección URL BackendServiceEndpoint debe usar el formato https://<api_app_name>.azurewebsites.net/.

    Sugerencia

    Recuerde agregar *.local_secrets.* al archivo .gitignore para evitar confirmar este archivo en el control de código fuente.

Creación de la interfaz de usuario

Para crear la interfaz de usuario de la aplicación:

  1. En Visual Studio, abra MainPage.xaml y reemplace VerticalStackLayout y sus elementos secundarios por el código XAML siguiente:

    <VerticalStackLayout Margin="20"
                         Spacing="6">
        <Button x:Name="registerButton"
                Text="Register"
                Clicked="OnRegisterButtonClicked" />
        <Button x:Name="deregisterButton"
                Text="Deregister"
                Clicked="OnDeregisterButtonClicked" />
    </VerticalStackLayout>
    
  2. En Visual Studio, abra MainPage.xaml.cs y agregue una instrucción using para el espacio de nombres PushNotificationsDemo.Services :

    using PushNotificationsDemo.Services;
    
  3. En MainPage.xaml.cs, agregue un campo de respaldo readonly para almacenar una referencia a la implementación de INotificationRegistrationService:

    readonly INotificationRegistrationService _notificationRegistrationService;
    
  4. En el constructor MainPage, resuelva la implementación de INotificationRegistrationService y asígnela al campo de respaldo de _notificationRegistrationService:

    public MainPage(INotificationRegistrationService service)
    {
        InitializeComponent();
    
        _notificationRegistrationService = service;
    }
    
  5. En la clase MainPage, implemente los controladores de eventos OnRegisterButtonClicked y OnDeregisterButtonClicked, llamando a los métodos register y desregister correspondientes en el 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

    En la aplicación, el registro y el des-registro se realizan en respuesta a la entrada del usuario, para permitir que esta funcionalidad se explore y pruebe más fácilmente. En una aplicación de producción, normalmente realizaría las acciones de registro y des registro durante el punto adecuado del ciclo de vida de la aplicación, sin necesidad de especificar explícitamente el usuario.

  6. En Visual Studio, abra App.xaml.cs y agregue las siguientes instrucciones using:

    using PushNotificationsDemo.Models;
    using PushNotificationsDemo.Services;
    
  7. En App.xaml.cs, agregue un campo de respaldo readonly para almacenar una referencia a la implementación IPushDemoNotificationActionService:

    readonly IPushDemoNotificationActionService _actionService;
    
  1. En el constructor App, resuelva la implementación de IPushDemoNotificationActionService y asígnela al campo de respaldo _actionService y suscríbase al evento IPushDemoNotificationActionService.ActionTriggered:

    public App(IPushDemoNotificationActionService service)
    {
        InitializeComponent();
    
        _actionService = service;
        _actionService.ActionTriggered += NotificationActionTriggered;
    
        MainPage = new AppShell();
    }
    
  1. En el constructor App, resuelva la implementación de IPushDemoNotificationActionService y asígnela al campo de respaldo _actionService y suscríbase al evento IPushDemoNotificationActionService.ActionTriggered:

    public App(IPushDemoNotificationActionService service)
    {
        InitializeComponent();
    
        _actionService = service;
        _actionService.ActionTriggered += NotificationActionTriggered;
    }
    
  1. En la clase App, implemente el controlador de eventos para el 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;
                });
        });
    }
    

    El controlador de eventos del evento ActionTriggered muestra la recepción y propagación de acciones de notificación de inserción. Normalmente, estos se controlarían de forma silenciosa, por ejemplo, navegando a una vista específica o actualizando algunos datos en lugar de mostrar una alerta.

Configuración de la aplicación Android

Para configurar la aplicación .NET MAUI en Android para recibir y procesar notificaciones de inserción:

  1. En Visual Studio, agregue el paquete NuGet Xamarin.Firebase.Messaging al proyecto de aplicación MAUI de .NET.

  2. En Visual Studio, agregue el archivo google-services.json a la carpeta Platforms/Android del proyecto de aplicación MAUI de .NET. Una vez agregado el archivo al proyecto, debe haberse agregado con una acción de compilación de GoogleServicesJson:

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

    Sugerencia

    No olvide agregar google-services.json al archivo .gitignore para evitar confirmar este archivo en el control de código fuente.

  3. En Visual Studio, edite el archivo de proyecto (*.csproj) y establezca SupportedOSPlatformVersion para Android en 26.0:

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

    Google realizó cambios en los canales de notificación de Android en la API 26. Para obtener más información, consulte Canales de notificación en developer.android.com.

  4. En la carpeta Platforms/Android del proyecto, agregue una nueva clase denominada DeviceInstallationService y reemplace su código por el código siguiente:

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

    Esta clase proporciona un identificador único, mediante el valor Secure.AndroidId y la carga de registro del centro de notificaciones.

  5. En la carpeta Platforms/Android del proyecto, agregue una nueva clase denominada PushNotificationFirebaseMessagingService y reemplace su código por el código siguiente:

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

    Esta clase tiene un atributo IntentFilter que incluye el filtro com.google.firebase.MESSAGING_EVENT. Este filtro permite a Android pasar mensajes entrantes a esta clase para su procesamiento.

    Para obtener información acerca del formato de mensaje Firebase Cloud Messaging, consulte Acerca de los mensajes de FCM en developer.android.com.

  6. En Visual Studio, abra el archivo MainActivity.cs en la carpeta Platforms/Android y agregue las siguientes instrucciones using:

    using Android.App;
    using Android.Content;
    using Android.Content.PM;
    using Android.OS;
    using PushNotificationsDemo.Services;
    using Firebase.Messaging;
    
  7. En la clase MainActivity, establezca el LaunchMode en SingleTop para que el MainActivity no se vuelva a crear cuando se abra:

    [Activity(
        Theme = "@style/Maui.SplashTheme",
        MainLauncher = true,
        LaunchMode = LaunchMode.SingleTop,
        ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize | ConfigChanges.Density)]
    
  8. En la clase MainActivity, agregue campos de respaldo para almacenar referencias a las implementaciones de IPushDemoNotificationActionService y IDeviceInstallationService :

    IPushDemoNotificationActionService _notificationActionService;
    IDeviceInstallationService _deviceInstallationService;
    
  9. En la clase MainActivity, agregue NotificationActionService y DeviceInstallationService propiedades privadas que recuperen sus implementaciones concretas del contenedor de inserción de dependencias de la aplicación:

    IPushDemoNotificationActionService NotificationActionService =>
        _notificationActionService ?? (_notificationActionService = IPlatformApplication.Current.Services.GetService<IPushDemoNotificationActionService>());
    
    IDeviceInstallationService DeviceInstallationService =>
        _deviceInstallationService ?? (_deviceInstallationService = IPlatformApplication.Current.Services.GetService<IDeviceInstallationService>());
    
  10. En la clase MainActivity, implemente la interfaz Android.Gms.Tasks.IOnSuccessListener para recuperar y almacenar el token de Firebase:

    public class MainActivity : MauiAppCompatActivity, Android.Gms.Tasks.IOnSuccessListener
    {
        public void OnSuccess(Java.Lang.Object result)
        {
            DeviceInstallationService.Token = result.ToString();
        }
    }
    
  11. En la clase MainActivity, agregue el método ProcessNotificationActions que comprobará si un Intent determinado tiene un valor adicional denominado action, y a continuación, desencadene condicionalmente ese action mediante la implementación 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. En la clase MainActivity, invalide el método OnNewIntent para llamar al método ProcessNotificationActions:

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

    Dado que el LaunchMode de la Activity se establece en SingleTop, se enviará una Intent a la instancia de Activity existente a través de la invalidación de OnNewIntent, en lugar del método OnCreate. Por lo tanto, debe controlar una intención entrante en OnNewIntent y OnCreate.

  13. En la clase MainActivity, invalide el método OnCreate para llamar al método ProcessNotificationActions y para recuperar el token de Firebase, agregando MainActivity como IOnSuccessListener:

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

    Nota:

    La aplicación debe volver a registrarse cada vez que la ejecute y detenerla de una sesión de depuración para seguir recibiendo notificaciones de inserción.

  14. En Visual Studio, agregue el permiso POST_NOTIFICATIONS al archivo AndroidManifest.xml en la carpeta Platforms/Android:

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

    Para obtener más información acerca del permiso, consulte Permiso de tiempo de ejecución de notificaciones en developer.android.com.

  15. En Visual Studio, abra MainPage.xaml.cs y agregue el código siguiente a la clase MainPage:

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

    Este código se ejecuta en Android cuando aparece MainPage, y solicita al usuario que conceda el permiso POST_NOTIFICATIONS. Para obtener más información sobre los permisos .NET MAUI, consulte Permisos.

Configuración de la aplicación iOS

El simulador de iOS admite notificaciones remotas en iOS 16+ cuando se ejecuta en macOS 13+ en equipos Mac con procesadores Apple Silicon o T2. Cada simulador genera tokens de registro que son únicos para la combinación de ese simulador y el hardware Mac en el que se ejecuta.

Importante

El simulador admite el entorno de espacio aislado de Apple Push Notification Service.

En las instrucciones siguientes se supone que usa hardware que admite la recepción de notificaciones remotas en un simulador de iOS. Si no es así, tendrá que ejecutar la aplicación de iOS en un dispositivo físico, lo que requerirá que cree un perfil de aprovisionamiento para la aplicación que incluya la funcionalidad notificaciones de inserción. A continuación, deberá asegurarse de que la aplicación se compila mediante el certificado y el perfil de aprovisionamiento. Para obtener más información acerca de cómo hacerlo, consulte Configuración de la aplicación iOS para que funcione con Azure Notification Hubs, y después siga las instrucciones que se indican a continuación.

Para configurar la aplicación .NET MAUI en iOS para recibir y procesar notificaciones de inserción:

  1. En Visual Studio, edite el archivo de proyecto (*.csproj) y establezca SupportedOSPlatformVersion para iOS en 13.0:

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

    Apple realizó cambios en su servicio de inserción en iOS 13. Para más información, consulte Actualizaciones de Azure Notification Hubs para iOS 13.

  2. En Visual Studio, agregue un archivo Entitlements.plist a la carpeta Platforms/iOS del proyecto y agregue el siguiente XML al archivo:

    <?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>
    

    Esto establece el derecho de entorno APS y especifica el uso del entorno de desarrollo del servicio Apple Push Notification. En las aplicaciones de producción, este valor de derecho debe establecerse en production. Para obtener más información acerca de este derecho, consulte APS Derecho de entorno en developer.apple.com.

    Para obtener más información acerca de cómo agregar un archivo de derechos, consulte Derechos de iOS.

  3. En Visual Studio, agregue una nueva clase denominada DeviceInstallationService a la carpeta Platforms/iOS del proyecto y agregue el código siguiente al archivo:

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

    Esta clase proporciona un identificador único, mediante el valor UIDevice.IdentifierForVendor y la carga de registro del centro de notificaciones.

  4. En Visual Studio, agregue una nueva clase denominada NSDataExtensions a la carpeta Platforms/iOS del proyecto y agregue el código siguiente al archivo:

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

    El código usará el método de extensión ToHexString que agregará que analiza el token de dispositivo recuperado.

  5. En Visual Studio, abra el archivo AppDelegate.csen la carpeta Platforms/iOS y agregue las siguientes instrucciones using:

    using System.Diagnostics;
    using Foundation;
    using PushNotificationsDemo.Platforms.iOS;
    using PushNotificationsDemo.Services;
    using UIKit;
    using UserNotifications;
    
  6. En la clase AppDelegate, agregue campos de respaldo para almacenar referencias a las implementaciones de IPushDemoNotificationActionService, INotificationRegistrationService, y IDeviceInstallationService:

    IPushDemoNotificationActionService _notificationActionService;
    INotificationRegistrationService _notificationRegistrationService;
    IDeviceInstallationService _deviceInstallationService;
    
  7. En la clase AppDelegate, agregue NotificationActionService, NotificationRegistrationService, y DeviceInstallationService propiedades privadas que recuperen sus implementaciones concretas del contenedor de inserción de dependencias de la aplicación:

    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. En la clase AppDelegate, agregue el método CompleteRegistrationAsync para establecer el valor de la propiedad IDeviceInstallationService.Token:

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

    Este método también actualiza el registro y almacena en caché el token de dispositivo si se ha actualizado desde que se almacenó por última vez.

  9. En la clase AppDelegate, agregue el método ProcessNotificationActions para procesar los datos de notificación de NSDictionary y llamar condicionalmente a 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. En la clase AppDelegate, agregue el método RegisteredForRemoteNotifications pasando el argumento deviceToken al método CompleteRegistrationAsync:

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

    Se llamará a este método cuando se registre la aplicación para recibir una notificación remota y se use para solicitar el token de dispositivo único, que es efectivamente la dirección de la aplicación en el dispositivo.

  11. En la clase AppDelegate, agregue el método ReceivedRemoteNotification pasando el argumento userInfo al método ProcessNotificationActions:

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

    Se llamará a este método cuando la aplicación haya recibido una notificación remota y se use para procesar la notificación.

  12. En la clase AppDelegate, agregue el método FailedToRegisterForRemoteNotifications para registrar los errores:

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

    Se llamará a este método cuando la aplicación no se haya registrado para recibir notificaciones remotas. Es posible que se produzca un error en el registro si el dispositivo no está conectado a la red, si el servidor APNS no es accesible o si la aplicación está configurada incorrectamente.

    Nota:

    En escenarios de producción, querrá implementar el registro y el control de errores adecuados en el método FailedToRegisterForRemoteNotifications.

  13. En la clase AppDelegate, agregue el método FinishedLaunching para solicitar condicionalmente permiso para usar notificaciones y registrarse para las notificaciones 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 obtener información acerca de cómo solicitar permiso para usar notificaciones, consulte Solicitar permiso para usar notificaciones en developer.apple.com.

Para obtener información acerca de las notificaciones en iOS, consulte Notificaciones de usuario en developer.apple.com.

Registro de tipos con el contenedor de inserción de dependencias de la aplicación

  1. En Visual Studio, abra MauiProgram.cs y agregue una instrucción using para el espacio de nombres PushNotificationsDemo.Services:

    using PushNotificationsDemo.Services;
    
  2. En la clase MauiProgram, agregue código para el método de extensión RegisterServices que registra el DeviceInstallationService en cada plataforma y los servicios de PushDemoNotificationActionService y NotificationRegistrationService multiplataforma, y que devuelve un 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. En la clase MauiProgram, agregue código para el método de extensión RegisterViews que registra el tipo MainPage como singleton y que devuelve un objeto MauiAppBuilder :

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

    El tipo MainPage se registra porque requiere una dependencia INotificationRegistrationService, y todos los tipos que requieran una dependencia deben registrarse con el contenedor de inserción de dependencias.

  4. En la clase MauiProgram, modifique el método CreateMauiApp para que llame a los métodos de extensión RegisterServices y 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 obtener más información sobre la inserción de dependencias en .NET MAUI, vea Inserción de dependencias.

Pruebas de la aplicación

Puede probar la aplicación mediante el envío de notificaciones de inserción a la aplicación mediante el servicio back-end o a través de Azure Portal.

El simulador de iOS admite notificaciones remotas en iOS 16+ cuando se ejecuta en macOS 13+ en equipos Mac con procesadores Apple Silicon o T2. Si no cumple estos requisitos de hardware, tendrá que probar la aplicación de iOS en un dispositivo físico. En Android, puede probar la aplicación en un dispositivo físico desbloqueado por el desarrollador o en un emulador.

Android e iOS muestran notificaciones de inserción en nombre de la aplicación cuando se ejecuta en segundo plano. Si la aplicación se ejecuta en primer plano cuando se recibe la notificación, el código de la aplicación determina el comportamiento. Por ejemplo, puede actualizar la interfaz de la aplicación para reflejar la nueva información contenida en la notificación.

Prueba mediante el servicio back-end

Para enviar una notificación de inserción de prueba a la aplicación a través del servicio back-end publicado en Azure App Service:

  1. En Visual Studio, ejecute la aplicación PushNotificationsDemo en Android o iOS y seleccione el botón Registrar.

    Nota:

    Si está probando en Android, asegúrese de que no se está ejecutando con la configuración de depuración. Como alternativa, si la aplicación se ha implementado anteriormente, asegúrese de que se ha cerrado la fuerza y a continuación, vuelva a iniciarla desde el iniciador.

  2. En las herramientas REST que prefiera, envíe una POST solicitud a la siguiente dirección:

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

    Asegúrese de configurar los encabezados de solicitud para incluir la clave apikey y su valor, establecer el cuerpo en sin procesar y usar el siguiente contenido JSON:

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

    La solicitud general debe ser similar al ejemplo siguiente:

    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. En las herramientas REST que prefiera, valide que recibe una respuesta 200 OK.

  4. En la aplicación en Android o iOS, debería aparecer una alerta que muestra la Acción ActionA recibida.

Para obtener más información acerca de cómo llamar a las API de REST, consulte Uso de archivos .http en Visual Studio y Probar API web con Http Repl. En Visual Studio Code, Cliente REST se puede usar para probar las API de REST.

Prueba mediante Azure Portal

Azure Notification Hubs le permite comprobar que la aplicación puede recibir notificaciones de inserción.

Para enviar una notificación de inserción de prueba a la aplicación a través de Azure Portal:

  1. En Visual Studio, ejecute la aplicación PushNotificationsDemo en Android o iOS y seleccione el botón Registrar.

    Nota:

    Si está probando en Android, asegúrese de que no se está ejecutando con la configuración de depuración. Como alternativa, si la aplicación se ha implementado anteriormente, asegúrese de que se ha cerrado la fuerza y a continuación, vuelva a iniciarla desde el iniciador.

  2. En Azure Portal, vaya al centro de notificaciones y seleccione el botón Enviar prueba en la hoja Información general.

  3. En la hoja Enviar prueba, seleccione la Plataforma necesaria y modifique la carga.

    Para Apple, use la siguiente carga:

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

    Para Android, use la siguiente carga:

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

    Azure Portal debe indicar que la notificación se ha enviado correctamente.

    Para obtener información acerca del formato de mensaje Firebase Cloud Messaging, consulte Acerca de los mensajes de FCM en developer.android.com.

  4. En la aplicación en Android o iOS, debería aparecer una alerta que muestra la Acción ActionA recibida.

Solución de problemas

En las secciones siguientes se describen los problemas comunes detectados al intentar consumir notificaciones de inserción en una aplicación cliente.

No hay respuesta del servicio de back-end

Cuando realice pruebas localmente, asegúrese de que el servicio de back-end se esté ejecutando y de que esté usando el puerto correcto.

Si realiza pruebas en la aplicación de API de Azure, compruebe que el servicio se está ejecutando y que se ha implementado y se ha iniciado sin errores.

Asegúrese de que ha especificado correctamente la dirección base en las herramientas REST o en la configuración de la aplicación MAUI de .NET. La dirección base debe ser https://<api_name>.azurewebsites.net o https://localhost:7020 al probar localmente.

Se recibe un código de estado 401 del servicio de back-end

Compruebe que está configurando correctamente el encabezado de la solicitud apikey y que este valor coincide con el que configuró para el servicio back-end.

Si recibe este error al probar localmente, asegúrese de que el valor de clave que definió en la aplicación MAUI de .NET coincide con el Authentication:ApiKey valor de secretos de usuario usado por el servicio back-end.

Si está probando con una aplicación de API de Azure, asegúrese de que el valor de clave definido en la aplicación MAUI de .NET coincide con el valor de configuración de la aplicación Authentication:ApiKey definido en Azure Portal. Si ha creado o cambiado esta configuración de aplicación después de haber implementado el servicio back-end, debe reiniciar el servicio para que el valor surta efecto.

Se recibe un código de estado 404 del servicio de back-end

Compruebe que el punto de conexión y el método de solicitud HTTP son correctos:

  • 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

O cuando se realizan las pruebas de manera local:

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

Importante

Al especificar la dirección base en la aplicación MAUI de .NET, asegúrese de que finaliza con un /. La dirección base debe ser https://<api_name>.azurewebsites.net o https://localhost:7020/ al probar localmente.

No se reciben notificaciones en Android después de iniciar o detener una sesión de depuración

Asegúrese de registrar cada vez que inicie una sesión de depuración. El depurador hará que se genere un nuevo token de Firebase y por tanto, se debe actualizar la instalación del centro de notificaciones.

No se puede realizar el registro y aparece un mensaje de error del centro de notificaciones

Compruebe que el dispositivo de prueba tenga conectividad de red. A continuación, determine el código de estado de respuesta HTTP estableciendo un punto de interrupción para inspeccionar la propiedad StatusCode en HttpResponse.

Revise las sugerencias de solución de problemas anteriores, si procede, en función del código de estado.

Establezca un punto de interrupción en las líneas que devuelven códigos de estado específicos para la API correspondiente. Después, intente llamar al servicio de back-end al depurar localmente.

Valide que el servicio back-end funciona según lo previsto por las herramientas REST que prefiera y use la carga creada por la aplicación MAUI de .NET para la plataforma elegida.

Revise las secciones de configuración específicas de la plataforma para asegurarse de que no se ha omitido ningún paso. Compruebe que los valores adecuados se resuelven para las variables InstallationId y Token para la plataforma elegida.

No se puede resolver un identificador para el dispositivo, el mensaje de error del dispositivo

Revise las secciones de configuración específicas de la plataforma para asegurarse de que no se ha omitido ningún paso.