Compartir a través de


Envío de notificaciones a aplicaciones Android e iOS

En este artículo se proporciona información general sobre la aplicación de ejemplo de Azure Notification Hub creada para demostrar las funcionalidades de Azure Notification Hub en varias plataformas. La aplicación usa un escenario de encuesta de llegada, en el que la aplicación de escritorio Contoso Land Survey envía notificaciones, que pueden recibir las aplicaciones Contoso de Android e iOS.

Puede descargar la muestra completa en GitHub.

Prerrequisitos

Para compilar la muestra, necesita los siguientes requisitos previos:

  • Suscripción a Azure. Si no tiene una suscripción a Azure, cree una cuenta gratuita de Azure antes de empezar.
  • Microsoft Visual Studio 2019 o posterior. En este ejemplo se utiliza Visual Studio 2019.
  • Visual Studio 2019 con las cargas de trabajo siguientes instaladas:
  • Cuenta de Firebase para habilitar las notificaciones push para Android.
  • Cuenta de desarrollador de Apple para habilitar las notificaciones push para iOS.
  • Una instancia de base de datos SQL Server, hospedada en Azure.
  • Un espacio de nombres y un centro de Azure Notification Hub.

Arquitectura de muestra

La solución consta de los siguientes componentes:

  • Instancia de Azure Notification Hub: un espacio de nombres y un centro de ANH configurados en el Azure Portal.
  • Base de datos SQL Server: una instancia de base de datos SQL Server, configurada en el Azure Portal.
  • Back-end de la aplicación ASP.NET: un back-end de API web basado en .NET 5.0, que se conecta con el centro de notificaciones y se hospeda como un Azure App Service.
  • Aplicación UWP de Windows: una aplicación UWP compilada con React Native y que actúa como una aplicación de "administrador" que envía información de noticias y encuestas a varios usuarios y grupos de encuestas. La aplicación también ayuda a crear nuevos usuarios y a editar grupos a los que se asigna un usuario.
  • Aplicaciones cliente de iOS y Android: aplicaciones "Land Survey" creadas con React Native. Estas aplicaciones muestran a los usuarios la información enviada por la aplicación de administrador de UWP.

Estructura de carpetas de muestra

La aplicación de ejemplo en GitHub contiene las siguientes carpetas:

  • NotificationHub.Sample.API: una solución Visual Studio 2019 ASP.NET de API web que actúa como back-end.
  • app: una aplicación multiplataforma React Native que permite enviar notificaciones con un inicio de sesión de administrador y, a continuación, recibir notificaciones con un inicio de sesión de usuario de encuesta.
  • azure-template: plantillas Azure Resource Manager (parameters.json y template.json) que puede usar para implementar todos los recursos necesarios para configurar esta implementación en la suscripción de Azure. Para obtener más información sobre implementación de plantillas de Resource Manager, consulte Creación e implementación de plantillas de ARM mediante el Azure Portal.

Información general de la muestra

En las secciones siguientes se proporciona información general de los componentes que componen la muestra.

Controladores

Authentication

Los métodos siguientes de AuthenticateController.cs se usan para autenticar a un usuario que ha iniciado sesión:

[HttpPost]
[Route("login")]
public async Task<IActionResult> Login([FromBody] LoginModel model)
{
   var user = await userManager.FindByNameAsync(model.Username);
    if (user != null && await userManager.CheckPasswordAsync(user, model.Password))
    {
        var userRoles = await userManager.GetRolesAsync(user);

        var authClaims = new List<Claim>
        {
            new Claim(ClaimTypes.Name, user.UserName),
            new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
        };

        foreach (var userRole in userRoles)
        {
            authClaims.Add(new Claim(ClaimTypes.Role, userRole));
        }

        var authSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["JWT:Secret"]));

        var token = new JwtSecurityToken(
            issuer: _configuration["JWT:ValidIssuer"],
            audience: _configuration["JWT:ValidAudience"],
            expires: DateTime.Now.AddHours(3),
            claims: authClaims,
            signingCredentials: new SigningCredentials(authSigningKey, SecurityAlgorithms.HmacSha256)
            );

        UserDetails userDetails = new UserDetails();
        userDetails.FirstName = model.Username;
        userDetails.LastName = model.Username;
        userDetails.UserName = model.Username;

        return Ok(new
        {
            token = new JwtSecurityTokenHandler().WriteToken(token),
            expiration = token.ValidTo,
            username = model.Username,
            email = user.Email,
            role = userRoles != null ? userRoles[0] : "Site-Manager",
            user = userDetails
        });
    }
    return Unauthorized();
}

