Tutorial: Envío de notificaciones push a aplicaciones Xamarin.Forms con Azure Notification Hubs mediante un servicio back-end

Ejemplo de descarga Descarga del ejemplo

En este tutorial, usará Azure Notification Hubs para enviar notificaciones push a una aplicación Xamarin.Forms con destino Android e iOS.

Se usa un backend de API web de ASP.NET Core para controlar el registro del dispositivo del cliente mediante el enfoque de instalación más reciente y más adecuado. El servicio también enviará notificaciones push en modo multiplataforma.

Estas operaciones se administran mediante el SDK de Notification Hubs para operaciones de back-end. Se proporcionan más detalles sobre el enfoque global en la documentación Administración de registros desde un back-end.

Este tutorial le guía por los siguientes pasos:

Requisitos previos

Para continuar, necesita lo siguiente:

  • Una suscripción a Azure donde pueda crear y administrar los recursos.
  • Un equipo Mac con Visual Studio para Mac instalado o un equipo que ejecute Visual Studio 2019.
  • Los usuarios de Visual Studio 2019 también deben tener instaladas las cargas de trabajo Desarrollo para dispositivos móviles con .NET y Desarrollo de ASP.NET y web.
  • La capacidad de ejecutar la aplicación en Android (dispositivos físicos o emuladores) o iOS (solo en dispositivos físicos).

En el caso de Android, debe tener lo siguiente:

  • Un dispositivo físico desbloqueado para desarrolladores o un emulador (que ejecute la API 26 y versiones posteriores con Google Play Services instalado) .

En el caso de iOS, debe tener lo siguiente:

Nota

El simulador de iOS no admite notificaciones remotas y, por tanto, se necesita un dispositivo físico para aplicar este ejemplo en iOS. Sin embargo, no es necesario ejecutar la aplicación tanto en Android como en iOS para completar este tutorial.

Puede seguir los pasos de este ejemplo de primeros principios sin experiencia previa. No obstante, resulta beneficioso estar familiarizado con los aspectos siguientes.

Importante

Los pasos proporcionados son específicos de Visual Studio para Mac. Es posible continuar con el uso de Visual Studio 2019, pero puede haber algunas diferencias que conciliar. Por ejemplo, las descripciones de la interfaz de usuario y los flujos de trabajo, los nombres de plantilla, la configuración del entorno, etc.

Configuración de servicios de notificación push y Azure Notification Hubs

En esta sección, configurará Firebase Cloud Messaging (FCM) y Apple Push Notification Service (APNs) . Después, creará y configurará un centro de notificaciones para que funcione con esos servicios.

Crear un proyecto de Firebase y habilitar Firebase Cloud Messaging para Android

  1. Inicie sesión en la consola Firebase. Cree un nuevo proyecto de Firebase escribiendo PushDemo como nombre del proyecto.

    Nota

    Se generará un nombre único. De forma predeterminada, se compone de una variante en minúsculas del nombre que proporcionó más un número generado separado mediante un guión. Puede cambiarlo si lo desea siempre que sea globalmente único.

  2. Después de crear el proyecto, seleccione Add Firebase to your Android app (Añade Firebase a tu aplicación de Android).

    Agregar Firebase a una aplicación de Android

  3. En la página Add Firebase to your Android app (Agregar Firebase a la aplicación Android), haga lo siguiente.

    1. En Android package name (Nombre del paquete Android), escriba un nombre para el paquete. Por ejemplo: com.<organization_identifier>.<package_name>.

      Especifique el nombre del paquete.

    2. Seleccione Registrar aplicación.

    3. Seleccione Download google-services.json (Descargar google-services.json). A continuación, guarde el archivo en una carpeta local para un uso posterior y seleccione Siguiente.

      Descargue el archivo google-services.json.

    4. Seleccione Next (Siguiente).

    5. Seleccione Continue to console(Continuar en la consola).

      Nota

      Si el botón Continue to console (Continuar en la consola) no está habilitado debido a la comprobación de la instalación, elija Omitir este paso.

  4. En la consola Firebase, seleccione el icono de la rueda dentada del proyecto. Luego, seleccione Project Settings (Configuración del proyecto).

    Seleccionar Project Settings (Configuración del proyecto)

    Nota

    Si no ha descargado el archivo google-services.json, puede hacerlo en esta página.

  5. Cambie a la pestaña Cloud Messaging de la parte superior. Copie y guarde la clave del servidor para su uso posterior. Este valor se utilizará para configurar el centro de notificaciones.

    Copia de la clave del servidor

Registro de una aplicación iOS para notificaciones push

Para enviar notificaciones push a una aplicación iOS, registre la aplicación en Apple y también regístrese para el uso de notificaciones push.

  1. Si todavía no ha registrado la aplicación, vaya al portal de aprovisionamiento de iOS en el centro para desarrolladores de Apple. Inicie sesión en el portal con el identificador de Apple, vaya a Certificados, Perfiles de identificadores y seleccione Identificadores. Haga clic en + para registrar una nueva aplicación.

    Página de identificadores de aplicaciones del portal de aprovisionamiento de iOS

  2. En la pantalla Register a New Identifier (Registrar un nuevo identificador), seleccione el botón de radio App IDs (Id. de aplicaciones). Después, seleccione Continuar.

    Portal de aprovisionamiento de iOS: página de registro de un nuevo identificador

  3. Actualice los tres campos siguientes de la nueva aplicación y, después, seleccione Continue (Continuar):

    • Descripción: escriba un nombre descriptivo para la aplicación.

    • Id. delote: escriba un identificador de lote con el formato com. organization_identifier . < product_name > como se mencionó en la <. En la captura de pantalla siguiente, el valor se usa como identificador de mobcat organización y el valor mobcat como nombre del producto.

      Portal de aprovisionamiento de iOS: página de registro del identificador de aplicación

    • Notificaciones push: marque la opción Push Notifications (Notificaciones push) en la sección Capabilities (Capacidades).

      Formulario para registrar un nuevo identificador de aplicación

      De esta forma, se genera el identificador de la aplicación y se solicita que confirme la información. Seleccione Continue (Continuar) y, luego, Register (Registrar) para confirmar el nuevo identificador de aplicación.

      Confirmar el nuevo identificador de aplicación

      Después de seleccionar Registrar,verá el nuevo identificador de aplicación como un elemento de línea en la página Certificados, Perfiles de identificadores.

  4. En la página Certificados, Perfiles de identificadores, en Identificadores, busque el elemento de línea Id. de aplicación que ha creado. A continuación, seleccione su fila para mostrar la pantalla Edit your App ID Configuration (Editar la configuración del identificador de aplicación).

Creación de un certificado para Notification Hubs

Para permitir que el centro de notificaciones funcione con Apple Push Notification Service (APNs) es necesario un certificado que se puede proporcionar de una de estas dos maneras:

  1. Creación de un certificado push p12 que se pueda cargar directamente en Notification Hubs (el enfoque original)

  2. Creación de un certificado p8 que se puede usar para la autenticación basada en token (el enfoque más reciente y recomendado)

El enfoque más reciente tiene varias ventajas, como se documenta en Autenticación basada en token (HTTP/2) para APNs. Requiere menos pasos, pero además se exige para ciertos escenarios concretos. De todas formas, se han proporcionado pasos para ambos enfoques, ya que ambos funcionan para los fines de este tutorial.

