Tutorial: Envío de notificaciones push a aplicaciones flutter mediante Azure Notification Hubs a través de un servicio back-end
Descargar el de ejemplo
- de Xamarin.Forms
- flutter
- React Native
En este tutorial, usará de Azure Notification Hubs para enviar notificaciones push a una aplicación de Flutter dirigida a Android y de iOS.
Se usa un back-end api web de
Estas operaciones se controlan mediante el SDK de Notification Hubs para operaciones de back-end. Se proporciona más información sobre el enfoque general en la documentación de Registro desde el back-end de la aplicación.
Este tutorial le guía por los pasos siguientes:
- Configurar Servicios de notificaciones push y Azure Notification Hubs.
- Crear una aplicación back-end de API web de ASP.NET Core.
- Crear una aplicación Flutter multiplataforma.
- Configurar el proyecto nativo de Android para las notificaciones push.
- Configurar el proyecto nativo de iOS para las notificaciones push.
- Probar la solución.
Prerrequisitos
Para continuar, necesita lo siguiente:
- Una suscripción de Azure donde puede crear y administrar recursos.
- El kit de herramientas flutter
(junto con sus requisitos previos). de Visual Studio Code con los complementos Flutter y Dart de instalados. - CocoaPods instalado para administrar dependencias de biblioteca.
- La capacidad de ejecutar la aplicación en android (dispositivos físicos o emuladores) o iOS (solo dispositivos físicos).
Para Android, debe tener:
- Un dispositivo físico desbloqueado por el desarrollador o un emulador (ejecutando la API 26 y versiones posteriores con Google Play Services instalado).
Para iOS, debe tener:
- Una cuenta de desarrollador de Apple activa .
- Un dispositivo iOS físico que se registrado en la cuenta de desarrollador(ejecutando iOS 13.0 y versiones posteriores).
- Un certificado de desarrollo de
.p12 instalado en la cadena de claves le permite ejecutar una aplicación en un dispositivo físico .
Nota
El simulador de iOS no admite notificaciones remotas y, por tanto, se requiere un dispositivo físico al explorar este ejemplo en iOS. Sin embargo, no es necesario ejecutar la aplicación en android y iOS para completar este tutorial.
Puede seguir los pasos descritos en este ejemplo de primeros principios sin experiencia previa. Sin embargo, se beneficiará de tener conocimientos sobre los siguientes aspectos.
- portal para desarrolladores de Apple.
- ASP.NET Core.
- consola de Google Firebase.
- Microsoft Azure y Enviar notificaciones push a aplicaciones iOS mediante Azure Notification Hubs.
- Flutter y Dart para el desarrollo multiplataforma.
- kotlin y Swift para el desarrollo nativo de Android e iOS.
Los pasos proporcionados son específicos de macOS. Es posible seguir los pasos de
Configuración de Servicios de notificaciones push y Centro de notificaciones de Azure
En esta sección, configurará firebase Cloud Messaging (FCM) y Apple Push Notification Services (APNS). A continuación, cree y configure un centro de notificaciones para trabajar con esos servicios.
Creación de un proyecto de Firebase y habilitación de Firebase Cloud Messaging para Android
Inicie sesión en la consola de Firebase. Cree un nuevo proyecto de Firebase que escriba PushDemo como nombre del proyecto de .
Nota
Se generará un nombre único automáticamente. De forma predeterminada, se compone de una variante minúscula del nombre que proporcionó más un número generado separado por un guión. Puede cambiar esto si quiere proporcionarlo todavía es único globalmente.
Después de crear el proyecto, seleccione Agregar Firebase a la aplicación Android.
En la página Agregar Firebase a la aplicación Android, siga estos pasos.
En el nombre del paquete de Android, escriba un nombre para el paquete. Por ejemplo:
com.<organization_identifier>.<package_name>
.Seleccione Registrar aplicación.
Seleccione Descargar google-services.json. A continuación, guarde el archivo en una carpeta local para usarlo más adelante y seleccione Siguiente.
Seleccione Siguiente.
Seleccione continuar con la consola
Nota
Si la
botón Continuar con la consola no está habilitada, debido a la comprobación del de instalación de, elija Omitir este paso .
En la consola de Firebase, seleccione el engranaje del proyecto. A continuación, seleccione Configuración del proyecto.
Nota
Si no ha descargado el archivo google-services.json, puede descargarlo en esta página.
Cambie a la pestaña
Cloud Messaging en la parte superior. Copie y guarde la clave de Server para su uso posterior. Use este valor para configurar el centro de notificaciones.
Registro de la aplicación iOS para notificaciones push
Para enviar notificaciones push a una aplicación iOS, registre la aplicación con Apple y también regístrese para recibir notificaciones push.
Si aún 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 , identificadores & perfilesy, a continuación, seleccione Identificadores. Haga clic en + para registrar una nueva aplicación.
En la pantalla Registrar un nuevo identificador, seleccione el botón de radio Identificadores de aplicación. A continuación, seleccione Continuar.
Actualice los tres valores siguientes para la nueva aplicación y seleccione Continuar:
Descripción: escriba un nombre descriptivo para la aplicación.
id. de lote: escriba un identificador de lote del formulario com.<organization_identifier>.<product_name> tal como se mencionó en la Guía de distribución de aplicaciones de . En la captura de pantalla siguiente, el valor de
mobcat
se usa como identificador de organización y el valor PushDemo se usa como nombre del producto.Notificaciones push: active la opción Notificaciones push de en la sección Funcionalidades de . Esta acción genera el identificador de la aplicación y las solicitudes que confirma la información. Seleccione
Continue (Continuar) y, a continuación, seleccioneRegister (Registrar) para 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, identificadores & perfiles de.
En la página certificados de , identificadores & perfiles, en Identificadores, busque el elemento de línea Id. de aplicación que creó. A continuación, seleccione su fila para mostrar la pantalla Editar la configuración del identificador de aplicación.
Creación de un certificado para Notification Hubs
Se requiere un certificado para permitir que el centro de notificaciones funcione con apple Push Notification Services (APNS) y se puede proporcionar de una de estas dos maneras:
Creación de un certificado de inserción p12 que se puede cargar directamente en notification Hub (el enfoque original)
Creación de un certificado p8 que se puede usar para la autenticación basada en tokens (el enfoque más reciente y recomendado)
El enfoque más reciente tiene varias ventajas, tal y como se documenta en la autenticación basada en tokens (HTTP/2) para APNS. Se requieren menos pasos, pero también se exige para escenarios específicos. Sin embargo, se han proporcionado pasos para ambos enfoques, ya que cualquiera de los dos funcionará para los fines de este tutorial.
OPCIÓN 1: Creación de un certificado de inserción p12 que se puede cargar directamente en el Centro de notificaciones
En el equipo Mac, ejecute la herramienta Acceso a llaveros. Se puede abrir desde la carpeta Utilidades o la carpeta Otros del Launchpad.
Seleccione acceso a llaveros, expanda asistente para certificados y, a continuación, seleccione Solicitar un certificado de una entidad de certificación.
Nota
De forma predeterminada, Keychain Access selecciona el primer elemento de la lista. Esto puede ser un problema si está en la categoría certificados
y entidad de certificación de relaciones para desarrolladores de Apple Worldwide no es el primer elemento de la lista. Asegúrese de que tiene un elemento que no es clave o de que está seleccionada la entidad de certificación de relaciones para desarrolladores de Apple Worldwide clave, antes de generar la CSR (solicitud de firma de certificado).Seleccione eldirección de correo electrónico de usuario de
, escriba el valor Nombre común , asegúrese de especificar Guardado en disco y, a continuación, seleccioneContinuar . Deje dirección de correo electrónico de CA en blanco, ya que no es necesario.información de certificado esperada
Escriba un nombre para el archivo solicitud de firma de certificado (CSR) en Guardar como, seleccione la ubicación en Wherey, a continuación, seleccione Guardar.
Esta acción guarda el archivo CSR de en la ubicación seleccionada. La ubicación predeterminada es Desktop. Recuerde la ubicación elegida para el archivo.
De nuevo en la página certificados de , identificadores & perfiles del portal de aprovisionamiento de iOS , desplácese hacia abajo hasta la opción Notificaciones push activadas y, a continuación, seleccione Configurar para crear el certificado.
Aparece la ventana de certificados TLS/SSL del servicio de notificaciones push de Apple. Seleccione el botón
Create Certificate (Crear certificado) en la sección De certificados TLS/SSL de desarrollo de. Se muestra la pantalla Crear un nuevo certificado.
Nota
En este tutorial se usa un certificado de desarrollo. El mismo proceso se usa al registrar un certificado de producción. Solo tiene que asegurarse de usar el mismo tipo de certificado al enviar notificaciones.
Seleccione Elija archivo, vaya a la ubicación donde guardó el archivo CSR de y haga doble clic en el nombre del certificado para cargarlo. A continuación, seleccione Continuar.
Después de crear el certificado en el portal, seleccione el botón Descargar
. Guarde el certificado y recuerde la ubicación en la que se guarda. El certificado se descarga y guarda en el equipo en la carpeta descargas de .
Nota
De forma predeterminada, el certificado de desarrollo descargado se denomina aps_development.cer.
Haga doble clic en el certificado de inserción descargado aps_development.cer. Esta acción instala el nuevo certificado en la cadena de claves, como se muestra en la imagen siguiente:
Nota
Aunque el nombre del certificado puede ser diferente, el nombre tendrá el prefijo Apple Development iOS Push Services y tendrá asociado el identificador de lote adecuado.
En Acceso a llaveros,
Control Haga clic en el nuevo certificado de inserción que creó en la categoría certificados de. Seleccione Exportar, asigne un nombre al archivo, seleccione el formato de p12 y, a continuación, seleccione Guardar. Puede optar por proteger el certificado con una contraseña, pero una contraseña es opcional. Haga clic en Aceptar si desea omitir la creación de contraseñas. Anote el nombre de archivo y la ubicación del certificado p12 exportado. Se usan para habilitar la autenticación con APN.
Nota
El nombre y la ubicación del archivo p12 pueden ser diferentes de lo que se muestra en este tutorial.
OPCIÓN 2: Creación de un certificado p8 que se puede usar para la autenticación basada en tokens
Anote los detalles siguientes:
- de prefijo de id. de aplicación (id. de equipo)
- de identificador de lote
De nuevo en certificados de , identificadores & perfiles, haga clic en Claves.
Nota
Si ya tiene una clave configurada para APNS, puede volver a usar el certificado p8 que descargó justo después de crearlo. Si es así, puede omitir los pasos 3 a través de 5.
Haga clic en el botón + (o botón Crear una clave) para crear una nueva clave.
Proporcione un
nombre de clave adecuado y, a continuación, active la opción servicio de notificaciones push de Apple (APNS) y, a continuación, haga clic en Continuar , seguido deRegistrar en la siguiente pantalla.Haga clic en
Descargar y, a continuación, mueva el archivo p8 de(con el prefijo AuthKey_ ) a un directorio local seguro y, a continuación, haga clic enListo .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 a medida que se quita la copia del servidor.
En claves, haga clic en la clave que creó (o una clave existente si ha elegido usarla en su lugar).
Anote el valor id. de clave de
. Abra el certificado p8 en una aplicación adecuada de su elección, como Visual Studio Code. Anote el valor de clave (entre -----BEGIN PRIVATE KEY----- y -----END PRIVATE KEY-----).
-----BEGIN PRIVATE KEY-----
<key_value>
-----END PRIVATE KEY-----Nota
Este es el valor de token de que se usará más adelante para configurar Notification Hub.
Al final de estos pasos, debe tener la siguiente información para usarla más adelante en Configuración del centro de notificaciones con información de APNS:
- de id. de equipo (consulte el paso 1)
- de identificador de lote (consulte el paso 1)
- id. de clave (consulte el paso 7)
- valor de token (valor de clave p8 obtenido en el paso 8)
Creación de un perfil de aprovisionamiento para la aplicación
Vuelva al portal de aprovisionamiento de iOS , seleccione Certificados, Identificadores & Perfiles, seleccione Perfiles en el menú de la izquierda y, a continuación, seleccione + para crear un nuevo perfil. Aparece la pantalla Registrar un nuevo perfil de aprovisionamiento.
Seleccione desarrollo de aplicaciones de iOS en Development como tipo de perfil de aprovisionamiento y, a continuación, seleccione Continuar.
A continuación, seleccione el identificador de aplicación que creó en la lista desplegable Id. de aplicación y seleccione Continuar.
En la ventana Seleccionar certificados, seleccione el certificado de desarrollo que usa para la firma de código y seleccione Continuar.
Nota
Este certificado no es el certificado de inserción que creó en el paso anterior . Este es el certificado de desarrollo. Si no existe, debe crearlo, ya que se trata de un requisito previo para este tutorial. Los certificados de desarrollador se pueden crear en
portal para desarrolladores de Apple mediante Xcode o enVisual Studio .Vuelva a la página Certificados , Identificadores & Perfiles, seleccione Perfiles en el menú de la izquierda y, a continuación, seleccione + para crear un nuevo perfil. Aparece la pantalla Registrar un nuevo perfil de aprovisionamiento.
En la ventana seleccionar certificados
, seleccione el certificado de desarrollo que creó. A continuación, seleccione Continuar. A continuación, seleccione los dispositivos que se van a usar para realizar pruebas y seleccione Continuar.
Por último, elija un nombre para el perfil en Nombre del perfil de aprovisionamientoy seleccione Generar.
Cuando se crea el nuevo perfil de aprovisionamiento, seleccione Descargar. Recuerde la ubicación a la que se guarda.
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 de inserción p12 o una autenticación basada en tokens. Si desea usar un centro de notificaciones que ya ha creado, puede ir directamente al paso 5.
Inicie sesión en Azure.
Haga clic en Crear un recursoy, después, busque y elija centro de notificaciones y haga clic en Crear.
Actualice los campos siguientes y, a continuación, haga clic en Crear:
DETALLES BÁSICOS
Suscripción: Elija la suscripción de destino en la lista desplegable.
grupo de recursos: Crear un nuevo grupo de recursos (o elegir uno existente)DETALLES DEL ESPACIO DE NOMBRES
Namespace del Centro de notificaciones: Escriba un nombre único global para el espacio de nombres del centro de notificaciones de
Nota
Asegúrese de que la opción Crear nueva esté seleccionada para este campo.
NOTIFICATION HUB DETAILS
Centro de notificaciones: Escriba un nombre para el centro de notificaciones de
Ubicación: Elegir una ubicación adecuada en la lista desplegable
plan de tarifa: mantener la opción predeterminada gratisNota
A menos que haya alcanzado el número máximo de concentradores en el nivel gratis.
Una vez que se ha aprovisionado el del centro de notificaciones de
, vaya a ese recurso. Vaya al nuevo centro de notificaciones de .
Seleccione directivas de acceso en la lista (en ADMINISTRAR).
Anote los valores nombre de directiva de
junto con sus valores de cadena de conexión correspondientes .
Configuración del Centro de notificaciones con información de APNS
En Notification Services, seleccione Apple, a continuación, siga los pasos adecuados según el enfoque que eligió anteriormente en la sección Creación de un certificado para Notification Hubs.
Nota
Usa el de producción de
OPCIÓN 1: Uso de un certificado de inserción .p12
Seleccione certificado.
Seleccione el icono de archivo.
Seleccione el archivo .p12 que exportó anteriormente y, a continuación, seleccione Abrir.
Si es necesario, especifique la contraseña correcta.
Seleccione modo de espacio aislado.
Seleccione Guardar.
OPCIÓN 2: Uso de la autenticación basada en tokens
Seleccione token.
Escriba los valores siguientes que adquirió anteriormente:
- de identificador de clave de
- de identificador de lote
- id. de equipo de
- de token de
- de identificador de clave de
Elija espacio aislado.
Seleccione Guardar.
Configuración del centro de notificaciones con información de FCM
- Seleccione Google (GCM/FCM) en la sección configuración de del menú izquierdo.
- Escriba la clave del servidor anotó en la Google Firebase Console.
- Seleccione Guardar en la barra de herramientas.
Creación de una aplicación back-end de API web de ASP.NET Core
En esta sección, creará el back-end de ASP.NET Core Web API para controlar de registro de dispositivos y el envío de notificaciones a la aplicación móvil flutter.
Creación de un proyecto web
En visual Studio, seleccione Archivo>Nueva solución.
Seleccione .NET Core>App>ASP.NET Core>API>Siguiente.
En el cuadro de diálogo Configure your new ASP.NET Core Web API ,seleccione Target Framework de .NET Core 3.1.
Escriba
PushDemoApi para el Nombre del proyecto de y, a continuación, seleccione Crear .Inicie la depuración (Comando + Escriba) para probar la aplicación con plantilla.
Nota
La aplicación con plantilla está configurada para usar el WeatherForecastController
como launchUrl . Esto se establece en Propiedades>launchSettings.json. Si se le solicita un certificado de desarrollo no válido encontrado mensaje:
Haga clic en Sí para aceptar ejecutar la herramienta "dotnet dev-certs https" para corregirlo. La herramienta "dotnet dev-certs https" le pedirá que escriba una contraseña para el certificado y la contraseña de la cadena de claves.
Haga clic en Sí cuando se le pida que Instalar y confíe en el nuevo certificadoy escriba la contraseña de la cadena de claves.
Expanda la carpeta Controladores de
y elimine WeatherForecastController.cs .Elimine WeatherForecast.cs.
Configure los valores de configuración local mediante la herramienta administrador de secretos de . Desacoplar los secretos de la solución garantiza que no terminan en el control de código fuente. Abra Terminal vaya al directorio del archivo del proyecto y ejecute los comandos siguientes:
dotnet user-secrets init dotnet user-secrets set "NotificationHub:Name" <value> dotnet user-secrets set "NotificationHub:ConnectionString" <value>
Reemplace los valores de marcador de posición por su propio nombre del centro de notificaciones y los valores de cadena de conexión. Los anotó en la sección crear un centro de notificaciones. De lo contrario, puede buscarlos en Azure.
NotificationHub:Name:
ConsulteName (Nombre) en el resumen de de Essentials en la parte superior de Información general .NotificationHub:ConnectionString:
ConsulteDefaultFullSharedAccessSignature en directivas de acceso Nota
En escenarios de producción, puede ver opciones como Azure KeyVault 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 de Azure App Service
.
Autenticación de clientes mediante una clave de API (opcional)
Las claves de API no son tan seguras como tokens, pero bastarán con los fines de este tutorial. Una clave de API se puede configurar fácilmente a través del middleware ASP.NET.
Agregue la clave de API de a los valores de configuración local.
dotnet user-secrets set "Authentication:ApiKey" <value>
Nota
Debe reemplazar el valor del marcador de posición por el suyo propio y anotarlo.
Control + Haga 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 .
Control Haga clic en en la carpeta autenticación dey elija Nuevo archivo... en el menú Agregar. Seleccione
General Clase vacía , escribaApiKeyAuthOptions.cs para elNombre dey haga clic en Nuevo agregar 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; } } }
Agregue otra clase vacía a la carpeta Authentication denominada ApiKeyAuthHandler.csy 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.
Agregue otra
de clase vacía a la carpeta autenticación de denominada ApiKeyAuthenticationBuilderExtensions.cs y 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 de middleware en Startup.cs lo que hace que sea más legible y, por lo general, más fácil de seguir.
En Startup.cs, actualice el método ConfigureServices para configurar la autenticación de clave de API debajo de la llamada a los servicios de . AddControllers método.
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); }
Todavía en
Startup.cs , actualice el métodoConfigure para llamar al UseAuthenticationy métodos de extensión UseAuthorization en laIApplicationBuilder de la aplicación . Asegúrese de que se llama a esos métodos después de useRouting y antes de aplicación. 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
Al llamar a UseAuthentication se registra el middleware que usa los esquemas de autenticación registrados anteriormente (de ConfigureServices). Se debe llamar a esto antes de cualquier middleware que dependa de los usuarios que se autentiquen.
Adición de dependencias y configuración de servicios
ASP.NET Core admite el patrón de diseño de software
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 está disponible a través de una abstracción adecuada.
Control Haga clic en en la carpeta Dependencias dey elija Administrar paquetes NuGet... .Busque Microsoft.Azure.NotificationHubs y asegúrese de que está activada.
Haga clic en Agregar paquetesy, a continuación, haga clic en Aceptar cuando se le pida que acepte los términos de licencia.
Control Haga clic en en el proyectoPushDemoApi , elijaNueva carpeta en el menú Agregary, a continuación, haga clic en Agregar medianteModels como nombre de carpeta. Control Haga clic en en la carpeta modelos dey elija Nuevo archivo... en el menú Agregar. Seleccione
clase vacía , escribaPushTemplates.cs para elNombre dey, a continuación, haga clic en Nuevo agregar 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 token para las notificaciones genéricas y silenciosas requeridas por este escenario. Las cargas se definen fuera del instalación de
para permitir la experimentación sin tener que actualizar las instalaciones existentes a través del servicio. El control de los cambios en las instalaciones de esta manera está fuera del ámbito de este tutorial. Para producción, considere plantillas personalizadas. Agregue otra clase vacía a la carpeta Models denominada DeviceInstallation.csy 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>(); } }
Agregue otra clase vacía a la carpeta Models denominada NotificationRequest.csy 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; } } }
Agregue otra
clase vacía a la carpeta Modelsdenominada NotificationHubOptions.cs y 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; } } }
Agregue una nueva carpeta al proyecto de PushDemoApi denominado Services.
Agregue una interfaz vacía
a la carpeta Services denominada INotificationService.cs y 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); } }
Agregue una
clase vacía a la carpeta deServices denominada NotificationHubsService.cs y agregue el código siguiente para implementar la interfaz deINotificationService :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 proporcionada para SendTemplateNotificationAsync está limitada a 20 etiquetas. Se limita a 6 para la mayoría de los operadores, pero la expresión solo contiene solicitudes organizativas (||) en este caso. Si hay más de 20 etiquetas en la solicitud, deben dividirse en varias solicitudes. Consulte la documentación
expresiones de enrutamiento y etiqueta para obtener más información. En
Startup.cs , actualice el métodoConfigureServices para agregar el de NotificationHubsService como una implementación singleton de INotificationService .using PushDemoApi.Models; using PushDemoApi.Services; public void ConfigureServices(IServiceCollection services) { ... services.AddSingleton<INotificationService, NotificationHubService>(); services.AddOptions<NotificationHubOptions>() .Configure(Configuration.GetSection("NotificationHub").Bind) .ValidateDataAnnotations(); }
Creación de la API de notificaciones
Control Haga clic en en la carpeta Controladores dey elija Nuevo archivo... en el menú Agregar. Seleccione
ASP.NET Core Clase de controlador de API web , escribaNotificationsController para elNombre dey haga clic en Nuevo .Nota
Si sigue con visual Studio 2019, elija el controlador de API de con acciones de lectura y escritura plantilla.
Agregue los siguientes espacios de nombres a la parte superior 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;
Actualice el controlador con plantilla para que derive de ControllerBase y esté decorado con el atributo ApiController.
[ApiController] [Route("api/[controller]")] public class NotificationsController : ControllerBase { // Templated methods here }
Nota
La clase base Controller proporciona compatibilidad con vistas, pero esto no es necesario en este caso, por lo que ControllerBase se puede usar en su lugar. Si sigue con visual Studio 2019, puede omitir este paso.
Si eligió completar la sección Autenticar clientes de
mediante una de clave de API, también debe decorar el NotificationsController decon el atributo Authorize . [Authorize]
Actualice el constructor para aceptar la instancia registrada de INotificationService como argumento y asignarla a un miembro de solo lectura.
readonly INotificationService _notificationService; public NotificationsController(INotificationService notificationService) { _notificationService = notificationService; }
En
launchSettings.json (dentro de la carpeta Propiedades de), cambie el launchUrl dea api/notifications para que coincida con la dirección URL especificada en el atributoRegistrationsController Route .Inicie la depuración (
Command Enter ) para validar que la aplicación está trabajando con el nuevo NotificationsController dey devuelve un estado de no autorizado 401. En una nueva pestaña Postman, establezca la solicitud en GET. Escriba la dirección siguiente reemplazando el marcador de posición
por el applicationUrl de httpsapplicationUrl que se encuentra en Properties launchSettings.json .<applicationUrl>/api/notifications
Nota
El applicationUrl de
debe ser " " para el perfil predeterminado. Si usa IIS (valor predeterminado en visual Studio 2019 en Windows), debe usar el applicationUrl especificado en el elemento iisSettings en su lugar. Recibirá una respuesta 404 si la dirección es incorrecta. Si eligió completar la sección autenticar clientes de mediante una clave de API, asegúrese de configurar los encabezados de solicitud para incluir el valor de apikey de .
Llave Valor apikey <your_api_key> Haga clic en el botón enviar
. Reemplace los métodos de clase con plantilla en NotificationsController.cs por el código siguiente.
[HttpPut] [Route("installations")] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest)] [ProducesResponseType((int)HttpStatusCode.UnprocessableEntity)] public async Task<IActionResult> UpdateInstallation( [Required]DeviceInstallation deviceInstallation) { var success = await _notificationService .CreateOrUpdateInstallationAsync(deviceInstallation, HttpContext.RequestAborted); if (!success) return new UnprocessableEntityResult(); return new OkResult(); } [HttpDelete()] [Route("installations/{installationId}")] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest)] [ProducesResponseType((int)HttpStatusCode.UnprocessableEntity)] public async Task<ActionResult> DeleteInstallation( [Required][FromRoute]string installationId) { 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 creará una de aplicación de API de
Inicie sesión en Azure Portal.
Haga clic en Crear un recursoy, después, busque y elija API Appy haga clic en Crear.
Actualice los campos siguientes y haga clic en Crear.
Nombre de la aplicación:
Escriba un nombre único global para API AppSuscripción:
Elija el mismo destino suscripción creó el centro de notificaciones.grupo de recursos de :
Elija la misma grupo de recursos creó el centro de notificaciones.plan o ubicación de App Service:
Creación de un nuevo plan de App ServiceNota
Cambie de la opción predeterminada a un plan que incluya compatibilidad con ssl. De lo contrario, deberá realizar los pasos adecuados al trabajar con la aplicación móvil para evitar que solicitudes http se bloqueen.
Application Insights:
Mantenga la opción sugerida (se creará un nuevo recurso con ese nombre) o elija un recurso existente.Una vez que se ha aprovisionado la aplicación de API , vaya a ese recurso.
Anote la propiedad URL de
en el resumen de de deEssentials en la parte superior de la Información general . Esta dirección URL es el punto de conexión de back-endque se usará más adelante en este tutorial. Nota
La dirección URL usa el nombre de la aplicación de API que especificó anteriormente, con el formato
https://<app_name>.azurewebsites.net
.Seleccione configuración en la lista (en Configuración).
Para cada una de las opciones siguientes, haga clic en
Nueva configuración de aplicación para escribir el Nombre dey un valor de 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 de usuario. Debería poder copiarlos. La configuración Authentication:ApiKey solo es necesaria si decide completar la sección Autenticar clientes con una clave de API. En escenarios de producción, puede ver opciones como Azure KeyVault. Estos se han agregado como configuración de la aplicación para simplificar en este caso.
Una vez agregada toda la configuración de la aplicación, haga clic en Guardary, a continuación, Continuar.
Publicación del servicio back-end
A continuación, implementará la aplicación en la aplicación de API para que sea accesible desde todos los dispositivos.
Nota
Los pasos siguientes son específicos de visual Studio para Mac. Si sigue con visual Studio 2019 en Windows, el flujo de publicación será diferente. Consulte Publicar en Azure App Service en Windows.
Cambie la configuración de de depuración a Release si aún no lo ha hecho.
Control Haga clic en el proyectoPushDemoApi y, a continuación, elijaPublicar en Azure... en el menú Publicar. Siga el flujo de autenticación si se le pide que lo haga. Use la cuenta que usó en la sección anterior crear la aplicación de API.
Seleccione la aplicación de API de Azure App Service creó anteriormente en la lista como destino de publicación y, a continuación, haga clic en Publicar.
Una vez completado el asistente, publica la aplicación en Azure y, a continuación, abre la aplicación. Tome nota de la dirección URL de si aún no lo ha hecho. Esta dirección URL es el punto de conexión de back-end de que se usa más adelante en este tutorial.
Validación de la API publicada
En Postman abrir 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 publicar el 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/
Si eligió completar la sección autenticar clientes de mediante una clave de API, asegúrese de configurar los encabezados de solicitud para incluir el valor de apikey de .
Llave Valor apikey <your_api_key> Elija la opción
sin formato para lacuerpo de y, después, elija JSON en la lista de opciones de formato y, a continuación, incluya algún marcador de posicióncontenido de JSON :{}
Haga clic en Enviar.
Nota
Debe recibir un 422 UnprocessableEntity estado del servicio.
Realice los pasos del 1 al 4 de nuevo, pero esta vez especificando el punto de conexión de solicitudes para validar 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 Flutter multiplataforma
En esta sección, creará una Flutter aplicación móvil que implementa notificaciones push de forma multiplataforma.
Permite registrar y anular el registro desde un centro de notificaciones a través del servicio back-end que creó.
Se muestra una alerta cuando se especifica una acción y la aplicación está en primer plano. De lo contrario, las notificaciones aparecen en el Centro de notificaciones.
Nota
Normalmente, realizaría las acciones de registro (y desregistro) durante el punto adecuado del ciclo de vida de la aplicación (o como parte de la experiencia de primera ejecución quizás) sin entradas explícitas de registro o registro del usuario. Sin embargo, este ejemplo requerirá una entrada explícita del usuario para permitir que esta funcionalidad se explore y pruebe más fácilmente.
Creación de la solución Flutter
Abra una nueva instancia de visual Studio Code.
Abra el paleta de comandos de
( Mayús Comando P ).Seleccione el comando Flutter: New Project y presione Entrar.
Escriba
push_demo para el nombre del proyecto dey, a continuación, seleccione una ubicación del proyecto . Cuando se le pida que lo haga, elija Obtener paquetes.
Control Haga clic en en la carpeta dekotlin (en aplicación src principal ), elijaReveal en Finder . A continuación, cambie el nombre de las carpetas secundarias (en la carpeta kotlin) acom
,<your_organization>
ypushdemo
respectivamente.Nota
Al usar la plantilla de
Visual Studio Code, estas carpetas se usan de forma predeterminada para com ,ejemplo , . Suponiendo que mobcat se usa para la organización , la estructura de carpetas debería aparecer de forma indicativa como:project_name - kotlin
- COM
- mobcat
- pushdemo
- mobcat
- COM
- kotlin
De nuevo en
de Visual Studio Code, actualice el valor applicationId de en aplicación android build.gradle para. Nota
Debe usar su propio nombre de organización para el marcador de posición <your_organization>. Por ejemplo, el uso de mobcat como la organización dará como resultado un nombre de paquete valor de com.mobcat.pushdemo.
Actualice el atributo del paquete de
en los archivos de AndroidManifest.xml , ensrc debug ,src principal ysrc profile respectivamente. Asegúrese de que los valores coinciden con el applicationId que usó en el paso anterior.<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.<your_organization>.pushdemo>"> ... </manifest>
Actualice el atributo
android:label
en el archivo de AndroidManifest.xml en src> principal a PushDemo. A continuación, agregue el atributoandroid:allowBackup
, directamente enandroid:label
, estableciendo su valor en false.<application android:name="io.flutter.app.FlutterApplication" android:label="PushDemo" android:allowBackup="false" android:icon="@mipmap/ic_launcher"> ... </application>
Abra el archivo build.gradle de nivel de aplicación
( aplicación android debuild.gradle ), actualice el compileSdkVersion de(desde la sección android ) para usar api29 . A continuación, actualice los valores deminSdkVersion ytargetSdkVersion (en la sección defaultConfig), a 26 y29 respectivamente.Nota
Solo se admiten los dispositivos que ejecutan nivel de API 26 y versiones posteriores para los fines de este tutorial, pero puede ampliarlos para admitir dispositivos que ejecutan versiones anteriores.
Control + Haga clic en en la carpeta ios y elija Abrir en Xcode.
En
Xcode , haga clic en Runner (el xcodeproj deen la parte superior, no en la carpeta). A continuación, seleccione el destino ejecutor de y seleccione la pestaña General de . Con la configuración de compilación todas las seleccionada, actualice el identificador de agrupación dea . Nota
Debe usar su propio nombre de organización para el marcador de posición <your_organization>. Por ejemplo, el uso de
mobcat como la organización dará como resultado unidentificador de agrupación de com.mobcat.PushDemo .Haga clic en
Info.plist y, a continuación, actualice el valor nombre del paquete de a PushDemo Cierre Xcode y vuelva a visual Studio Code.
De nuevo en visual Studio Code, abra pubspec.yaml, agregue el http y flutter_secure_storagepaquetes Dart como dependencias. A continuación, guarde el archivo y haga clic en Obtener paquetes cuando se le pida que lo haga.
dependencies: flutter: sdk: flutter http: ^0.12.1 flutter_secure_storage: ^3.3.3
En
Terminal , cambie el directorio a la carpetaios (para el proyecto flutter). A continuación, ejecute el comando pod install para instalar nuevos pods (requeridos por el paquete flutter_secure_storage). Control Haga clic en en la carpeta lib dey elija Nuevo archivo en el menú conmain_page.dart como nombre de archivo. A continuación, agregue el código siguiente.import 'package:flutter/material.dart'; class MainPage extends StatefulWidget { @override _MainPageState createState() => _MainPageState(); } class _MainPageState extends State<MainPage> { @override Widget build(BuildContext context) { return Scaffold( body: SafeArea( child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: <Widget>[], ) ) ); } }
En main.dart, reemplace el código con plantilla por lo siguiente.
import 'package:flutter/material.dart'; import 'package:push_demo/main_page.dart'; final navigatorKey = GlobalKey<NavigatorState>(); void main() => runApp(MaterialApp(home: MainPage(), navigatorKey: navigatorKey));
En Terminal, compile y ejecute la aplicación en cada plataforma de destino para probar las ejecuciones de la aplicación con plantilla en los dispositivos. Asegúrese de que los dispositivos compatibles están conectados.
flutter run
Implementación de los componentes multiplataforma
Control + Haga clic en en la carpeta lib y, a continuación, elija Nueva carpeta en el menú con modelos de como nombre de carpeta .
Control Haga clic en en la carpeta modelos dey elija Nuevo archivo en el menú condevice_installation.dart como nombre de archivo. A continuación, agregue el código siguiente.class DeviceInstallation { final String deviceId; final String platform; final String token; final List<String> tags; DeviceInstallation(this.deviceId, this.platform, this.token, this.tags); DeviceInstallation.fromJson(Map<String, dynamic> json) : deviceId = json['installationId'], platform = json['platform'], token = json['pushChannel'], tags = json['tags']; Map<String, dynamic> toJson() => { 'installationId': deviceId, 'platform': platform, 'pushChannel': token, 'tags': tags, }; }
Agregue un nuevo archivo a la carpeta modelos
denominada push_demo_action.dart definir la enumeración de acciones que se admiten en este ejemplo.enum PushDemoAction { actionA, actionB, }
Agregue una nueva carpeta al proyecto denominado services agregue un nuevo archivo a esa carpeta denominada device_installation_service.dart con la siguiente implementación.
import 'package:flutter/services.dart'; class DeviceInstallationService { static const deviceInstallation = const MethodChannel('com.<your_organization>.pushdemo/deviceinstallation'); static const String getDeviceIdChannelMethod = "getDeviceId"; static const String getDeviceTokenChannelMethod = "getDeviceToken"; static const String getDevicePlatformChannelMethod = "getDevicePlatform"; Future<String> getDeviceId() { return deviceInstallation.invokeMethod(getDeviceIdChannelMethod); } Future<String> getDeviceToken() { return deviceInstallation.invokeMethod(getDeviceTokenChannelMethod); } Future<String> getDevicePlatform() { return deviceInstallation.invokeMethod(getDevicePlatformChannelMethod); } }
Nota
Debe usar su propio nombre de organización para el marcador de posición <your_organization>. Por ejemplo, el uso de mobcat como la organización dará como resultado un MethodChannel nombre de com.mobcat.pushdemo/deviceinstallation.
Esta clase encapsula el trabajo con la plataforma nativa subyacente para adquirir los detalles de instalación de dispositivos necesarios. Una MethodChannel facilita la comunicación asincrónica bidireccional con las plataformas nativas subyacentes. El homólogo específico de la plataforma para este canal se creará en los pasos posteriores.
Agregue otro archivo a esa carpeta denominada notification_action_service.dart con la siguiente implementación.
import 'package:flutter/services.dart'; import 'dart:async'; import 'package:push_demo/models/push_demo_action.dart'; class NotificationActionService { static const notificationAction = const MethodChannel('com.<your_organization>.pushdemo/notificationaction'); static const String triggerActionChannelMethod = "triggerAction"; static const String getLaunchActionChannelMethod = "getLaunchAction"; final actionMappings = { 'action_a' : PushDemoAction.actionA, 'action_b' : PushDemoAction.actionB }; final actionTriggeredController = StreamController.broadcast(); NotificationActionService() { notificationAction .setMethodCallHandler(handleNotificationActionCall); } Stream get actionTriggered => actionTriggeredController.stream; Future<void> triggerAction({action: String}) async { if (!actionMappings.containsKey(action)) { return; } actionTriggeredController.add(actionMappings[action]); } Future<void> checkLaunchAction() async { final launchAction = await notificationAction.invokeMethod(getLaunchActionChannelMethod) as String; if (launchAction != null) { triggerAction(action: launchAction); } } Future<void> handleNotificationActionCall(MethodCall call) async { switch (call.method) { case triggerActionChannelMethod: return triggerAction(action: call.arguments as String); default: throw MissingPluginException(); break; } } }
Nota
Esto se usa como un mecanismo sencillo para centralizar el control de las acciones de notificación para que se puedan controlar de forma multiplataforma mediante una enumeración fuertemente tipada. El servicio permite que la plataforma nativa subyacente desencadene una acción, cuando se especifica una en la carga de notificación. También permite que el código común compruebe retrospectivamente si se especificó una acción durante el inicio de la aplicación una vez que Flutter esté listo para procesarlo. Por ejemplo, cuando la aplicación se inicia pulsando en una notificación desde el centro de notificaciones.
Agregue un nuevo archivo a la carpeta
services denominada notification_registration_service.dart con la siguiente implementación.import 'dart:convert'; import 'package:flutter/services.dart'; import 'package:http/http.dart' as http; import 'package:push_demo/services/device_installation_service.dart'; import 'package:push_demo/models/device_installation.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; class NotificationRegistrationService { static const notificationRegistration = const MethodChannel('com.<your_organization>.pushdemo/notificationregistration'); static const String refreshRegistrationChannelMethod = "refreshRegistration"; static const String installationsEndpoint = "api/notifications/installations"; static const String cachedDeviceTokenKey = "cached_device_token"; static const String cachedTagsKey = "cached_tags"; final deviceInstallationService = DeviceInstallationService(); final secureStorage = FlutterSecureStorage(); String baseApiUrl; String apikey; NotificationRegistrationService(this.baseApiUrl, this.apikey) { notificationRegistration .setMethodCallHandler(handleNotificationRegistrationCall); } String get installationsUrl => "$baseApiUrl$installationsEndpoint"; Future<void> deregisterDevice() async { final cachedToken = await secureStorage.read(key: cachedDeviceTokenKey); final serializedTags = await secureStorage.read(key: cachedTagsKey); if (cachedToken == null || serializedTags == null) { return; } var deviceId = await deviceInstallationService.getDeviceId(); if (deviceId.isEmpty) { throw "Unable to resolve an ID for the device."; } var response = await http .delete("$installationsUrl/$deviceId", headers: {"apikey": apikey}); if (response.statusCode != 200) { throw "Deregister request failed: ${response.reasonPhrase}"; } await secureStorage.delete(key: cachedDeviceTokenKey); await secureStorage.delete(key: cachedTagsKey); } Future<void> registerDevice(List<String> tags) async { try { final deviceId = await deviceInstallationService.getDeviceId(); final platform = await deviceInstallationService.getDevicePlatform(); final token = await deviceInstallationService.getDeviceToken(); final deviceInstallation = DeviceInstallation(deviceId, platform, token, tags); final response = await http.put(installationsUrl, body: jsonEncode(deviceInstallation), headers: {"apikey": apikey, "Content-Type": "application/json"}); if (response.statusCode != 200) { throw "Register request failed: ${response.reasonPhrase}"; } final serializedTags = jsonEncode(tags); await secureStorage.write(key: cachedDeviceTokenKey, value: token); await secureStorage.write(key: cachedTagsKey, value: serializedTags); } on PlatformException catch (e) { throw e.message; } catch (e) { throw "Unable to register device: $e"; } } Future<void> refreshRegistration() async { final currentToken = await deviceInstallationService.getDeviceToken(); final cachedToken = await secureStorage.read(key: cachedDeviceTokenKey); final serializedTags = await secureStorage.read(key: cachedTagsKey); if (currentToken == null || cachedToken == null || serializedTags == null || currentToken == cachedToken) { return; } final tags = jsonDecode(serializedTags); return registerDevice(tags); } Future<void> handleNotificationRegistrationCall(MethodCall call) async { switch (call.method) { case refreshRegistrationChannelMethod: return refreshRegistration(); default: throw MissingPluginException(); break; } } }
Nota
Esta clase encapsula el uso del DeviceInstallationService y las solicitudes al servicio back-end para realizar las acciones de registro, registro y actualización necesarias. El argumento apiKey
solo es necesario si eligió completar la sección autenticar clientes mediante una clave de API sección.Agregue un nuevo archivo a la carpeta lib
denominada config.dart con la siguiente implementación.class Config { static String apiKey = "API_KEY"; static String backendServiceEndpoint = "BACKEND_SERVICE_ENDPOINT"; }
Nota
Esto se usa como una manera sencilla de definir secretos de aplicación. Reemplace los valores de marcador de posición por los suyos propios. Debe haber tomado nota de estos al compilar el servicio back-end. La dirección URL de la aplicación de API debe ser
https://<api_app_name>.azurewebsites.net/
. El miembro apiKeysolo es necesario si eligió completar la sección autenticación de clientes mediante una clave de API .Asegúrese de agregarlo al archivo gitignore para evitar confirmar estos secretos en el control de código fuente.
Implementación de la interfaz de usuario multiplataforma
En
main_page.dart , reemplace la función de compilaciónpor lo siguiente. @override Widget build(BuildContext context) { return Scaffold( body: Padding( padding: const EdgeInsets.symmetric(horizontal: 20.0, vertical: 40.0), child: Column( mainAxisAlignment: MainAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.stretch, children: <Widget>[ FlatButton( child: Text("Register"), onPressed: registerButtonClicked, ), FlatButton( child: Text("Deregister"), onPressed: deregisterButtonClicked, ), ], ), ), ); }
Agregue las importaciones necesarias a la parte superior del archivo main_page.dart.
import 'package:push_demo/services/notification_registration_service.dart'; import 'config.dart';
Agregue un campo a la clase
_MainPageState para almacenar una referencia a laNotificationRegistrationService de. final notificationRegistrationService = NotificationRegistrationService(Config.backendServiceEndpoint, Config.apiKey);
En la clase _MainPageState, implemente los controladores de eventos para los eventos Register y Deregister botones onPressed. Llame a los métodos Register/Deregister correspondientes y muestre una alerta para indicar el resultado.
void registerButtonClicked() async { try { await notificationRegistrationService.registerDevice(List<String>()); await showAlert(message: "Device registered"); } catch (e) { await showAlert(message: e); } } void deregisterButtonClicked() async { try { await notificationRegistrationService.deregisterDevice(); await showAlert(message: "Device deregistered"); } catch (e) { await showAlert(message: e); } } Future<void> showAlert({ message: String }) async { return showDialog<void>( context: context, barrierDismissible: false, builder: (BuildContext context) { return AlertDialog( title: Text('PushDemo'), content: SingleChildScrollView( child: ListBody( children: <Widget>[ Text(message), ], ), ), actions: <Widget>[ FlatButton( child: Text('OK'), onPressed: () { Navigator.of(context).pop(); }, ), ], ); }, ); }
Ahora, en main.dart, asegúrese de que las siguientes importaciones están presentes en la parte superior del archivo.
import 'package:flutter/material.dart'; import 'package:push_demo/models/push_demo_action.dart'; import 'package:push_demo/services/notification_action_service.dart'; import 'package:push_demo/main_page.dart';
Declare una variable para almacenar la referencia a una instancia de NotificationActionService e inicialícela.
final notificationActionService = NotificationActionService();
Agregue funciones para controlar la visualización de una alerta cuando se desencadena una acción.
void notificationActionTriggered(PushDemoAction action) { showActionAlert(message: "${action.toString().split(".")[1]} action received"); } Future<void> showActionAlert({ message: String }) async { return showDialog<void>( context: navigatorKey.currentState.overlay.context, barrierDismissible: false, builder: (BuildContext context) { return AlertDialog( title: Text('PushDemo'), content: SingleChildScrollView( child: ListBody( children: <Widget>[ Text(message), ], ), ), actions: <Widget>[ FlatButton( child: Text('OK'), onPressed: () { Navigator.of(context).pop(); }, ), ], ); }, ); }
Actualice la función principal de
para observar la acción notificationActionService y comprobar las acciones capturadas durante el inicio de la aplicación.secuencia de void main() async { runApp(MaterialApp(home: MainPage(), navigatorKey: navigatorKey,)); notificationActionService.actionTriggered.listen((event) { notificationActionTriggered(event as PushDemoAction); }); await notificationActionService.checkLaunchAction(); }
Nota
Esto es simplemente para demostrar la recepción y propagación de acciones de notificación push. Normalmente, estos se controlarían silenciosamente, por ejemplo, navegando a una vista específica o actualizando algunos datos en lugar de mostrar una alerta en este caso.
Configuración del proyecto nativo de Android para notificaciones push
Adición del archivo JSON de Google Services
Control Haga clic en en la carpeta android dey, a continuación, elija Abrir en Android Studio . A continuación, cambie a la vista Project (si aún no lo está).Busque el archivo de google-services.json que descargó anteriormente al configurar el proyecto PushDemo en la consola de Firebase. A continuación, arrástrelo al directorio raíz del módulo
aplicación de ( android android app ).
Configuración de los permisos y opciones de compilación
Cambie la vista de
Project a Android .Abra
AndroidManifest.xml y agregue los permisosINTERNET yREAD_PHONE_STATE después del elemento de aplicaciónantes de la etiqueta de cierre . <manifest> <application>...</application> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.READ_PHONE_STATE" /> </manifest>
Adición de los SDK de Firebase
En android Studio, abra el archivo build.gradle de nivel de proyecto (scripts de Gradle>build.gradle (Project: android)). y asegúrese de que tiene la ruta de clase "com.google.gms:google-services" en el nodo dependencias de
. buildscript { repositories { // Check that you have the following line (if not, add it): google() // Google's Maven repository } dependencies { // ... // Add the following line: classpath 'com.google.gms:google-services:4.3.3' // Google Services plugin } } allprojects { // ... repositories { // Check that you have the following line (if not, add it): google() // Google's Maven repository // ... } }
Nota
Asegúrese de hacer referencia a la versión más reciente según las instrucciones proporcionadas en el de consola de Firebase de
al crear la Android Project .En el archivo build.gradle de
nivel de aplicación ( scripts de Gradle build.gradle (módulo: aplicación) ), aplique el complemento Gradle deServicios de Google . Aplique el complemento justo encima del nodo de android.// ... // Add the following line: apply plugin: 'com.google.gms.google-services' // Google Services plugin android { // ... }
En el mismo archivo, en el nodo dependencias de
, agregue la dependencia para la biblioteca Cloud Messaging Android.dependencies { // ... implementation 'com.google.firebase:firebase-messaging:20.2.0' }
Nota
Asegúrese de hacer referencia a la versión más reciente según la documentación del cliente android de Cloud Messaging.
Guarde los cambios y, a continuación, haga clic en el botón Sincronizar ahora (en el símbolo de la barra de herramientas) o Proyecto de sincronización con archivos de Gradle.
Controlar las notificaciones push para Android
En android Studio, Control + Haga clic en el com.<your_organization>carpeta de paquetes .pushdemo (app>src>principal>kotlin), elija Paquete en el menú Nuevo. Escriba servicios como nombre y presione Devolver.
Control Haga clic en en la carpeta de servicios de, elija archivo o clase kotlin en el menú Nuevo de . Escriba DeviceInstallationService como nombre y presione Devolver. Implemente el deviceInstallationService de
mediante el código siguiente. package com.<your_organization>.pushdemo.services import android.annotation.SuppressLint import android.content.Context import android.provider.Settings.Secure import com.google.android.gms.common.ConnectionResult import com.google.android.gms.common.GoogleApiAvailability import io.flutter.embedding.engine.FlutterEngine import io.flutter.plugin.common.MethodCall import io.flutter.plugin.common.MethodChannel @SuppressLint("HardwareIds") class DeviceInstallationService { companion object { const val DEVICE_INSTALLATION_CHANNEL = "com.<your_organization>.pushdemo/deviceinstallation" const val GET_DEVICE_ID = "getDeviceId" const val GET_DEVICE_TOKEN = "getDeviceToken" const val GET_DEVICE_PLATFORM = "getDevicePlatform" } private var context: Context private var deviceInstallationChannel : MethodChannel val playServicesAvailable get() = GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(context) == ConnectionResult.SUCCESS constructor(context: Context, flutterEngine: FlutterEngine) { this.context = context deviceInstallationChannel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, DEVICE_INSTALLATION_CHANNEL) deviceInstallationChannel.setMethodCallHandler { call, result -> handleDeviceInstallationCall(call, result) } } fun getDeviceId() : String = Secure.getString(context.applicationContext.contentResolver, Secure.ANDROID_ID) fun getDeviceToken() : String { if(!playServicesAvailable) { throw Exception(getPlayServicesError()) } // TODO: Revisit once we have created the PushNotificationsFirebaseMessagingService val token = "Placeholder_Get_Value_From_FirebaseMessagingService_Implementation" if (token.isNullOrBlank()) { throw Exception("Unable to resolve token for FCM.") } return token } fun getDevicePlatform() : String = "fcm" private fun handleDeviceInstallationCall(call: MethodCall, result: MethodChannel.Result) { when (call.method) { GET_DEVICE_ID -> { result.success(getDeviceId()) } GET_DEVICE_TOKEN -> { getDeviceToken(result) } GET_DEVICE_PLATFORM -> { result.success(getDevicePlatform()) } else -> { result.notImplemented() } } } private fun getDeviceToken(result: MethodChannel.Result) { try { val token = getDeviceToken() result.success(token) } catch (e: Exception) { result.error("ERROR", e.message, e) } } private fun getPlayServicesError(): String { val resultCode = GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(context) if (resultCode != ConnectionResult.SUCCESS) { return if (GoogleApiAvailability.getInstance().isUserResolvableError(resultCode)){ GoogleApiAvailability.getInstance().getErrorString(resultCode) } else { "This device is not supported" } } return "An error occurred preventing the use of push notifications" } }
Nota
Esta clase implementa el homólogo específico de la plataforma para el canal de
com.<your_organization>.pushdemo/deviceinstallation
. Esto se definió en la parte Flutter de la aplicación dentro de DeviceInstallationService.dart. En este caso, las llamadas se realizan desde el código común al host nativo. Asegúrese de reemplazar <your_organization> por su propia organización dondequiera que se use.Esta clase proporciona un identificador único (mediante Secure.AndroidId) como parte de la carga de registro del centro de notificaciones.
Agregue otro de clase o archivo kotlin a la carpeta de services denominada NotificationRegistrationServicey agregue el código siguiente.
package com.<your_organization>.pushdemo.services import io.flutter.embedding.engine.FlutterEngine import io.flutter.plugin.common.MethodChannel class NotificationRegistrationService { companion object { const val NOTIFICATION_REGISTRATION_CHANNEL = "com.<your_organization>.pushdemo/notificationregistration" const val REFRESH_REGISTRATION = "refreshRegistration" } private var notificationRegistrationChannel : MethodChannel constructor(flutterEngine: FlutterEngine) { notificationRegistrationChannel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, NotificationRegistrationService.NOTIFICATION_REGISTRATION_CHANNEL) } fun refreshRegistration() { notificationRegistrationChannel.invokeMethod(REFRESH_REGISTRATION, null) } }
Nota
Esta clase implementa el homólogo específico de la plataforma para el canal de
com.<your_organization>.pushdemo/notificationregistration
. Esto se definió en la parte flutter de la aplicación en NotificationRegistrationService.dart. En este caso, las llamadas se realizan desde el host nativo al código común. De nuevo, tenga cuidado de reemplazar <your_organization> por su propia organización dondequiera que se use.Agregue otro de clase o archivo kotlin a la carpeta services denominada NotificationActionServicey agregue el código siguiente.
package com.<your_organization>.pushdemo.services import io.flutter.embedding.engine.FlutterEngine import io.flutter.plugin.common.MethodCall import io.flutter.plugin.common.MethodChannel class NotificationActionService { companion object { const val NOTIFICATION_ACTION_CHANNEL = "com.<your_organization>.pushdemo/notificationaction" const val TRIGGER_ACTION = "triggerAction" const val GET_LAUNCH_ACTION = "getLaunchAction" } private var notificationActionChannel : MethodChannel var launchAction : String? = null constructor(flutterEngine: FlutterEngine) { notificationActionChannel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, NotificationActionService.NOTIFICATION_ACTION_CHANNEL) notificationActionChannel.setMethodCallHandler { call, result -> handleNotificationActionCall(call, result) } } fun triggerAction(action: String) { notificationActionChannel.invokeMethod(NotificationActionService.TRIGGER_ACTION, action) } private fun handleNotificationActionCall(call: MethodCall, result: MethodChannel.Result) { when (call.method) { NotificationActionService.GET_LAUNCH_ACTION -> { result.success(launchAction) } else -> { result.notImplemented() } } } }
Nota
Esta clase implementa el homólogo específico de la plataforma para el canal de
com.<your_organization>.pushdemo/notificationaction
. Esto se definió en la parte Flutter de la aplicación en NotificationActionService.dart. Las llamadas se pueden realizar en ambas direcciones en este caso. Asegúrese de reemplazar <your_organization> por su propia organización dondequiera que se use.Agregue un nuevo de clase o archivo kotlin a la com.<your_organization>paquete de .pushdemo denominado PushNotificationsFirebaseMessagingServicey, a continuación, implemente con el código siguiente.
package com.<your_organization>.pushdemo import android.os.Handler import android.os.Looper import com.google.firebase.messaging.FirebaseMessagingService import com.google.firebase.messaging.RemoteMessage import com.<your_organization>.pushdemo.services.NotificationActionService import com.<your_organization>.pushdemo.services.NotificationRegistrationService class PushNotificationsFirebaseMessagingService : FirebaseMessagingService() { companion object { var token : String? = null var notificationRegistrationService : NotificationRegistrationService? = null var notificationActionService : NotificationActionService? = null } override fun onNewToken(token: String) { PushNotificationsFirebaseMessagingService.token = token notificationRegistrationService?.refreshRegistration() } override fun onMessageReceived(message: RemoteMessage) { message.data.let { Handler(Looper.getMainLooper()).post { notificationActionService?.triggerAction(it.getOrDefault("action", null)) } } } }
Nota
Esta clase es responsable de controlar las notificaciones cuando la aplicación se ejecuta en primer plano. Llamará condicionalmente al triggerAction en el NotificationActionService si se incluye una acción en la carga de notificación que se recibe en onMessageReceived. Esto también llamará a
refreshRegistration en la NotificationRegistrationService decuando se vuelva a generar el token de Firebase reemplazando la funciónonNewToken .Una vez más, tenga cuidado de reemplazar <your_organization> por su propia organización dondequiera que se use.
En AndroidManifest.xml (aplicación>src>principal), agregue el PushNotificationsFirebaseMessagingService a la parte inferior del elemento aplicación con el filtro de intención de
com.google.firebase.MESSAGING_EVENT
.<manifest> <application> <!-- EXISTING MANIFEST CONTENT --> <service android:name="com.<your_organization>.pushdemo.PushNotificationsFirebaseMessagingService" android:exported="false"> <intent-filter> <action android:name="com.google.firebase.MESSAGING_EVENT" /> </intent-filter> </service> </application> </manifest>
De nuevo en DeviceInstallationService, asegúrese de que las siguientes importaciones están presentes en la parte superior del archivo.
package com.<your_organization>.pushdemo import com.<your_organization>.pushdemo.services.PushNotificationsFirebaseMessagingService
Nota
Reemplace <your_organization> por su propio valor de organización.
Actualice el texto del marcador de posición Placeholder_Get_Value_From_FirebaseMessagingService_Implementation para obtener el valor del token del PushNotificationFirebaseMessagingService.
fun getDeviceToken() : String { if(!playServicesAvailable) { throw Exception(getPlayServicesError()) } // Get token from the PushNotificationsFirebaseMessagingService.token field. val token = PushNotificationsFirebaseMessagingService.token if (token.isNullOrBlank()) { throw Exception("Unable to resolve token for FCM.") } return token }
En MainActivity, asegúrese de que las siguientes importaciones están presentes en la parte superior del archivo.
package com.<your_organization>.pushdemo import android.content.Intent import android.os.Bundle import com.google.android.gms.tasks.OnCompleteListener import com.google.firebase.iid.FirebaseInstanceId import com.<your_organization>.pushdemo.services.DeviceInstallationService import com.<your_organization>.pushdemo.services.NotificationActionService import com.<your_organization>.pushdemo.services.NotificationRegistrationService import io.flutter.embedding.android.FlutterActivity
Nota
Reemplace <your_organization> por su propio valor de organización.
Agregue una variable para almacenar una referencia al DeviceInstallationService.
private lateinit var deviceInstallationService: DeviceInstallationService
Agregue una función denominada
processNotificationActions para comprobar si un de intención detiene un valor adicional denominado acción . Desencadene condicionalmente esa acción o almacénela para usarla más adelante si la acción se está procesando durante el inicio de la aplicación.private fun processNotificationActions(intent: Intent, launchAction: Boolean = false) { if (intent.hasExtra("action")) { var action = intent.getStringExtra("action"); if (action.isNotEmpty()) { if (launchAction) { PushNotificationsFirebaseMessagingService.notificationActionService?.launchAction = action } else { PushNotificationsFirebaseMessagingService.notificationActionService?.triggerAction(action) } } } }
Invalide la función onNewIntent para llamar a processNotificationActions.
override fun onNewIntent(intent: Intent) { super.onNewIntent(intent) processNotificationActions(intent) }
Nota
Dado que el LaunchMode de
para MainActivity está establecido enSingleTop , se enviará una intent dea la instancia de Activity de existente a través del NewIntent función en lugar de la funciónonCreate y, por tanto, debe controlar una de intención deentrante en onCreate yonNewIntent funciones.Invalide la función onCreate, establezca el deviceInstallationService en una nueva instancia de DeviceInstallationService.
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) flutterEngine?.let { deviceInstallationService = DeviceInstallationService(context, it) } }
Establezca las propiedades notificationActionService y notificationRegistrationService en PushNotificationFirebaseMessagingServices.
flutterEngine?.let { deviceInstallationService = DeviceInstallationService(context, it) PushNotificationsFirebaseMessagingService.notificationActionService = NotificationActionService(it) PushNotificationsFirebaseMessagingService.notificationRegistrationService = NotificationRegistrationService(it) }
En la misma función, llame condicionalmente a FirebaseInstanceId.getInstance().instanceId. Implemente el onCompleteListener de
para establecer el valor de del token de resultante en PushNotificationFirebaseMessagingService antes de llamar arefreshRegistration .if(deviceInstallationService?.playServicesAvailable) { FirebaseInstanceId.getInstance().instanceId .addOnCompleteListener(OnCompleteListener { task -> if (!task.isSuccessful) return@OnCompleteListener PushNotificationsFirebaseMessagingService.token = task.result?.token PushNotificationsFirebaseMessagingService.notificationRegistrationService?.refreshRegistration() }) }
Todavía en onCreate, llame a processNotificationActions al final de la función. Use
verdadero para el argumento launchAction depara indicar que esta acción se está procesando durante el inicio de la aplicación. processNotificationActions(this.intent, true)
Nota
Debe volver a registrar la aplicación cada vez que la ejecute y detenerla desde una sesión de depuración para continuar recibiendo notificaciones push.
Configuración del proyecto nativo de iOS para notificaciones push
Configuración del destino del ejecutor e Info.plist
En visual Studio Code, Control + Haga clic en en la carpeta ios y elija Abrir en Xcode.
En
Xcode , haga clic en Runner ( xcodeproj en la parte superior, no en la carpeta) seleccione el destino de Ejecutor dey, a continuación, Firma & Funcionalidades . Con la configuración de compilaciónseleccionada, elija la cuenta de desarrollador delequipo de . Asegúrese de que la opción "Administrar automáticamente la firma" está activada y que el certificado de firma y el perfil de aprovisionamiento se seleccionan automáticamente. Nota
Si no ve el nuevo valor del perfil de aprovisionamiento, pruebe a actualizar los perfiles de la identidad de firma seleccionando Xcode>Preferencias>Cuenta, seleccione el botón Descargar perfiles manuales para descargar los perfiles.
Haga clic en + Funcionalidady busque Notificaciones push. hacer doble clic en de notificaciones push para agregar esta funcionalidad.
Abra Info.plist y establezca versión mínima del sistema en 13.0.
Nota
Solo se admiten los dispositivos que ejecutan iOS 13.0 y versiones posteriores para los fines de este tutorial, pero puede ampliarlo para admitir dispositivos que ejecutan versiones anteriores.
Abra
Runner.entitlements y asegúrese de que la configuración entorno de APS de esté establecida en de desarrollo .
Control de notificaciones push para iOS
Control Haga clic en en la carpeta Ejecutor de(en el proyecto Ejecutor) y, a continuación, elija Nuevo grupo conServices como nombre.Control Haga clic en en la carpetaServices y, a continuación, elija Nuevo archivo... . A continuación, elijaarchivo Swift y haga clic en Siguiente . Especifique DeviceInstallationService para el nombre y, a continuación, haga clic en Crear.Implemente DeviceInstallationService.swift mediante el código siguiente.
import Foundation class DeviceInstallationService { enum DeviceRegistrationError: Error { case notificationSupport(message: String) } var token : Data? = nil let DEVICE_INSTALLATION_CHANNEL = "com.<your_organization>.pushdemo/deviceinstallation" let GET_DEVICE_ID = "getDeviceId" let GET_DEVICE_TOKEN = "getDeviceToken" let GET_DEVICE_PLATFORM = "getDevicePlatform" private let deviceInstallationChannel : FlutterMethodChannel var notificationsSupported : Bool { get { if #available(iOS 13.0, *) { return true } else { return false } } } init(withBinaryMessenger binaryMessenger : FlutterBinaryMessenger) { deviceInstallationChannel = FlutterMethodChannel(name: DEVICE_INSTALLATION_CHANNEL, binaryMessenger: binaryMessenger) deviceInstallationChannel.setMethodCallHandler(handleDeviceInstallationCall) } func getDeviceId() -> String { return UIDevice.current.identifierForVendor!.description } func getDeviceToken() throws -> String { if(!notificationsSupported) { let notificationSupportError = getNotificationsSupportError() throw DeviceRegistrationError.notificationSupport(message: notificationSupportError) } if (token == nil) { throw DeviceRegistrationError.notificationSupport(message: "Unable to resolve token for APNS.") } return token!.reduce("", {$0 + String(format: "%02X", $1)}) } func getDevicePlatform() -> String { return "apns" } private func handleDeviceInstallationCall(call: FlutterMethodCall, result: @escaping FlutterResult) { switch call.method { case GET_DEVICE_ID: result(getDeviceId()) case GET_DEVICE_TOKEN: getDeviceToken(result: result) case GET_DEVICE_PLATFORM: result(getDevicePlatform()) default: result(FlutterMethodNotImplemented) } } private func getDeviceToken(result: @escaping FlutterResult) { do { let token = try getDeviceToken() result(token) } catch let error { result(FlutterError(code: "UNAVAILABLE", message: error.localizedDescription, details: nil)) } } private func getNotificationsSupportError() -> String { if (!notificationsSupported) { return "This app only supports notifications on iOS 13.0 and above. You are running \(UIDevice.current.systemVersion)" } return "An error occurred preventing the use of push notifications." } }
Nota
Esta clase implementa el homólogo específico de la plataforma para el canal de
com.<your_organization>.pushdemo/deviceinstallation
. Esto se definió en la parte Flutter de la aplicación dentro de DeviceInstallationService.dart. En este caso, las llamadas se realizan desde el código común al host nativo. Asegúrese de reemplazar <your_organization> por su propia organización dondequiera que se use.Esta clase proporciona un identificador único (mediante el UIDevice.identifierForVendor valor) como parte de la carga de registro del centro de notificaciones.
Agregue otra
Swift File a la carpeta deServices denominada NotificationRegistrationService y agregue el código siguiente.import Foundation class NotificationRegistrationService { let NOTIFICATION_REGISTRATION_CHANNEL = "com.<your_organization>.pushdemo/notificationregistration" let REFRESH_REGISTRATION = "refreshRegistration" private let notificationRegistrationChannel : FlutterMethodChannel init(withBinaryMessenger binaryMessenger : FlutterBinaryMessenger) { notificationRegistrationChannel = FlutterMethodChannel(name: NOTIFICATION_REGISTRATION_CHANNEL, binaryMessenger: binaryMessenger) } func refreshRegistration() { notificationRegistrationChannel.invokeMethod(REFRESH_REGISTRATION, arguments: nil) } }
Nota
Esta clase implementa el homólogo específico de la plataforma para el canal de
com.<your_organization>.pushdemo/notificationregistration
. Esto se definió en la parte flutter de la aplicación en NotificationRegistrationService.dart. En este caso, las llamadas se realizan desde el host nativo al código común. De nuevo, tenga cuidado de reemplazar <your_organization> por su propia organización dondequiera que se use.Agregue otro
swift File a la carpeta de Services denominada NotificationActionService y agregue el código siguiente.import Foundation class NotificationActionService { let NOTIFICATION_ACTION_CHANNEL = "com.<your_organization>.pushdemo/notificationaction" let TRIGGER_ACTION = "triggerAction" let GET_LAUNCH_ACTION = "getLaunchAction" private let notificationActionChannel: FlutterMethodChannel var launchAction: String? = nil init(withBinaryMessenger binaryMessenger: FlutterBinaryMessenger) { notificationActionChannel = FlutterMethodChannel(name: NOTIFICATION_ACTION_CHANNEL, binaryMessenger: binaryMessenger) notificationActionChannel.setMethodCallHandler(handleNotificationActionCall) } func triggerAction(action: String) { notificationActionChannel.invokeMethod(TRIGGER_ACTION, arguments: action) } private func handleNotificationActionCall(call: FlutterMethodCall, result: @escaping FlutterResult) { switch call.method { case GET_LAUNCH_ACTION: result(launchAction) default: result(FlutterMethodNotImplemented) } } }
Nota
Esta clase implementa el homólogo específico de la plataforma para el canal de
com.<your_organization>.pushdemo/notificationaction
. Esto se definió en la parte Flutter de la aplicación en NotificationActionService.dart. Las llamadas se pueden realizar en ambas direcciones en este caso. Asegúrese de reemplazar <your_organization> por su propia organización dondequiera que se use.En AppDelegate.swift, agregue variables para almacenar una referencia a los servicios que creó anteriormente.
var deviceInstallationService : DeviceInstallationService? var notificationRegistrationService : NotificationRegistrationService? var notificationActionService : NotificationActionService?
Agregue una función denominada processNotificationActions para procesar los datos de notificación. Desencadene condicionalmente esa acción o almacénela para usarla más adelante si la acción se está procesando durante el inicio de la aplicación.
func processNotificationActions(userInfo: [AnyHashable : Any], launchAction: Bool = false) { if let action = userInfo["action"] as? String { if (launchAction) { notificationActionService?.launchAction = action } else { notificationActionService?.triggerAction(action: action) } } }
Invalide la función didRegisterForRemoteNotificationsWithDeviceToken estableciendo el valor del token de para el DeviceInstallationService. A continuación, llame a
refreshRegistration en elnotificationRegistrationService de. override func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { deviceInstallationService?.token = deviceToken notificationRegistrationService?.refreshRegistration() }
Invalide la función
didReceiveRemoteNotification pasando el argumento userInfoa la función processNotificationActions . override func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any]) { processNotificationActions(userInfo: userInfo) }
Invalide el función didFailToRegisterForRemoteNotificationsWithError para registrar el error.
override func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) { print(error); }
Nota
Esto es muy un marcador de posición. Querrá implementar el registro y el control de errores adecuados para escenarios de producción.
En didFinishLaunchingWithOptions, cree una instancia de las variables deviceInstallationService, notificationRegistrationServicey notificationActionService.
let controller : FlutterViewController = window?.rootViewController as! FlutterViewController deviceInstallationService = DeviceInstallationService(withBinaryMessenger: controller.binaryMessenger) notificationRegistrationService = NotificationRegistrationService(withBinaryMessenger: controller.binaryMessenger) notificationActionService = NotificationActionService(withBinaryMessenger: controller.binaryMessenger)
En la misma función, solicite condicionalmente la autorización y regístrese para las notificaciones remotas.
if #available(iOS 13.0, *) { UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) { (granted, error) in if (granted) { DispatchQueue.main.async { let pushSettings = UIUserNotificationSettings(types: [.alert, .sound, .badge], categories: nil) application.registerUserNotificationSettings(pushSettings) application.registerForRemoteNotifications() } } } }
Si el launchOptions de
contiene la clave remoteNotification resultante, llame a processNotificationActions al final de la funcióndidFinishLaunchingWithOp tions. Pase el objeto userInfoy use verdadero para el argumento launchAction. Un verdadero valor indica que la acción se está procesando durante el inicio de la aplicación. if let userInfo = launchOptions?[.remoteNotification] as? [AnyHashable : Any] { processNotificationActions(userInfo: userInfo, launchAction: true) }
Prueba de la solución
Ahora puede probar el envío de notificaciones a través del servicio back-end.
Envío de una notificación de prueba
Abra una nueva pestaña en postman.
Establezca la solicitud en POSTy escriba la siguiente dirección:
https://<app_name>.azurewebsites.net/api/notifications/requests
Si eligió completar la sección autenticar clientes de mediante una clave de API, asegúrese de configurar los encabezados de solicitud para incluir el valor de apikey de .
Llave Valor apikey <your_api_key> Elija la opción
sin formato para lacuerpo de y, después, elija JSON en la lista de opciones de formato y, a continuación, incluya algún marcador de posicióncontenido de JSON :{ "text": "Message from Postman!", "action": "action_a" }
Seleccione el botón código
, que se encuentra bajo el botón Guardar de la parte superior derecha de la ventana. La solicitud debe tener un aspecto similar al ejemplo siguiente cuando se muestra para HTML (dependiendo de si ha incluido un encabezado 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" }
Ejecute la aplicación PushDemo en una o ambas plataformas de destino (Android y de iOS ).
Nota
Si está probando en Android asegúrese de que no se está ejecutando en Depuracióno si la aplicación se ha implementado mediante la ejecución de la aplicación, obligue a cerrar la aplicación e iníciela de nuevo desde el iniciador.
En la aplicación PushDemo, pulse en el botón Registrar.
De nuevo en
, cierre la ventana generar fragmentos de códigoPostman (si aún no lo ha hecho) y haga clic en el botón Enviar .Compruebe que recibe una respuesta de de
200 OK en Postman y la alerta aparece en la aplicación que muestraacción ActionA recibida .Cierre la aplicación pushDemo
y haga clic en el botón Enviar de nuevo en .Postman Compruebe que recibe una respuesta de de
200 OK en pushDemo de nuevo. Compruebe que una notificación aparece en el área de notificación de la aplicaciónPostman con el mensaje correcto. Pulse en la notificación para confirmar que abre la aplicación y muestra la acción ActionA recibida alerta.
De nuevo en Postman, modifique el cuerpo de la solicitud anterior para enviar una notificación silenciosa que especifique action_b en lugar de action_a para la acción valor.
{ "action": "action_b", "silent": true }
Con la aplicación abierta, haga clic en el botón Enviar
en .Postman Compruebe que recibe una respuesta de de
200 OK en y que la alerta aparece en la aplicación que muestraPostman acción ActionB recibida en lugar deacción ActionA recibida .Cierre la aplicación pushDemo
y haga clic en el botón Enviar de nuevo en .Postman Compruebe que recibe una respuesta de 200 OK en Postman y que la notificación silenciosa no aparece en el área de notificación.
Solución de problemas
No hay respuesta del servicio back-end
Al probar localmente, asegúrese de que el servicio back-end se está ejecutando y está usando el puerto correcto.
Si las pruebas con la aplicación de API de Azure , compruebe que el servicio se está ejecutando y se ha implementado y se ha iniciado sin errores.
Asegúrese de comprobar que ha especificado correctamente la dirección base en postman o en la configuración de la aplicación móvil al probar a través del cliente. La dirección base debe ser https://<api_name>.azurewebsites.net/
o https://localhost:5001/
al realizar pruebas localmente.
No recibir notificaciones en Android después de iniciar o detener una sesión de depuración
Asegúrese de volver a registrarse después de iniciar o detener una sesión de depuración. El depurador hará que se genere un nuevo token Firebase. La instalación del centro de notificaciones también debe actualizarse.
Recepción de un código de estado 401 del servicio back-end
Compruebe que va a establecer el encabezado de solicitud apikey de
Si recibe este error al probar localmente, asegúrese de que el valor de clave definido en la configuración del cliente coincide con el valor de autenticación Authentication:ApiKey valor de configuración de usuario usado por la API de .
Si está probando con una aplicación de API de , asegúrese de que el valor de clave del archivo de configuración de cliente coincide con la configuración de la aplicación Authentication:ApiKey que usa en la aplicación de API .
Nota
Si ha creado o cambiado esta configuración después de haber implementado el servicio back-end, debe reiniciar el servicio para que surta efecto.
Si eligió no completar la sección autenticar clientes de
Recepción de un código de estado 404 del servicio 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]
https://<api_name>.azurewebsites.net/api/notifications/installations
-
https://<api_name>.azurewebsites.net/api/notifications/installations/<installation_id>
[DELETE] -
[POST]
https://<api_name>.azurewebsites.net/api/notifications/requests
O al probar localmente:
-
[PUT]
https://localhost:5001/api/notifications/installations
-
https://localhost:5001/api/notifications/installations/<installation_id>
[DELETE] -
[POST]
https://localhost:5001/api/notifications/requests
Al especificar la dirección base en la aplicación cliente, asegúrese de que termina con un /
. La dirección base debe ser https://<api_name>.azurewebsites.net/
o https://localhost:5001/
al realizar pruebas localmente.
No se puede registrar y se muestra un mensaje de error del centro de notificaciones
Compruebe que el dispositivo de prueba tiene conectividad de red. A continuación, determine el código de estado de respuesta Http estableciendo un punto de interrupción para inspeccionar el valor de propiedad statusCode
Revise las sugerencias de solución de problemas anteriores cuando corresponda en función del 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. A continuación, intente llamar al servicio back-end al depurar localmente.
Valide que el servicio 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 perdido ningún paso. Compruebe que los valores adecuados se resuelven para installation id
y token
variables para 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 perdido ningún paso.
Vínculos relacionados
- introducción a Azure Notification Hubs
- instalación de Flutter en macOS
- instalación de Flutter en Windows
- SDK de Notification Hubs para operaciones de back-end
- SDK de Notification Hubs en GitHub
- Registrarse con back-end de la aplicación
- administración de registros
- Trabajar con etiquetas
- Trabajar con plantillas personalizadas
Pasos siguientes
Ahora debería tener una aplicación básica de Flutter conectada a un centro de notificaciones a través de un servicio back-end y puede enviar y recibir notificaciones.
Es probable que tenga que adaptar el ejemplo usado en este tutorial para ajustarse a su propio escenario. También se recomienda implementar un control de errores más sólido, la lógica de reintento y el registro.
de Visual Studio App Center se pueden incorporar rápidamente en aplicaciones móviles que proporcionan análisis y diagnósticos de para ayudar a solucionar problemas.