[HttpPost]
[Route("register")]
public async Task<IActionResult> Register([FromBody] RegisterModel model)
{
    var userExists = await userManager.FindByNameAsync(model.Username);
    if (userExists != null)
        return StatusCode(StatusCodes.Status500InternalServerError, new Response { Status = "Error", Message = "User already exists!" });

    ApplicationUser user = new ApplicationUser()
    {
        Email = model.Email,
        SecurityStamp = Guid.NewGuid().ToString(),
        UserName = model.Username
    };
    var result = await userManager.CreateAsync(user, model.Password);
    if (!result.Succeeded)
        return StatusCode(StatusCodes.Status500InternalServerError, new Response { Status = "Error", Message = "User creation failed! Please check user details and try again." });

    if (!await roleManager.RoleExistsAsync(UserRoles.SiteManager))
        await roleManager.CreateAsync(new IdentityRole(UserRoles.SiteManager));

    if (await roleManager.RoleExistsAsync(UserRoles.SiteManager))
    {
        await userManager.AddToRoleAsync(user, UserRoles.SiteManager);
    }

    return Ok(new Response { Status = "Success", Message = "User created successfully!" });
}

Panel

El controlador de panel de DashboardController.cs devuelve toda la información de notificación:

public class DashboardController : ControllerBase
{
    private readonly ApplicationDbContext _db;
    private readonly INotificationService _notificationService;

    public DashboardController(ApplicationDbContext dbContext, INotificationService notificationService)
    {
        _db = dbContext;
        _notificationService = notificationService;
    }