OPCIÓN 1: Creación de un certificado push p12 que se pueda cargar directamente en Notification Hubs.
  1. En el Mac, ejecute la herramienta Acceso a llaves. Se puede activar desde la carpeta Utilities (Utilidades) o la carpeta Other (Otros) en el panel de inicio.

  2. Seleccione Keychain Access (Acceso con cadena de claves), expanda Certificate Assistant (Asistente para certificados) y, después, seleccione Request a Certificate from a Certificate Authority (Solicitar un certificado a una entidad de certificación).

    Use Acceso a llaves para solicitar un nuevo certificado

    Nota

    De forma predeterminada, Keychain Access (Acceso con cadena de claves) selecciona el primer elemento de la lista. Esto puede ser un problema si está en la categoría Certificates (Certificados) y Apple Worldwide Developer Relations Certification Authority (Entidad de certificación de Apple Worldwide Developer Relations) no es el primer elemento de la lista. Antes de generar la solicitud de firma de certificado, asegúrese de que tiene un elemento que no sea clave o de que esté seleccionada la clave Apple Worldwide Developer Relations Certification Authority (Entidad de certificación de Apple Worldwide Developer Relations).

  3. Seleccione una dirección de correo electrónico de usuario, escriba un valor en Common Name (Nombre común), asegúrese de que especifica Saved to disk (Guardar en disco) y, finalmente, seleccione Continue (Continuar). Deje en blanco CA Email Address (Dirección de correo de la entidad de certificación), ya que no es obligatorio.

    Información esperada del certificado

  4. Escriba un nombre para el archivo de solicitud de firma de certificado (CSR) en Save As (Guardar como), seleccione la ubicación en Where (Dónde) y, a continuación, haga clic en Save (Guardar).

    Elección de un nombre de archivo para el certificado

    Esta acción guarda el archivo CSR en la ubicación seleccionada. La ubicación predeterminada es Escritorio. Recuerde la ubicación seleccionada para este archivo.

  5. De nuevo en la página Certificados, Perfiles de identificadores del Portal de aprovisionamiento de iOS,desplácese hacia abajo hasta la opción Notificaciones push activada y, a continuación, seleccione Configurar para crear el certificado.

    Editar página de identificadores de aplicaciones

  6. Aparece la ventana Apple Push Notification service TLS/SSL Certificates (Certificados TLS/SSL de Apple Push Notification Service). Seleccione el botón Create Certificate (Crear certificado) en la sección Development TLS/SSL Certificate (Certificado TLS/SSL de desarrollo).

    Crear certificado para el botón de identificador de aplicación

    Se muestra la pantalla Create a new Certificate (Crear un nuevo certificado).

    Nota

    Este tutorial usa un certificado de desarrollo. Se usa el mismo proceso cuando se registra un certificado de producción. Asegúrese de usa el mismo tipo de certificado cuando envíe notificaciones.

  7. Seleccione Choose File (Elegir archivo), vaya a la ubicación donde guardó el archivo CSR y haga doble clic en el nombre del certificado para cargarlo. Después, seleccione Continuar.

  8. Una vez que el portal cree el certificado, seleccione el botón Download (Descargar). Guarde el certificado y recuerde la ubicación donde se ha guardado.

    Página de descarga de certificados generados

    El certificado se descarga y se guarda en el equipo en la carpeta de descargas.

    Busque el archivo de certificado en la carpeta de descargas

    Nota

    De manera predeterminada, el certificado de desarrollo descargado se denomina aps_development.cer.

  9. Haga doble clic en el certificado de inserción aps_development.cer descargado. Esta operación instala el certificado nuevo en la cadena de llaves, tal como se muestra en la siguiente imagen:

    Lista de certificados de Acceso a llaves en la que aparece el nuevo certificado

    Nota

    Aunque el nombre del certificado puede ser diferente, llevará el prefijo Apple Development iOS Push Services (Servicios push de desarrollo de Apple iOS) y tendrá el identificador del lote asociado.

  10. En Acceso a la cadena de claves, control Hagaclic en el nuevo certificado de inserción que creó en la categoría Certificados. Seleccione Export (Exportar), asigne un nombre al archivo, seleccione el formato p12 y, después, haga clic en Save (Guardar).

    Exportación del certificado en formato p12

    Puede proteger el certificado con una contraseña, pero el uso de contraseña es opcional. Haga clic en OK (Aceptar) si quiere omitir la creación de una contraseña. Anote el nombre de archivo y la ubicación del certificado p12 exportado. Estos datos se usan para habilitar la autenticación con APNs.

    Nota

    El nombre y la ubicación del archivo p12 pueden ser diferentes a los que se ilustran en este tutorial.

OPCIÓN 2: Creación de un certificado p8 que se pueda usar para autenticación basada en tokens.
  1. Anote los siguientes datos:

    • App ID Prefix (Prefijo de identificador de aplicación), se trata de un identificador de equipo
    • Identificador de lote
  2. De nuevo en Certificados, Perfiles de identificadores, haga clic en Claves.

    Nota

    Si ya tiene configurada una clave para APNs, puede volver a usar el certificado p8 que descargó justo después de crearlo. En tal caso, puede omitir los pasos 3 a 5.

  3. Haga clic en el botón + [o en el botón + (Crear una clave)] para crear una nueva clave.

  4. Proporcione un valor de Key Name (Nombre de clave) adecuado, active la opción Apple Push Notifications service (APNs) y, a continuación, haga clic en Continue (Continuar), seguido de Register (Registrar) en la pantalla siguiente.

  5. Haga clic en Download (Descargar) y, a continuación, mueva el archivo p8 (con el prefijo AuthKey_ ) a un directorio local seguro y, a continuación, haga clic en Done (Listo).

    Nota

    Asegúrese de mantener el archivo p8 en un lugar seguro (y guardar una copia de seguridad). Después de descargar la clave, no se puede volver a descargar, ya que se quita la copia del servidor.

  6. En Keys (Claves), haga clic en la clave que ha creado (o en una clave existente si decide utilizarla en su lugar).

  7. Anote el valor de Key ID (Id. de clave).

  8. Abra el certificado p8 en una aplicación adecuada, como Visual Studio Code. Apunte el valor clave (el que aparece entre -----BEGIN PRIVATE KEY----- y -----END PRIVATE KEY----- ).

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

    Nota

    Este es el valor de token que se usará más adelante para configurar el centro de notificaciones.

Al final de estos pasos debe tener la siguiente información para usarla más adelante en Configuración de un centro de notificaciones con información de APNs:

  • Identificador de equipo (consulte el paso 1)
  • Identificador de lote (consulte el paso 1)
  • Identificador de clave (consulte el paso 7)
  • Valor de token (el valor de clave de p8 obtenido en el paso 8)

Creación de un perfil de aprovisionamiento para la aplicación

  1. Vuelva al Portal de aprovisionamiento de iOS,seleccione Certificados, Perfiles de identificadores, seleccione Perfiles en el menú de la izquierda y, a continuación, seleccione para crear un nuevo perfil. Aparece la pantalla Register a New Provisioning Profile (Registrar un nuevo perfil de aprovisionamiento).

  2. En Development (Desarrollo), seleccione iOS App Development (Desarrollo de aplicaciones de iOS) como tipo de perfil de aprovisionamiento y, después, haga clic en Continue (Continuar).

    Lista de perfiles de aprovisionamiento

  3. Una vez hecho esto, seleccione el identificador de la aplicación que acaba de crear en la lista desplegable App ID (Id. de la aplicación) y, a continuación, seleccione Continuar.

    Seleccionar el identificador de la aplicación

  4. En la ventana Select certificates (Seleccionar certificados), seleccione el certificado de desarrollo que se usa para la firma del código y, después, seleccione Continue (Continuar).

    Nota

    Este certificado no es el certificado push que creó en el paso anterior. Este es el certificado de desarrollo. Si no existe uno, tiene que crearlo, ya que se trata de un requisito previo para este tutorial. Los certificados de desarrollador se pueden crear en el portal para desarrolladores de Apple, a través de Xcode o en Visual Studio.

  5. Vuelva a la página Certificados, Perfiles de identificadores, seleccione Perfiles en el menú de la izquierda y, a continuación, seleccione para crear un perfil. Aparece la pantalla Register a New Provisioning Profile (Registrar un nuevo perfil de aprovisionamiento).

  6. En la ventana Select certificates (Seleccionar certificados), seleccione el certificado de desarrollo que acaba de crear. Después, seleccione Continuar.

  7. Después, seleccione en los dispositivos que va a usar para la prueba y seleccione Continue (Continuar).

  8. Para terminar, elija un nombre para el perfil en Provisioning Profile Name (Nombre del perfil de aprovisionamiento) y seleccione Generate (Generar).

    Elegir el nombre del perfil de aprovisionamiento

  9. Cuando se cree el perfil de aprovisionamiento, seleccione Download (Descargar). Recuerde la ubicación donde se ha guardado.

  10. Vaya a la ubicación del perfil de aprovisionamiento y haga doble clic en él para instalarlo en la máquina de desarrollo.

Creación de un centro de notificaciones

En esta sección, creará un centro de notificaciones y configurará la autenticación con APNS. Puede usar un certificado push P12 o una autenticación basada en token. Si desea usar un centro de notificaciones ya creado, puede omitir los pasos hasta el paso 5.

  1. Inicie sesión en Azure.

  2. Haga clic en Crear un recurso, busque y elija Centro de notificaciones y, a continuación, haga clic en Crear.

  3. Actualice los campos siguientes y haga clic en Crear:

    DETALLES BÁSICOS

    Subscription (Suscripción): Elija la suscripción de destino en la lista desplegable.
    Grupos de recursos: Cree un grupo de recursos o escoja uno ya existente.

    DETALLES DEL ESPACIO DE NOMBRES

    Espacio de nombres de Notification Hubs: escriba un nombre único global para el espacio de nombres de Notification Hubs.

    Nota

    Asegúrese de que la opción Crear nuevo está seleccionada para este campo.

    DETALLES DE NOTIFICATION HUBS

    Notification Hubs: especifique un nombre para el centro de notificaciones.
    Ubicación: elija una ubicación adecuada en la lista desplegable.
    Plan de tarifa: mantenga la opción predeterminada Gratis,

    Nota

    a menos que haya alcanzado el número máximo de centros del nivel Gratis.

  4. Una vez que se haya aprovisionado el centro de notificaciones, vaya a ese recurso.

  5. Vaya al nuevo centro de notificaciones.

  6. Seleccione Directivas de acceso en la lista (en ADMINISTRAR.

  7. Anote los valores de nombre de directiva junto con sus valores correspondientes de cadena de conexión.

Configuración del centro de notificaciones con información de APNS

En Servicios de notificaciones, seleccione Apple y siga los pasos adecuados en función del enfoque elegido anteriormente en la sección Creación de un certificado para Notification Hubs.

Nota

Use Production (Producción) como Application Mode (Modo de aplicación) solo si desea enviar notificaciones push a los usuarios que compraron la aplicación en la tienda.

OPCIÓN 1: Uso de un certificado push .p12

  1. Seleccione Certificado.

  2. Seleccione el icono del archivo.

  3. Seleccione el archivo .p12 que exportó antes y, luego, Open (Abrir).

  4. Si es necesario, especifique la contraseña correcta.

  5. Seleccione el modo Espacio aislado.

  6. Seleccione Guardar.

OPCIÓN 2: Uso de autenticación basada en tokens

  1. Seleccione Token.

  2. Escriba los siguientes valores adquiridos anteriormente:

    • Identificador de clave
    • Identificador de lote
    • Identificador de equipo
    • Token
  3. Seleccione Espacio aislado.

  4. Seleccione Guardar.

Configuración de un centro de notificaciones con información de FCM

  1. Seleccione Google (GCM/FCM) en la sección Configuración en el menú de la izquierda.
  2. Escriba la clave de servidor que anotó en Google Firebase Console.
  3. Seleccione Guardar en la barra de herramientas.

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

En esta sección, creará la aplicación de back-end de ASP.NET Core API Web para controlar el registro de dispositivos y el envío de notificaciones a la aplicación móvil Xamarin.Forms.

Creación de un proyecto web

  1. En Visual Studio, seleccione ArchivoNueva solución.

  2. Seleccione .NET CoreAppASP.NET CoreAPISiguiente.

  3. En el cuadro de diálogo Configure your new ASP.NET Core Web API (Configure su nueva API web de ASP.NET Core), seleccione la plataforma de destino.NET Core 3.1.

  4. Escriba PushDemoApi como nombre del proyecto y, luego, seleccione Crear.

  5. Inicie la depuración(Escribael comando) para probar la aplicación con plantilla.

    Nota

    La aplicación con plantilla está configurada para usar WeatherForecastController como launchUrl. Se establece en PropiedadeslaunchSettings.json.

    Si aparece un mensaje que le indica que se ha encontrado un certificado de desarrollo no válido:

    1. Haga clic en para aceptar la ejecución de la herramienta "dotnet dev-certs https" para corregirlo. La herramienta "dotnet dev-certs https" le pide que especifique una contraseña para el certificado y la contraseña de la cadena de claves.

    2. Haga clic en cuando se le pida que instale y confíe en el nuevo certificado y, a continuación, escriba la contraseña de la cadena de claves.

  6. Expanda la carpeta Controladores y, después, elimine WeatherForecastController.cs.

  7. Elimine WeatherForecast.cs.

  8. Configure los valores de la configuración local con la herramienta Secret Manager. Si desacopla los secretos de la solución se asegurará de que no terminen en el control de código fuente. Abra Terminal y, a continuación, vaya al directorio del archivo de proyecto y 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 de los marcadores de posición por el nombre de su propio centro de notificaciones y por los valores de las cadenas de conexión. Tomó nota de ellos en la sección Creación de un centro de notificaciones. Si no lo hizo, puede buscarlos en Azure.

    NotificationsHub:Name:
    Consulte el nombre en el resumen de Información esencial en la parte superior de Información general.

    NotificationHub:ConnectionString:
    Consulte DefaultFullSharedAccessSignature en Directivas de acceso.

    Nota

    En escenarios de producción, puede contemplar otras opciones como Azure Key Vault para almacenar de forma segura la cadena de conexión. Por motivos de simplicidad, los secretos se agregarán a la configuración de la aplicación Azure App Service.

Autenticación de clientes mediante una clave de API (opcional)

Las claves de API no son tan seguras como los tokens, pero serán suficientes para la finalidad de este tutorial. Una clave de API se puede configurar fácilmente mediante el middleware de ASP.NET.

  1. Agregue la clave de API a los valores de configuración locales.

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

    Nota

    Debe reemplazar el valor del marcador de posición por el suyo propio y tomar nota del mismo.

  2. ControlHaga clic en el proyecto PushDemoApi, elija Nueva carpeta en el menú Agregar y, a continuación, haga clic en Agregar mediante autenticación como nombre de carpeta.

  3. ControlHaga clic en la carpeta Autenticación y elija Nuevo archivo... en el menú Agregar.

  4. Seleccione GeneralEmpty Class (Clasevacía general), escriba ApiKeyAuthOptions.cs en Name(Nombre) y haga clic en New (Nuevo) agregando la siguiente implementación.

    using Microsoft.AspNetCore.Authentication;
    
    namespace PushDemoApi.Authentication
    {
        public class ApiKeyAuthOptions : AuthenticationSchemeOptions
        {
            public const string DefaultScheme = "ApiKey";
            public string Scheme => DefaultScheme;
            public string ApiKey { get; set; }
        }
    }
    
  5. Agregue otra clase vacía a la carpeta Autenticación denominada ApiKeyAuthHandler.cs y, a continuación, agregue la siguiente implementación.

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

    Nota

    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.

  6. Agregue otra clase vacía a la carpeta Autenticación denominada ApiKeyAuthenticationBuilderExtensions.cs y, a continuación, agregue la siguiente implementación.

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

    Nota

    Este método de extensión simplifica el código de configuración del middleware en Startup.cs facilitando su lectura y seguimiento.

  7. En Startup.cs, actualice el método ConfigureServices para configurar la autenticación de la clave de API con la llamada al método services.AddControllers.

    using PushDemoApi.Authentication;
    using PushDemoApi.Models;
    using PushDemoApi.Services;
    
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllers();
    
        services.AddAuthentication(options =>
        {
            options.DefaultAuthenticateScheme = ApiKeyAuthOptions.DefaultScheme;
            options.DefaultChallengeScheme = ApiKeyAuthOptions.DefaultScheme;
        }).AddApiKeyAuth(Configuration.GetSection("Authentication").Bind);
    }
    
  8. También en Startup.cs, actualice el método Configure para llamar a los métodos de extensión UseAuthentication y UseAuthorization de IApplicationBuilder de la aplicación. Asegúrese de que se llama a esos métodos después de UseRouting y antes de app.UseEndpoints.

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

    Nota

    La llamada a UseAuthentication registra el middleware que usa los esquemas de autenticación previamente registrados (a partir de ConfigureServices). Se debe llamar a este antes que a cualquier otro middleware que dependa de la autenticación de los usuarios.

Incorporación de dependencias y configuración de servicios

ASP.NET Core admite el modelo de diseño de software de inserción de dependencias (DI), que es una técnica para lograr la inversión de control (IoC) entre las clases y sus dependencias.