    [HttpGet("insights")]
    public async Task<IActionResult> GetDashboardInsight(string duration)
    {
        DashboardInsight dashboardInsight = new DashboardInsight();

        dashboardInsight.DeviceTrends = await _notificationService.GetAllRegistrationInfoAsync();

        var notificationMessages = _db.NotificationMessages.ToList();

        switch (duration)
        {
            case "Daily":
                {
                    dashboardInsight.NotificationTrends = _db.NotificationMessages
                                                            .GroupBy(m => m.SentTime.Date)
                                                            .Select(m => new NotificationTrend()
                                                            {
                                                                Timestamp = m.Key.ToShortDateString(),
                                                                NotificationsSent = m.Count()
                                                            }).ToList();
                }
                break;
            case "Weekly":
                {
                    dashboardInsight.NotificationTrends = notificationMessages
                                                            .GroupBy(m => WeekNumber(m.SentTime.Date))
                                                            .Select(m => new NotificationTrend()
                                                            {
                                                                Timestamp = FirstDateOfWeekISO8601(DateTime.Now.Year, m.Key).ToShortDateString(),
                                                                NotificationsSent = m.Count()
                                                            }).ToList();
                }
                break;
            case "Monthly":
                {
                    dashboardInsight.NotificationTrends = _db.NotificationMessages
                                                            .GroupBy(m => m.SentTime.Date.Month)
                                                            .Select(m => new NotificationTrend()
                                                            {
                                                                Timestamp = m.Key + "-" + DateTime.Now.Year,
                                                                NotificationsSent = m.Count()
                                                            }).ToList();
                }
                break;
            default:
                break;
        }

        dashboardInsight.TotalGroups = _db.SurveyGroups.Count();
        dashboardInsight.TotalUsers = _db.Users.Count();
        dashboardInsight.TotalNotificationsSent = _db.NotificationMessages.Count();

        return Ok(dashboardInsight);
    }

Front-end

Para llamar a cualquier API de back-end, la muestra crea el servicio notification.service.js, que realiza una llamada API real. Este código se encuentra en app\data\services\notification.service.js:

export const sendNotificationAPI = async (userInfo) => {
  let url = `${api}notification/send`;
  let authHeader = await getAuthHeaders();
  return await post(url, userInfo, { ...authHeader });
};

export const getNotificationsAPI = async () => {
  let url = `${api}notification/get`;
  let authHeader = await getAuthHeaders();
  return await get(url, { ...authHeader });
};

Aplicación de administrador

Esta muestra tiene una aplicación para UWP compilada con React Native y que actúa como una aplicación de "administrador" que envía información de noticias y encuestas a varios usuarios y grupos de encuestas. La aplicación también ayuda a crear nuevos usuarios y editar grupos a los que se asigna un usuario.

Para el inicio de sesión del administrador, use el siguiente punto de conexión y el cuerpo POST para generar sus credenciales de usuario. Puede usar cualquier cliente REST HTTP:

Punto de conexión

POST {{endpoint}}api/authenticate/register-admin

Cuerpo

{
  "username": "<USER_NAME>",
  "email": "<EMAIL>",
  "password": "<PASSWORD>"
}

Una vez registrado, debería poder iniciar sesión en la aplicación para UWP con las mismas credenciales.

Controlador de notificación

El código siguiente, en NotificationController.cs, obtiene y envía notificaciones:

[Produces("application/json")]
[Consumes("application/json")]
[HttpPost("send")]
public async Task<ActionResult> SendNotification([FromBody] NotificationMessage notificationMessage)
{
    try
    {
        List<string> tags = new List<string>();

        // attach survey group and user information with notificationMessage
        notificationMessage.SurveyGroupTags.ForEach(surveyGroupId =>
        {
            var group = _db.SurveyGroups.Where(g => g.Id == surveyGroupId).FirstOrDefault();
            if (group != null)
            {
                notificationMessage.SurveyGroups.Add(group);
                tags.Add($"group:{group.GroupName.Replace(' ', '-')}");
            }
        });

        notificationMessage.UserTags.ForEach(userId =>
        {
            var user = _db.Users.Where(u => u.Id == userId).FirstOrDefault();
            if (user != null)
            {
                notificationMessage.Users.Add(user);
                tags.Add($"username:{user.UserName}");
            }
        });
        _db.NotificationMessages.Add(notificationMessage);

        // send template notification
        var notification = new Dictionary<string, string>();
        notification.Add("title", notificationMessage.NotificationTitle);
        notification.Add("message", notificationMessage.NotificationDescription);

        var res = await _notificationService.RequestNotificationAsync(notificationMessage, tags, HttpContext.RequestAborted);

        await _db.SaveChangesAsync();
        return Ok(notificationMessage);
    }
    catch (Exception ex)
    {
        return BadRequest(ex.Message);
    }
}

[Produces("application/json")]
[HttpGet("get")]
public async Task<ActionResult> Get()
{
    try
    {
        var surveyGroups = _db.NotificationMessages.Include(message => message.SurveyGroups).Include(message => message.Users).ToList();
        return Ok(surveyGroups);
    }
    catch (Exception ex)
    {
        return BadRequest();
    }
}

Servicio de notificación

El servicio de notificación ubicado en NotificationHub.Sample.API/NotificationHub.Sample.API/Services/Notifications/INotificationService.cs dispone de métodos para crear y eliminar la instalación. También hay un método para enviar notificaciones a todos los usuarios registrados y para obtener toda la información de registro:

public interface INotificationService
{
   Task<bool> CreateOrUpdateInstallationAsync(DeviceInstallation deviceInstallation, CancellationToken cancellationToken);
   Task<bool> DeleteInstallationByIdAsync(string installationId, CancellationToken cancellationToken);
   Task<bool> RequestNotificationAsync(NotificationMessage notificationMessage, IList<string> tags, CancellationToken cancellationToken);
   Task<List<DeviceTrend>> GetAllRegistrationInfoAsync();
}

Implementación de la solución

Para ejecutar la muestra, se precisan los siguientes requisitos previos:

  • Suscripción a Azure. Si no tiene una suscripción a Azure, cree una cuenta de Azure gratuita.
  • Cuenta de Firebase para configurar las notificaciones push para Android.
  • Cuenta de desarrollador de Apple para configurar las notificaciones push para iOS.
  • Un equipo Windows con Visual Studio 2019 instalado, junto con compatibilidad con desarrollo de aplicaciones para UWP y ASP.NET Core.
  • Android SDK y Android Studio en su máquina de desarrollo si se ejecuta la aplicación Android.
  • Una máquina Mac OSX con XCode y el SDK de iOS más reciente, si se ejecuta la aplicación iOS.

Implementación de recursos

Si no desea implementar todos los recursos manualmente, puede usar el archivo template.jsen de Azure Resource Manager proporcionado en el repositorio de GitHub para implementar todas las instancias necesarias en un solo paso. El archivo de plantilla está disponible en el repositorio en /azure-template. Para obtener más información sobre cómo usar plantillas de Resource Manager, consulte Tutorial: uso de plantillas de inicio rápido de Azure.

Configuración de notificaciones para Android e iOS

  • Firebase: para recibir notificaciones en Android, configure el servicio Firebase y conéctelo con la instancia del centro de notificaciones de Azure. Para más información, consulte Configuración de Google Firebase.
  • Apple Push Notification Service (APNS): para recibir notificaciones en iOS, configure el servicio APNS con su cuenta de desarrollador de Apple y conéctelo con la instancia del centro de notificaciones de Azure. Para obtener más información, consulte Configurar Apple Push Notification Service.

Compilar la solución

Para compilar la muestra, siga estos pasos.

Creación de un recurso: base de datos SQL

Cree una instancia de base de datos SQL Server en Azure Portal. Por ejemplo:

Recursos de instancia de SQL

Plantilla de implementación

Creación de un recurso: centro de notificaciones

Creación de un centro de notificaciones en Azure Portal de la siguiente manera:

  1. Inicie sesión en Azure Portal.

  2. Seleccione Todos los servicios en el menú de la izquierda. Una captura de pantalla que muestra la selección de Todos los servicios para un espacio de nombres existente.

  3. Escriba Notification Hubs en el cuadro de texto Filtrar servicios. Seleccione el icono de estrella junto al nombre del servicio para agregarlo a la sección FAVORITOS en el menú de la izquierda. Seleccione Notification Hubs.

    Una captura de pantalla que muestra cómo filtrar los centros de notificaciones.

  4. En la página Notification Hubs, seleccione Crear en la barra de herramientas.

    Captura de pantalla que muestra cómo crear un centro de notificaciones.

  5. En la pestaña Básico de la página Centro de notificaciones, siga estos pasos:

    1. En Suscripción, seleccione el nombre de la suscripción de Azure que desea usar y, a continuación, seleccione un grupo de recursos existente o cree uno nuevo.

    2. Escriba un nombre único para el nuevo espacio de nombres en Detalles del espacio de nombres.

    3. Un espacio de nombres contiene uno o varios centros de notificaciones, por lo que debe escribir un nombre para el centro en Detalles del centro de notificaciones.

    4. Seleccione un valor en el cuadro de lista desplegable Ubicación. Este valor especifica la ubicación en la que quiere crear el centro.

      Captura de pantalla que muestra los detalles del centro de notificaciones.

    5. Revise la opción Availability Zones. Si elige una región que tiene zonas de disponibilidad, la casilla estará activada de forma predeterminada. Availability Zones es una característica de pago, por lo que se agrega un honorario adicional al nivel.

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

      Captura de pantalla que muestra los detalles de la zona de disponibilidad.

    7. Seleccione Crear.

  6. Cuando la implementación se complete, seleccione Ir al recurso.

Configuración del back-end

Para configurar el back-end de la aplicación, busque el archivo /NotificationHub.Sample.API/appsettings.json y configure la cadena de conexión de SQL Server.

Puede ejecutar la solución API localmente o en cualquier servidor IIS, o implementarla como una instancia de Azure Web App Service. Mantenga la dirección URL de la API a mano.

Compilación de la aplicación NotificationHub.Sample.API

  1. En Visual Studio, cargue la solución /NotificationHubSample/NotificationHub.Sample.API/NotificationHub.Sample.API.sln.

  2. En el menú Compilar, seleccione Compilar solución.