El uso del centro de notificaciones y el SDK de Notification Hubs para operaciones de back-end se encapsula dentro de un servicio. El servicio se registra y se pone a disposición a través de una abstracción adecuada.

  1. ControlHaga clic en la carpeta Dependencias y elija Administrar NuGet paquetes....

  2. Busque Microsoft.Azure.NotificationHubs y asegúrese de que está activado.

  3. Haga clic en Agregar paquetes y, a continuación, haga clic en Aceptar cuando se le pida que acepte los términos de licencia.

  4. ControlHaga clic en el proyecto PushDemoApi, elija Nueva carpeta en el menú Agregar y, a continuación, haga clic en Agregar mediante modelos como nombre de carpeta.

  5. ControlHaga clic en la carpeta Modelos y elija Nuevo archivo... en el menú Agregar.

  6. Seleccione GeneralEmpty Class (Clasevacía general), escriba PushTemplates.cs enName(Nombre) y haga clic en New (Nuevo) agregando la siguiente implementación.

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

    Nota

    Esta clase contiene las cargas de notificación con tokens para las notificaciones genéricas y silenciosas que requiere este escenario. Las 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 forma está fuera del ámbito de este tutorial. Para producción, considere la posibilidad de utilizar plantillas personalizadas.

  7. Agregue otra clase vacía a la carpeta Models llamada DeviceInstallation.cs y, a continuación, agregue la siguiente implementación.

    using System.Collections.Generic;
    using System.ComponentModel.DataAnnotations;
    
    namespace PushDemoApi.Models
    {
        public class DeviceInstallation
        {
            [Required]
            public string InstallationId { get; set; }
    
            [Required]
            public string Platform { get; set; }
    
            [Required]
            public string PushChannel { get; set; }
    
            public IList<string> Tags { get; set; } = Array.Empty<string>();
        }
    }
    
  8. Agregue otra clase vacía a la carpeta Modelos denominada NotificationRequest.cs y, a continuación, agregue la siguiente implementación.

    using System;
    
    namespace PushDemoApi.Models
    {
        public class NotificationRequest
        {
            public string Text { get; set; }
            public string Action { get; set; }
            public string[] Tags { get; set; } = Array.Empty<string>();
            public bool Silent { get; set; }
        }
    }
    
  9. Agregue otra clase vacía a la carpeta Modelos denominada NotificationHubOptions.cs y, a continuación, agregue la siguiente implementación.

    using System.ComponentModel.DataAnnotations;
    
    namespace PushDemoApi.Models
    {
        public class NotificationHubOptions
        {
            [Required]
            public string Name { get; set; }
    
            [Required]
            public string ConnectionString { get; set; }
        }
    }
    
  10. Agregue una nueva carpeta al proyecto PushDemoApi denominada Servicios.

  11. Agregue una interfaz vacía a la carpeta Servicios denominada INotificationService.cs y, a continuación, agregue la siguiente implementación.

    using System.Threading;
    using System.Threading.Tasks;
    using PushDemoApi.Models;
    
    namespace PushDemoApi.Services
    {
        public interface INotificationService
        {
            Task<bool> CreateOrUpdateInstallationAsync(DeviceInstallation deviceInstallation, CancellationToken token);
            Task<bool> DeleteInstallationByIdAsync(string installationId, CancellationToken token);
            Task<bool> RequestNotificationAsync(NotificationRequest notificationRequest, CancellationToken token);
        }
    }
    
  12. Agregue una clase vacía a la carpeta Servicios denominada NotificationHubsService.cs y, a continuación, agregue el código siguiente para implementar la interfaz INotificationService:

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

    Nota

    La expresión de etiqueta que se ha proporcionado a SendTemplateNotificationAsync está limitada a 20 etiquetas. Está limitada a 6 para la mayoría de los operadores, pero la expresión solo contiene "OR" (||) en este caso. Si hay más de 20 etiquetas en la solicitud, se deben dividir en varias solicitudes. Consulte la documentación Expresiones de etiqueta y enrutamiento para obtener más detalles.

  13. En Startup.cs, actualice el método ConfigureServices para agregar NotificationHubsService como una implementación de base de datos única de INotificationService.

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

Creación de la API de notificaciones

  1. ControlHaga clic en la carpeta Controladores y elija Nuevo archivo... en el menú Agregar.

  2. Seleccione ASP.NET Corede controlador de API web,escriba NotificationsController en Nombrey, a continuación, haga clic en Nuevo.

    Nota

    Si está utilizando Visual Studio 2019, elija la plantilla Controlador de API con acciones de lectura y escritura.

  3. Agregue los siguientes espacios de nombres al principio del archivo.

    using System.ComponentModel.DataAnnotations;
    using System.Net;
    using System.Threading;
    using System.Threading.Tasks;
    using Microsoft.AspNetCore.Authorization;
    using Microsoft.AspNetCore.Mvc;
    using PushDemoApi.Models;
    using PushDemoApi.Services;
    
  4. Actualice el controlador con plantilla para que se derive de ControllerBase y se decore con el atributo ApiController.

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

    Nota

    La clase base Controller proporciona compatibilidad con las vistas pero esto no es necesario en este caso y, por tanto, se puede usar ControllerBase en su lugar. Si está utilizando Visual Studio 2019, puede omitir este paso.

  5. Si eligió completar la sección Autenticación de clientes mediante una clave de API, debe decorar NotificationsController con el atributo Authorize también.

    [Authorize]
    
  6. Actualice el constructor para que acepte la instancia registrada de INotificationService como argumento y asígnela a un miembro de solo lectura.

    readonly INotificationService _notificationService;
    
    public NotificationsController(INotificationService notificationService)
    {
        _notificationService = notificationService;
    }
    
  7. En launchSettings.json (dentro de la carpeta Propiedades), cambie launchUrl de a api/notifications para que coincida con la dirección URL especificada en el atributo RegistrationsControllerRoute.

  8. Inicie la depuración(Escribael comando ) para validar que la aplicación funciona con el nuevo NotificationsController y devuelve un estado 401 No autorizado.

    Nota

    Puede que Visual Studio no inicie automáticamente la aplicación en el explorador. Va a usar Postman para probar la API a partir de ahora.

  9. En una nueva pestaña de Postman , establezca la solicitud en GET. Escriba la dirección siguiente reemplazando el marcador de posición > applicationUrl por el valor https > que se encuentra en PropiedadeslaunchSettings.json.

    <applicationUrl>/api/notifications
    

    Nota

    ApplicationUrl debe ser ' ' para el perfil predeterminado. Si usa IIS (opción predeterminada en Visual Studio 2019 en Windows), debe usar la dirección applicationUrl especificada en el elemento iisSettings en su lugar. Recibirá una respuesta 404 si la dirección es incorrecta.

  10. Si eligió completar la sección Autenticación de clientes mediante una clave de API, asegúrese de configurar los encabezados de solicitud para que incluyan el valor apikey.

    Clave Value
    apikey <your_api_key>
  11. Haga clic en el botón Send (Enviar).

    Nota

    Debe recibir el estado 200 CORRECTO con cierto contenido JSON.

    Si recibe una advertencia de comprobación de certificado SSL, puede cambiar la configuración de Postman de la comprobación de certificado SSL en Configuración.

  12. Reemplace los métodos de clase con plantilla de NotificationsController.cs por el siguiente código.

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

Creación de la aplicación de API

Ahora va a crear una aplicación de API en Azure App Service para hospedar el servicio de back-end.

  1. Inicie sesión en Azure Portal.

  2. Haga clic en Crear un recurso, busque y elija Aplicación de API y, a continuación, haga clic en Crear.

  3. Actualice los campos siguientes y haga clic en Crear.

    Nombre de la aplicación:
    Escriba un nombre único global para la aplicación de API.

    Subscription (Suscripción):
    Elija la misma suscripción de destino en la que creó el centro de notificaciones.

    Grupos de recursos:
    Elija el mismo grupo de recursos en el que creó el centro de notificaciones.

    Plan de App Service/ubicación:
    Cree un nuevo plan de App Service.

    Nota

    Cambie la opción predeterminada a un plan que incluya compatibilidad con SSL. De lo contrario, tendrá que realizar los pasos adecuados al trabajar con la aplicación móvil para evitar que se bloqueen las solicitudes http.

    Application Insights:
    Mantenga la opción sugerida (se creará un nuevo recurso con ese nombre) o elija un recurso existente.

  4. Una vez que se haya aprovisionado la aplicación de API, vaya a ese recurso.

  5. Tome nota de la propiedad URL en el resumen de Información esencial que aparece en la parte superior de Información general. Esta dirección URL es el punto de conexión de back-end que se utilizará más adelante en este tutorial.

    Nota

    La dirección URL utiliza el nombre de la aplicación de API que especificó anteriormente, con el formato https://<app_name>.azurewebsites.net.

  6. Seleccione Configuración en la lista (en Configuración).

  7. Para cada una de las opciones siguientes, haga clic en Nueva configuración de la aplicación para escribir el nombre y un valor y, a continuación, haga clic en Aceptar.

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

    Nota

    Se trata de la misma configuración que definió anteriormente en la configuración del usuario. Estos valores se pueden copiar. La opción Authentication:ApiKey solo es necesaria si eligió completar la sección Autenticación de clientes mediante una clave de API. En escenarios de producción, puede contemplar otras opciones como Azure Key Vault. En este caso, estas opciones se han agregado como configuración de la aplicación por motivos de claridad.

  8. Una vez que se haya agregado la configuración de la aplicación, haga clic en Guardar y en Continuar.

Publicación del servicio back-end

A continuación implementaremos la aplicación en la aplicación de API para que todos los dispositivos puedan acceder a ella.

Nota

Los pasos siguientes son específicos de Visual Studio para Mac. Si está utilizando Visual Studio 2019 en Windows, el flujo de publicación será diferente. Consulte Publicación en Azure App Service en Windows.

  1. Cambie la configuración de Depurar a Crear versión si todavía no lo ha hecho.

  2. ControlHaga clic en el proyecto PushDemoApi y elija Publicar en Azure... en el menú Publicar.

  3. Si se le solicita, siga el flujo de autenticación. Use la cuenta que usó en la sección anterior de creación de la aplicación de API.

  4. Seleccione la aplicación de API de Azure App Service que creó anteriormente en la lista como destino de publicación y, a continuación, haga clic en Publicar.

Una vez completado el asistente, este publica la aplicación en Azure y, posteriormente, la abre. Tome nota de la dirección URL si aún no lo ha hecho. Esta dirección URL es el punto de conexión de back-end que se utiliza más adelante en este tutorial.

Validación de la API publicada

  1. En Postman abra una nueva pestaña, establezca la solicitud en PUT y escriba la dirección siguiente. Reemplace el marcador de posición por la dirección base que anotó en la sección anterior de publicación del servicio back-end.

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

    Nota

    La dirección base debe tener el formato https://<app_name>.azurewebsites.net/.

  2. Si eligió completar la sección Autenticación de clientes mediante una clave de API, asegúrese de configurar los encabezados de solicitud para que incluyan el valor apikey.

    Clave Value
    apikey <your_api_key>
  3. Elija la opción sin formato para el cuerpo y, a continuación, elija JSON en la lista de opciones de formato y, después, incluya algún contenido JSON de marcador de posición:

    {}
    
  4. Haga clic en Enviar.

    Nota

    Debe recibir el estado 422 - Entidad no procesable del servicio.

  5. Realice de nuevo los pasos del 1 al 4, pero esta vez especificando el punto de conexión de las solicitudes para comprobar que recibe una respuesta 400 - Solicitud incorrecta.

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

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 móvil cliente.

Creación de una aplicación Xamarin.Forms multiplataforma

En esta sección, creará una aplicación móvil Xamarin.Forms que implemente notificaciones push entre plataformas.

Le permite registrar y anular el registro de un centro de notificaciones a través del servicio back-end que ha creado.

Cuando se especifica una acción y la aplicación está en primer plano se muestra una alerta. De lo contrario, las notificaciones aparecen en el centro de notificaciones.

Nota

Normalmente, realizará las acciones de registro (y de anulación de registro) durante el momento adecuado del ciclo de vida de la aplicación (o quizás como parte de la experiencia de primera ejecución) sin entradas explícitas de registro o anulación de registro del usuario. De todas formas, en este ejemplo se requerirá la intervención explícita del usuario para permitir que esta funcionalidad se pueda explorar y probar más fácilmente.

Creación de la solución Xamarin.Forms

  1. En Visual Studio, cree una nueva solución Xamarin.Forms mediante Aplicación de Forms en blanco como plantilla y escriba PushDemo como nombre del proyecto.

    Nota

    En el cuadro de diálogo Configuración de Aplicación de Forms en blanco, asegúrese de que el identificador de la organización coincide con el valor que ha utilizado anteriormente y de que están seleccionados los destinos de Android e iOS.

  2. ControlHaga clic en la solución PushDemo y elija Actualizar NuGet paquetes.

  3. ControlHaga clic en la solución PushDemo y elija Administrar NuGet paquetes. .

  4. Busque Newtonsoft.Json y asegúrese de que está seleccionado.

  5. Haga clic en Agregar paquetes y, a continuación, haga clic en Aceptar cuando se le pida que acepte los términos de licencia.

  6. Compile y ejecute la aplicación en cada plataforma de destino(comandoEscriba ) para probar las ejecuciones de la aplicación con plantilla en los dispositivos.

Implementación de componentes multiplataforma

  1. ControlHaga clic en el proyecto PushDemo, elija Nueva carpeta en el menú Agregar y, a continuación, haga clic en Agregar mediante modelos como nombre de carpeta.

  2. ControlHaga clic en la carpeta Modelos y elija Nuevo archivo... en el menú Agregar.

  3. Seleccione GeneralEmpty Class (Clase vacíageneral), escriba DeviceInstallation.csy agregue la siguiente implementación.

    using System.Collections.Generic;
    using Newtonsoft.Json;
    
    namespace PushDemo.Models
    {
        public class DeviceInstallation
        {
            [JsonProperty("installationId")]
            public string InstallationId { get; set; }
    
            [JsonProperty("platform")]
            public string Platform { get; set; }
    
            [JsonProperty("pushChannel")]
            public string PushChannel { get; set; }
    
            [JsonProperty("tags")]
            public List<string> Tags { get; set; } = new List<string>();
        }
    }
    
  4. Agregue una enumeración vacía a la carpeta Modelos denominada PushDemoAction.cs con la siguiente implementación.

    namespace PushDemo.Models
    {
        public enum PushDemoAction
        {
            ActionA,
            ActionB
        }
    }
    
  5. Agregue una nueva carpeta al proyecto PushDemo denominada Servicios y, a continuación, agregue una clase vacía a esa carpeta llamada ServiceContainer.cs con la siguiente implementación.

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

    Nota

    Se trata de una versión reducida de la clase ServiceContainer del repositorio XamCAT. Se usará como un contenedor de inversión de control ligero.

  6. Agregue una interfaz vacía a la carpeta Servicios denominada IDeviceInstallationService.cs y, a continuación, agregue el siguiente código.

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

    Nota

    Cada destino implementará y arrancará posteriormente esta interfaz para proporcionar la funcionalidad específica de la plataforma y la información de DeviceInstallation que necesita el servicio back-end.

  7. Agregue otra interfaz vacía a la carpeta Servicios denominada INotificationRegistrationService.cs y, a continuación, agregue el siguiente código.

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

    Nota

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

  8. Agregue otra interfaz vacía a la carpeta Servicios denominada INotificationActionService.cs y, a continuación, agregue el siguiente código.

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

    Nota

    Este se usa como un mecanismo sencillo para centralizar el control de las acciones de notificación.

  9. Agregue una interfaz vacía a la carpeta Servicios denominada IPushDemoNotificationActionService.cs que se deriva de INotificationActionService con la siguiente implementación.

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

    Nota

    Este tipo es específico de la aplicación PushDemo y utiliza la enumeración PushDemoAction para identificar la acción que se desencadena de forma fuertemente tipada.

  10. Agregue una clase vacía a la carpeta Servicios denominada NotificationRegistrationService.cs que implemente INotificationRegistrationService con el siguiente código.

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

    Nota

    El argumento apiKey solo es necesario si eligió completar la sección Autenticación de clientes mediante una clave de API.

  11. Agregue una clase vacía a la carpeta Servicios denominada PushDemoNotificationActionService.cs que implemente IPushDemoNotificationActionService con el siguiente código.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using PushDemo.Models;
    
    namespace PushDemo.Services
    {
        public class PushDemoNotificationActionService : IPushDemoNotificationActionService
        {
            readonly Dictionary<string, PushDemoAction> _actionMappings = new Dictionary<string, PushDemoAction>
            {
                { "action_a", PushDemoAction.ActionA },
                { "action_b", PushDemoAction.ActionB }
            };
    
            public event EventHandler<PushDemoAction> ActionTriggered = delegate { };
    
            public void TriggerAction(string action)
            {
                if (!_actionMappings.TryGetValue(action, out var pushDemoAction))
                    return;
    
                List<Exception> exceptions = new List<Exception>();
    
                foreach (var handler in ActionTriggered?.GetInvocationList())
                {
                    try
                    {
                        handler.DynamicInvoke(this, pushDemoAction);
                    }
                    catch (Exception ex)
                    {
                        exceptions.Add(ex);
                    }
                }
    
                if (exceptions.Any())
                    throw new AggregateException(exceptions);
            }
        }
    }
    
  12. Agregue una clase vacía al proyecto PushDemo llamada Config.cs con la siguiente implementación.

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

    Nota

    Se utiliza como una forma 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. Esto lo hará en el paso siguiente.

    El campo ApiKey solo es necesario si eligió completar la sección Autenticación de clientes mediante una clave de API.

  13. Agregue una clase vacía al proyecto PushDemo llamada esta vez Config.local_secrets.cs con la siguiente implementación.

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

    Nota

    Reemplace los valores del marcador de posición por los suyos propios. Debe anotarlos al crear el servicio back-end. La dirección URL de la aplicación de API debe ser . No olvide agregar *.local_secrets.* al archivo gitignore para evitar la confirmación de este archivo.

    El campo ApiKey solo es necesario si eligió completar la sección Autenticación de clientes mediante una clave de API.

  14. Agregue una clase vacía al proyecto PushDemo llamada Bootstrap.cs con la siguiente implementación.

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

    Nota

    Cada plataforma llamará al método Begin cuando la aplicación se inicie pasando una implementación específica de la plataforma de IDeviceInstallationService.

    El argumento de constructor notificationRegistrationServiceapiKey solo es necesario si eligió completar la sección Autenticar clientes mediante una clave de API.