  3. Publique la solución en Azure: en Explorador de soluciones, haga clic con el botón derecho en el proyecto NotificationHub.Sample.API y seleccione Publicar.

  4. En el diálogo Publicar, seleccione Azure (primera opción) y, a continuación, seleccione Siguiente.

  5. Seleccione Azure App Service (Windows) y, a continuación, seleccione Siguiente.

  6. Se le redirigirá al siguiente diálogo, desde el que puede seleccionar el nombre de suscripción adecuado y las instancias de AppService. El back-end se implementará en Azure y se le redirigirá a una nueva dirección URL.

    Publicar proyecto

  7. Una vez publicado el back-end, agregue la dirección URL generada aconfig.js, que se encuentra en la carpeta /app. Asegúrese de anexar /api/ después de la dirección URL.

Ejecución de una aplicación de front-end React Native para Windows

La aplicación requiere que la aplicación móvil (ya sea Android o iOS) y la aplicación de administrador para UWP se ejecuten simultáneamente. Siga estos pasos para ejecutar ambas aplicaciones:

Abra la carpeta app en la ventana de terminal o shell que prefiera. A continuación, haga lo siguiente:

Windows

  1. Ejecute npm install para instalar todas las dependencias del paquete.
  2. Ejecute npm run start para iniciar el servidor metro en una ventana de consola.
  3. Abra otra ventana de terminal y ejecute npx react-native run-windows para ejecutar la aplicación para UWP.
  4. Si se produce un error en la implementación o compilación, consulte la guía de solución de problemas.

Android

  1. Configure Firebase en su proyecto React-Native para asegurarse de que puede usar las funcionalidades de notificación.
  2. Después de configurar correctamente el proyecto Firebase, descargue el archivo google-services.json del portal de Firebase.
  3. Reemplace ./app/android/app/google-services.json por este nuevo archivo. Asegúrese de que el nombre del paquete de aplicación coincide con el configurado en Firebase. El nombre del paquete se configura en el archivo AndroidManifest.xml.
  4. Para obtener más información sobre cómo configurar notificaciones en una aplicación React Native, consulte Tutorial: envío de notificaciones push a React Native.
  5. Una vez configuradas las notificaciones, ejecute npm run start para iniciar el servidor metro en una ventana de consola. Si la aplicación Windows ya se está ejecutando, puede omitir este paso.
  6. En una nueva ventana de consola, ejecute npx react-native run-android para ejecutar la aplicación Android.

iOS

  1. Configure Firebase en su proyecto React-Native para asegurarse de que puede usar las funcionalidades de notificación.
  2. En iOS, las notificaciones solo las pueden recibir las aplicaciones firmadas e instaladas a través de App Store o TestFlight. Debe crear una aplicación en su cuenta de desarrollador de Apple. El identificador de agrupación de la aplicación configurado en la cuenta de desarrollador de Apple debe configurarse en los archivos Info.plist y Entitlements.plist de su aplicación.
  3. Para obtener más información sobre cómo configurar notificaciones en una aplicación React Native, consulte Tutorial: envío de notificaciones push a React Native.
  4. Una vez configuradas las notificaciones, ejecute npm run start para iniciar el servidor metro en una ventana de consola. Si la aplicación Windows ya se está ejecutando, puede omitir este paso.
  5. En una nueva ventana de consola, ejecute npx react-native run-ios para ejecutar la aplicación iOS. Como se mencionó anteriormente, las notificaciones no funcionarán en iOS si se implementan localmente. No puede recibir notificaciones en el simulador de iOS.

Solución de problemas

Es posible que reciba el siguiente error al ejecutar la aplicación React Native para Windows:

error MSB4057: The target "Deploy" does not exist in the project

Se trata de un problema conocido con React Native para Windows. Para solucionar este problema, descargue el archivo .csproj para los proyectos CheckboxWindows y ReactNativeAsyncStorage en Visual Studio después de abrir app.sln en la carpeta app/windows. A continuación, en el archivo app.csproj agregue la siguiente línea justo antes de ...</Project>, y vuelva a cargar el proyecto:

<Target Name="Deploy"/>

Pasos siguientes