Implementación de una interfaz de usuario multiplataforma

  1. En el proyecto PushDemo, abra MainPage.xaml y reemplace el control StackLayout por lo siguiente.

    <StackLayout VerticalOptions="EndAndExpand"  
                 HorizontalOptions="FillAndExpand"
                 Padding="20,40">
        <Button x:Name="RegisterButton"
                Text="Register"
                Clicked="RegisterButtonClicked" />
        <Button x:Name="DeregisterButton"
                Text="Deregister"
                Clicked="DeregisterButtonClicked" />
    </StackLayout>
    
  2. Ahora, en MainPage.xaml.cs, agregue un campo de respaldo de solo lectura para almacenar una referencia a la implementación INotificationRegistrationService.

    readonly INotificationRegistrationService _notificationRegistrationService;
    
  3. En el constructor MainPage, resuelva la implementación INotificationRegistrationService con ServiceContainer y asígnela al campo de respaldo notificationRegistrationService.

    public MainPage()
    {
        InitializeComponent();
    
        _notificationRegistrationService =
            ServiceContainer.Resolve<INotificationRegistrationService>();
    }
    
  4. Implemente los controladores de eventos para los botones RegisterButton y DeregisterButton Eventos clicked que llaman a los métodos RegisterDeregister correspondientes.

    void RegisterButtonClicked(object sender, EventArgs e)
        => _notificationRegistrationService.RegisterDeviceAsync().ContinueWith((task)
            => { ShowAlert(task.IsFaulted ?
                    task.Exception.Message :
                    $"Device registered"); });
    
    void DeregisterButtonClicked(object sender, EventArgs e)
        => _notificationRegistrationService.DeregisterDeviceAsync().ContinueWith((task)
            => { ShowAlert(task.IsFaulted ?
                    task.Exception.Message :
                    $"Device deregistered"); });
    
    void ShowAlert(string message)
        => MainThread.BeginInvokeOnMainThread(()
            => DisplayAlert("PushDemo", message, "OK").ContinueWith((task)
                => { if (task.IsFaulted) throw task.Exception; }));
    
  5. Ya en App.xaml.cs, asegúrese de que se hace referencia a los siguientes espacios de nombres.

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

    void NotificationActionTriggered(object sender, PushDemoAction e)
        => ShowActionAlert(e);
    
    void ShowActionAlert(PushDemoAction action)
        => MainThread.BeginInvokeOnMainThread(()
            => MainPage?.DisplayAlert("PushDemo", $"{action} action received", "OK")
                .ContinueWith((task) => { if (task.IsFaulted) throw task.Exception; }));
    
  7. En el constructor App, resuelva la implementación de IPushNotificationActionService mediante ServiceContainer y suscríbase al eventoActionTriggeredde IPushDemoNotificationActionService.

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

    Nota

    Esto es simplemente para mostrar la recepción y propagación de las acciones con notificaciones push. Normalmente, estas se tratarían de forma silenciosa, por ejemplo, yendo a una vista específica o actualizando algunos datos en lugar de mostrar una alerta a través de la página raíz, MainPage en este caso.

Configuración del proyecto de Android nativo para las notificaciones push

Validación del nombre y los permisos del paquete

  1. En PushDemo.Android, abra las opciones del proyecto y, a continuación, Aplicación de Android en la sección Compilación.

  2. Compruebe que el nombre del paquete coincide con el valor que usó en el proyecto PushDemo de la consola firebase. El nombre del paquete tenía el formato .

  3. Establezca la opción Versión de Android mínima en Android 8.0 (nivel de API 26) y la opción Versión de Android de destino en el nivel de API más reciente.

    Nota

    En este tutorial solo se admiten los dispositivos que ejecutan el nivel de API 26 o posterior, pero se puede ampliar para admitir dispositivos que ejecuten versiones anteriores.

  4. Asegúrese de que los permisos INTERNET y READ_PHONE_STATE estén habilitados en Permisos necesarios.

  5. Haga clic en Aceptar

Incorporación de los paquetes de Xamarin Google Play Services base y Xamarin.Firebase.Messaging

  1. En PushDemo.Android,controlHaga clic en la carpeta Paquetes y, a continuación, elija Administrar NuGet paquetes....

  2. Busque Xamarin.GooglePlayServices.Base (no Basement) y asegúrese de que está seleccionado.

  3. Busque Xamarin.Firebase.Messaging y asegúrese de que está seleccionado.

  4. Haga clic en Agregar paquetes y, a continuación, haga clic en Aceptar cuando se le pida que acepte los términos de licencia.

Adición del archivo JSON de Google Services

  1. ControlHaga clic en el proyecto y, a continuación, elija Archivo existente... en el menú Agregar.

  2. Elija el archivo google-services.json que descargó anteriormente al configurar el proyecto PushDemo en la consola Firebase y, a continuación, haga clic en Abrir.

  3. Cuando se le solicite, elija copiar el archivo en el directorio.

  4. ControlHaga clic en el archivo google-services.json desde el proyecto y asegúrese de que GoogleServicesJson está establecido como acción de compilación.

Administración de las notificaciones push para Android

  1. ControlHaga clic en el proyecto, elija Nueva carpeta en el menú Agregar y, a continuación, haga clic en Agregar mediante servicios como nombre de carpeta.

  2. ControlHaga clic en la carpeta Servicios y, a continuación, elija Nuevo archivo... en el menú Agregar.

  3. Seleccione GeneralEmpty Class (Clase vacíageneral), escriba DeviceInstallationService.cs enName(Nombre) y haga clic en New (Nuevo) agregando la siguiente implementación.

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

    Nota

    Esta clase proporciona un identificador único (mediante Secure.AndroidId) como parte de la carga de registro del centro de notificaciones.

  4. Agregue otra clase vacía a la carpeta Servicios denominada PushNotificationFirebaseMessagingService.cs y, a continuación, agregue la siguiente implementación.

    using Android.App;
    using Android.Content;
    using Firebase.Messaging;
    using PushDemo.Services;
    
    namespace PushDemo.Droid.Services
    {
        [Service]
        [IntentFilter(new[] { "com.google.firebase.MESSAGING_EVENT" })]
        public class PushNotificationFirebaseMessagingService : FirebaseMessagingService
        {
            IPushDemoNotificationActionService _notificationActionService;
            INotificationRegistrationService _notificationRegistrationService;
            IDeviceInstallationService _deviceInstallationService;
    
            IPushDemoNotificationActionService NotificationActionService
                => _notificationActionService ??
                    (_notificationActionService =
                    ServiceContainer.Resolve<IPushDemoNotificationActionService>());
    
            INotificationRegistrationService NotificationRegistrationService
                => _notificationRegistrationService ??
                    (_notificationRegistrationService =
                    ServiceContainer.Resolve<INotificationRegistrationService>());
    
            IDeviceInstallationService DeviceInstallationService
                => _deviceInstallationService ??
                    (_deviceInstallationService =
                    ServiceContainer.Resolve<IDeviceInstallationService>());
    
            public override void OnNewToken(string token)
            {
                DeviceInstallationService.Token = token;
    
                NotificationRegistrationService.RefreshRegistrationAsync()
                    .ContinueWith((task) => { if (task.IsFaulted) throw task.Exception; });
            }
    
            public override void OnMessageReceived(RemoteMessage message)
            {
                if(message.Data.TryGetValue("action", out var messageAction))
                    NotificationActionService.TriggerAction(messageAction);
            }
        }
    }
    
  5. En MainActivity.cs, asegúrese de que se han agregado los siguientes espacios de nombres en la parte superior del archivo.

    using System;
    using Android.App;
    using Android.Content;
    using Android.Content.PM;
    using Android.OS;
    using Android.Runtime;
    using Firebase.Iid;
    using PushDemo.Droid.Services;
    using PushDemo.Services;
    
  6. En MainActivity.cs, establezca LaunchMode en SingleTop para que MainActivity no se cree de nuevo cuando se abra.

    [Activity(
        Label = "PushDemo",
        LaunchMode = LaunchMode.SingleTop,
        Icon = "@mipmap/icon",
        Theme = "@style/MainTheme",
        MainLauncher = true,
        ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
    
  7. Agregue propiedades privadas y sus respectivos campos de respaldo para almacenar una referencia a las implementaciones IPushNotificationActionService y IDeviceInstallationService.

    IPushDemoNotificationActionService _notificationActionService;
    IDeviceInstallationService _deviceInstallationService;
    
    IPushDemoNotificationActionService NotificationActionService
        => _notificationActionService ??
            (_notificationActionService =
            ServiceContainer.Resolve<IPushDemoNotificationActionService>());
    
    IDeviceInstallationService DeviceInstallationService
        => _deviceInstallationService ??
            (_deviceInstallationService =
            ServiceContainer.Resolve<IDeviceInstallationService>());
    
  8. Implemente la interfaz IOnSuccessListener para recuperar y almacenar el token de Firebase.

    public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity, Android.Gms.Tasks.IOnSuccessListener
    {
        ...
    
        public void OnSuccess(Java.Lang.Object result)
            => DeviceInstallationService.Token =
                result.Class.GetMethod("getToken").Invoke(result).ToString();
    }
    
  9. Agregue un nuevo método llamado ProcessNotificationActions que comprobará si una intención determinada tiene un valor adicional denominado acción. Desencadene condicionalmente esa acción mediante la implementación de IPushDemoNotificationActionService.

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

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

    Nota

    Dado que LaunchMode para la actividad está establecido en SingleTop, se enviará una intención a la instancia de la actividad existente a través del método OnNewIntent en lugar de a través del método OnCreate y, por tanto, debe controlar una intención entrante en ambos métodos, OnCreate y OnNewIntent.

  11. Actualice el método OnCreate para llamar a justo después de la llamada a pasando la implementación específica de la plataforma de base.OnCreatebase.OnCreate.

    Bootstrap.Begin(() => new DeviceInstallationService());
    
  12. En el mismo método, llame condicionalmente a GetInstanceId en la instancia de FirebaseApp, justo después de la llamada a , agregando MainActivity como IOnSuccessListener.

    if (DeviceInstallationService.NotificationsSupported)
    {
        FirebaseInstanceId.GetInstance(Firebase.FirebaseApp.Instance)
            .GetInstanceId()
            .AddOnSuccessListener(this);
    }
    
  13. Todavía en OnCreate, llame a ProcessNotificationActions inmediatamente después de la llamada a pasando la intención actual.

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

Nota

Debe volver a registrar la aplicación cada vez que la ejecute y la detenga en una sesión de depuración para seguir recibiendo notificaciones push.

Configuración del proyecto de iOS nativo para las notificaciones push

Configuración de Info.plist y Entitlements.plist

  1. Asegúrese de que ha iniciado sesión en su cuenta de desarrollador de Apple en Visual StudioPreferencias...EditorialSe han descargado las cuentas de desarrollador de Apple y el certificado y el perfil de aprovisionamiento adecuados. Debe haber creado estos recursos como parte de los pasos anteriores.

  2. En PushDemo.iOS,abra Info.plist y asegúrese de que BundleIdentifier coincide con el valor que se usó para el perfil de aprovisionamiento correspondiente en apple Portal para desarrolladores. BundleIdentifier tenía el formato .

  3. En el mismo archivo, establezca el valor Versión mínima del sistema en 13.0.

    Nota

    En este tutorial solo se admiten los dispositivos que ejecutan iOS 13.0 y versiones posteriores, pero se puede ampliar para admitir dispositivos que ejecuten versiones anteriores.

  4. Abra Opciones del proyecto para PushDemo.iOS (haga doble clic en el proyecto).

  5. En Project opciones,en Build iOS Bundle Signing (Compilar firma de lote de iOS),asegúrese de que la cuenta de desarrollador está seleccionada en Team (Equipo). A continuación, asegúrese de que la opción, "Automatically manage signing" (Administrar automáticamente la firma) esté seleccionada, y las opciones Certificado de firma y Perfil de aprovisionamiento estén también seleccionadas automáticamente.

    Nota

    Si Certificado de firma y Perfil de aprovisionamiento no se han seleccionado automáticamente, elija Aprovisionamiento manual y, a continuación, haga clic en Bundle Signing Options (Opciones de firma de lote). Asegúrese de que su Equipo está seleccionado en Identidad de firma, y de que el perfil de aprovisionamiento específico de PushDemo está seleccionado para Perfil de aprovisionamiento, tanto para la configuración de Depurar como para la de Versión, y compruebe que iPhone está seleccionado como Plataforma en ambos casos.

  6. En PushDemo.iOS, abra Entitlements.plist y asegúrese de que la casilla Habilitar notificaciones push está seleccionada cuando se visualiza en la pestaña Derechos. A continuación, asegúrese de que el valor Entorno APS está establecida en desarrollo cuando se visualiza en la pestaña Origen.

Administración de las notificaciones push para iOS

  1. ControlHaga clic en el proyecto PushDemo.iOS, elija Nueva carpeta en el menú Agregar y, a continuación, haga clic en Agregar mediante servicios como nombre de carpeta.

  2. ControlHaga clic en la carpeta Servicios y, a continuación, elija Nuevo archivo... en el menú Agregar.

  3. Seleccione GeneralEmpty Class (Clase vacíageneral), escriba DeviceInstallationService.cs enName(Nombre) y haga clic en New (Nuevo) agregando la siguiente implementación.

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

    Nota

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

  4. Agregue una nueva carpeta al proyecto PushDemo.iOS denominada Extensiones y, a continuación, agregue una clase vacía a esa carpeta llamada NSDataExtensions.cs con la siguiente implementación.

    using System.Text;
    using Foundation;
    
    namespace PushDemo.iOS.Extensions
    {
        internal static class NSDataExtensions
        {
            internal static string ToHexString(this NSData data)
            {
                var bytes = data.ToArray();
    
                if (bytes == null)
                    return null;
    
                StringBuilder sb = new StringBuilder(bytes.Length * 2);
    
                foreach (byte b in bytes)
                    sb.AppendFormat("{0:x2}", b);
    
                return sb.ToString().ToUpperInvariant();
            }
        }
    }
    
  5. En AppDelegate.cs, asegúrese de que se han agregado los siguientes espacios de nombres en la parte superior del archivo.

    using System;
    using System.Diagnostics;
    using System.Threading.Tasks;
    using Foundation;
    using PushDemo.iOS.Extensions;
    using PushDemo.iOS.Services;
    using PushDemo.Services;
    using UIKit;
    using UserNotifications;
    using Xamarin.Essentials;
    
  6. Agregue propiedades privadas y sus respectivos campos de respaldo para almacenar una referencia a las implementaciones IPushDemoNotificationActionService, INotificationRegistrationService y IDeviceInstallationService.

    IPushDemoNotificationActionService _notificationActionService;
    INotificationRegistrationService _notificationRegistrationService;
    IDeviceInstallationService _deviceInstallationService;
    
    IPushDemoNotificationActionService NotificationActionService
        => _notificationActionService ??
            (_notificationActionService =
            ServiceContainer.Resolve<IPushDemoNotificationActionService>());
    
    INotificationRegistrationService NotificationRegistrationService
        => _notificationRegistrationService ??
            (_notificationRegistrationService =
            ServiceContainer.Resolve<INotificationRegistrationService>());
    
    IDeviceInstallationService DeviceInstallationService
        => _deviceInstallationService ??
            (_deviceInstallationService =
            ServiceContainer.Resolve<IDeviceInstallationService>());
    
  7. Agregue el método RegisterForRemoteNotifications para registrar la configuración de notificaciones de usuario y, a continuación, para las notificaciones remotas con APNs.

    void RegisterForRemoteNotifications()
    {
        MainThread.BeginInvokeOnMainThread(() =>
        {
            var pushSettings = UIUserNotificationSettings.GetSettingsForTypes(
                UIUserNotificationType.Alert |
                UIUserNotificationType.Badge |
                UIUserNotificationType.Sound,
                new NSSet());
    
            UIApplication.SharedApplication.RegisterUserNotificationSettings(pushSettings);
            UIApplication.SharedApplication.RegisterForRemoteNotifications();
        });
    }
    
  8. Agregue el método CompleteRegistrationAsync para establecer el valor de propiedad. Actualice el registro y almacene en caché el token del dispositivo si se ha actualizado desde que se almacenó por última vez.

    Task CompleteRegistrationAsync(NSData deviceToken)
    {
        DeviceInstallationService.Token = deviceToken.ToHexString();
        return NotificationRegistrationService.RefreshRegistrationAsync();
    }
    
  9. 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
        {
            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. Invalide el método RegisteredForRemoteNotifications pasando el argumento deviceToken al método CompleteRegistrationAsync.

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

    public override void ReceivedRemoteNotification(
        UIApplication application,
        NSDictionary userInfo)
        => ProcessNotificationActions(userInfo);
    
  12. Invalide el método FailedToRegisterForRemoteNotifications para registrar el error.

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

    Nota

    Esto es claramente un marcador de posición. Para escenarios de producción tendrá que implementar el registro y el control de errores apropiados.

  13. Actualice el método FinishedLaunching para llamar a justo después de la llamada a pasando la implementación específica de la plataforma de Forms.InitForms.Init.

    Bootstrap.Begin(() => new DeviceInstallationService());
    
  14. En el mismo método, solicite autorización condicionalmente y regístrese para notificaciones remotas inmediatamente después de Bootstrap.Begin.

    if (DeviceInstallationService.NotificationsSupported)
    {
        UNUserNotificationCenter.Current.RequestAuthorization(
                UNAuthorizationOptions.Alert |
                UNAuthorizationOptions.Badge |
                UNAuthorizationOptions.Sound,
                (approvalGranted, error) =>
                {
                    if (approvalGranted && error == null)
                        RegisterForRemoteNotifications();
                });
    }
    
  15. Todavía en FinishedLaunching,llame a ProcessNotificationActions inmediatamente después de la llamada a si el argumento options contiene el elemento UIApplication.LaunchOptionsRemoteNotificationKey pasando el objeto userInfo resultante.

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

Probar la solución

Ahora puede probar el envío de notificaciones mediante el servicio back-end.

Envío de una notificación de prueba

  1. En Postman, abra una nueva pestaña.

  2. Establezca la solicitud en POST y escriba la siguiente dirección:

    https://<app_name>.azurewebsites.net/api/notifications/requests
    
  3. Si eligió completar la sección Autenticación de clientes mediante una clave de API, asegúrese de configurar los encabezados de solicitud para que incluyan el valor apikey.

    Clave Value
    apikey <your_api_key>
  4. Elija la opción sin formato para el cuerpo y, a continuación, elija JSON en la lista de opciones de formato y, después, incluya algún contenido JSON de marcador de posición:

    {
        "text": "Message from Postman!",
        "action": "action_a"
    }
    
  5. Seleccione el botón Codificar que aparece en la ventana del botón Guardar, en la parte superior derecha. La solicitud debe ser parecida a la del ejemplo siguiente cuando se muestra para HTML (dependiendo de si incluyó un encabezado de apikey):

    POST /api/notifications/requests HTTP/1.1
    Host: https://<app_name>.azurewebsites.net
    apikey: <your_api_key>
    Content-Type: application/json
    
    {
        "text": "Message from backend service",
        "action": "action_a"
    }
    
  6. Ejecute la aplicación PushDemo en una de las plataformas de destino o en las dos (Android e iOS).

    Nota

    Si va a realizar las pruebas en Android asegúrese de que no ejecutará en Depuración, o si esta se ha implementado mediante la ejecución de la aplicación, realice un cierre forzado de esta e iníciela de nuevo desde el iniciador.

  7. En la aplicación PushDemo, pulse en el botón Registrar.

  8. De nuevo en Postman , cierre la ventana Generate Code Snippets (Generar fragmentos de código), si todavía no lo ha hecho, y haga clic en el botón Enviar.

  9. Compruebe que obtiene una respuesta 200 CORRECTO en Postman y que la alerta aparece en la aplicación indicando que se ha recibido la acción ActionA.

  10. Cierre la aplicación PushDemo y, después, haga clic de nuevo en el botón Enviar en Postman .

  11. Compruebe que obtiene una respuesta 200 CORRECTO en Postman de nuevo. Compruebe que aparece una notificación en el área de notificaciones de la aplicación PushDemo con el mensaje correcto.

  12. Pulse en la notificación para confirmar que se abre la aplicación y que aparece la alerta que indica que se ha recibido la acción ActionA.

  13. De nuevo en Postman , modifique el cuerpo de la solicitud anterior para enviar una notificación silenciosa que especifica action_b en lugar de action_a para el valor action.

    {
        "action": "action_b",
        "silent": true
    }
    
  14. Con la aplicación abierta todavía, haga clic en el botón Enviar en Postman .

  15. Compruebe que obtiene una respuesta 200 CORRECTO en Postman y que la alerta aparece en la aplicación indicando que se ha recibido la acción ActionB en lugar de se ha recibido la acción ActionA.

  16. Cierre la aplicación PushDemo y, después, haga clic de nuevo en el botón Enviar en Postman .

  17. Compruebe que obtiene una respuesta 200 CORRECTO en Postman y que la notificación silenciosa no aparece en el área de notificación.

Solucionar problemas

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 las pruebas en la aplicación de API de Azure, compruebe que el servicio se esté ejecutando y que se haya implementado e iniciado sin errores.

Asegúrese de comprobar que ha especificado la dirección base correctamente en Postman o en la configuración de la aplicación móvil al realizar las pruebas mediante el cliente. La dirección base debe ser https://<api_name>.azurewebsites.net/ o https://localhost:5001/ al realizar las pruebas de forma local.

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

Asegúrese de registrarse de nuevo después de iniciar o detener una sesión de depuración. El depurador hará que se genere un nuevo token de Firebase. También se debe actualizar la instalación del centro de notificaciones.

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

Compruebe que se configura el encabezado de la solicitud apikey y que este valor coincide con el que había configurado para el servicio de back-end.

Si recibe este error al realizar pruebas localmente, asegúrese de que el valor de clave que definió en la configuración de cliente coincide con el valor de configuración de usuario Authentication:ApiKey que usa la API.

Si está realizando las pruebas con una aplicación de API, asegúrese de que el valor de clave en el archivo de configuración del cliente coincide con la configuración de la aplicación Authentication:ApiKey que está utilizando en la aplicación de API.

Nota

Si ha creado o cambiado este valor después de haber implementado el servicio de back-end, debe reiniciar el servicio para que surta efecto.

Si eligió no completar la sección Autenticación de clientes mediante una clave de API, asegúrese de que no aplicó el atributo Authorize a la clase NotificationsController.

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. Por ejemplo, los puntos de conexión deben ser:

  • [PUT]
  • [DELETE]
  • [POST]

O cuando se realizan las pruebas de manera local:

  • [PUT]
  • [DELETE]
  • [POST]

Al especificar la dirección base en la aplicación cliente, asegúrese de que finaliza con un /. La dirección base debe ser https://<api_name>.azurewebsites.net/ o https://localhost:5001/ al realizar las pruebas de forma local.

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 la respuesta http estableciendo un punto de interrupción para que inspeccione el valor de propiedad StatusCode en HttpResponse.

Revise las sugerencias de solución de problemas anteriores, si procede, según el código de estado.

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

Compruebe que el servicio de back-end funciona según lo previsto a través de Postman con la carga adecuada. Use la carga real creada por el código de cliente para la plataforma en cuestión.

Revise las secciones de configuración específicas de la plataforma para asegurarse de que no se ha omitido ningún paso. Compruebe que se están resolviendo los valores adecuados para las variables installation id y token de la plataforma adecuada.

No se puede resolver un identificador para 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.

Pasos siguientes

Ahora debe contar con una aplicación Xamarin.Forms básica conectada a un centro de notificaciones mediante un servicio back-end y ya puede enviar y recibir notificaciones.

Probablemente tendrá que adaptar el ejemplo que se usa en este tutorial para que se ajuste a su propio escenario. También se recomienda implementar un control de errores, una lógica de reintentos y un registro más sólidos.

Visual Studio App Center se puede incorporar rápidamente a las aplicaciones móviles, lo que proporciona análisis y diagnósticos útiles para la solución de problemas.