Cómo agregar notificaciones push a una app web

Los mensajes push proporcionan una forma sencilla y eficaz de volver a atraer a los usuarios. En este codelab, aprenderás a agregar notificaciones push a tu app web.

Qué aprenderás

  • Cómo suscribir y anular la suscripción de un usuario a los mensajes push
  • Cómo controlar los mensajes push entrantes
  • Cómo mostrar una notificación
  • Cómo responder a los clics en las notificaciones

Requisitos

  • Chrome 52 o versiones posteriores
  • Web Server for Chrome o el servidor web que prefieras
  • Un editor de texto
  • Conocimientos básicos de HTML, CSS, JavaScript y las Herramientas para desarrolladores de Chrome
  • El código de muestra (consulta la sección de configuración)

Descarga el código de muestra

Tienes dos opciones para obtener el código de muestra de este codelab:

  • Clona el repositorio de Git:
git clone https://github.com/GoogleChrome/push-notifications.git
  • Descarga el archivo ZIP:

Descargar el código fuente

Si descargas el código fuente como un archivo ZIP, al descomprimirlo, obtendrás una carpeta raíz push-notifications-master.

Instala y verifica el servidor web

Si bien puedes usar tu propio servidor web, este codelab está diseñado para funcionar bien con la app de Web Server for Chrome. Si aún no tienes instalada esa app, puedes obtenerla en Chrome Web Store:

Instala Web Server for Chrome

Después de instalar la app de Web Server for Chrome, haz clic en el acceso directo Apps en la barra de favoritos:

En la ventana Apps, haz clic en el ícono de Web Server:

Luego, verás este diálogo, que te permitirá configurar tu servidor web local:

Haz clic en el botón Elegir carpeta y selecciona la carpeta app en la carpeta push-notifications que descargaste. Esto te permite entregar el trabajo en curso a través de la URL que se muestra en la sección Web Server URL(s) del diálogo.

En Options, marca la casilla junto a Automatically show index.html, como se muestra a continuación:

Luego, detén y reinicia el servidor deslizando el interruptor Web Server: STARTED hacia la izquierda y, luego, hacia la derecha.

Haz clic en la URL del servidor web para visitar tu sitio en el navegador web. Deberías ver una página similar a esta, aunque tu versión podría mostrar 127.0.0.1:8887 como la dirección:

00-push-codelab.png

Siempre actualiza el trabajador de servicio

Durante el desarrollo, es útil asegurarse de que el trabajador de servicio esté siempre actualizado y tenga los cambios más recientes.

Sigue estos pasos para configurar la función en Chrome:

  1. Ve a la pestaña Push Codelab.
  2. Abre Herramientas para desarrolladores: Ctrl + Mayúsculas + I en Windows y Linux, Cmd + Opción + I en macOS.
  3. Selecciona el panel Application, haz clic en la pestaña Service Workers y marca la casilla de verificación Update on Reload. Cuando esta casilla de verificación está habilitada, el service worker se actualiza de forma forzada cada vez que se vuelve a cargar la página.

Código completado

En tu directorio app, observa que tienes un archivo vacío llamado sw.js. Este archivo será tu service worker. Por ahora, puede permanecer vacío. Le agregarás código más adelante.

Primero, debes registrar este archivo como tu service worker.

Se carga tu página app/index.htmlscripts/main.js. En este archivo JavaScript, registras tu service worker.

Agrega el siguiente código a scripts/main.js:

if ('serviceWorker' in navigator && 'PushManager' in window) {
  console.log('Service Worker and Push are supported');

  navigator.serviceWorker.register('sw.js')
  .then(function(swReg) {
    console.log('Service Worker is registered', swReg);

    swRegistration = swReg;
  })
  .catch(function(error) {
    console.error('Service Worker Error', error);
  });
} else {
  console.warn('Push messaging is not supported');
  pushButton.textContent = 'Push Not Supported';
}

Este código verifica si tu navegador admite los Service Workers y los mensajes push. Si se admiten, el código registra tu archivo sw.js.

Probar

Para verificar tus cambios, actualiza la pestaña Push Codelab en el navegador.

Verifica la consola en las Herramientas para desarrolladores de Chrome para ver un Service Worker is registered message, como se muestra a continuación:

Obtén las claves del servidor de aplicaciones

Para trabajar con este codelab, debes generar claves del servidor de aplicaciones. Puedes hacerlo en el sitio complementario: web-push-codelab.glitch.me

Aquí puedes generar un par de claves pública y privada.

push-codelab-04-companion.png

Copia tu clave pública en scripts/main.js y reemplaza el valor <Your Public Key>:

const applicationServerPublicKey = '<Your Public Key>';

Importante: Nunca debes colocar tu clave privada en tu app web.

Código completado

Por el momento, el botón Habilitar de la app web está inhabilitado y no se puede hacer clic en él. Esto se debe a que es una buena práctica inhabilitar el botón de envío de forma predeterminada y habilitarlo después de saber que el navegador admite la mensajería push y de que puedes verificar si el usuario está suscrito a la mensajería o no.

Deberás crear dos funciones en scripts/main.js:

  • initializeUI, para verificar si el usuario está suscrito actualmente
  • updateBtn, para habilitar el botón y cambiar el texto según si el usuario está suscrito o no

Agrega una función initializeUI a main.js de la siguiente manera:

function initializeUI() {
  // Set the initial subscription value
  swRegistration.pushManager.getSubscription()
  .then(function(subscription) {
    isSubscribed = !(subscription === null);

    if (isSubscribed) {
      console.log('User IS subscribed.');
    } else {
      console.log('User is NOT subscribed.');
    }

    updateBtn();
  });
}

Tu nuevo método usa el swRegistration del paso anterior, obtiene la propiedad pushManager de él y llama a getSubscription() en ese objeto.

pushManager. getSubscription() devuelve una promesa que se resuelve con la suscripción actual, si existe. De lo contrario, devuelve null. Con esto, puedes verificar si el usuario ya se suscribió, establecer el valor de isSubscribed y, luego, llamar a updateBtn() para actualizar el botón.

Agrega la función updateBtn() a main.js:

function updateBtn() {
  if (isSubscribed) {
    pushButton.textContent = 'Disable Push Messaging';
  } else {
    pushButton.textContent = 'Enable Push Messaging';
  }

  pushButton.disabled = false;
}

Esta función habilita el botón y cambia el texto según si el usuario está suscrito o no.

Lo último que debes hacer es llamar a initializeUI() cuando tu service worker esté registrado en main.js:

navigator.serviceWorker.register('sw.js')
.then(function(swReg) {
  console.log('Service Worker is registered', swReg);

  swRegistration = swReg;
  initializeUI();
})

Probar

Actualiza la pestaña Push Codelab. Deberías ver que el botón Enable Push Messaging ahora está habilitado (puedes hacer clic en él) y deberías ver User is NOT subscribed en la consola.

A medida que avances en el resto de este codelab, deberías ver que el texto del botón cambia cada vez que te suscribes o cancelas la suscripción.

Código completado

Por el momento, el botón Enable Push Messaging no hace mucho. Sin embargo, podemos solucionarlo.

En la función initializeUI(), agrega un objeto de escucha de clics para tu botón:

function initializeUI() {
  pushButton.addEventListener('click', function() {
    pushButton.disabled = true;
    if (isSubscribed) {
      // TODO: Unsubscribe user
    } else {
      subscribeUser();
    }
  });

  // Set the initial subscription value
  swRegistration.pushManager.getSubscription()
  .then(function(subscription) {
    isSubscribed = !(subscription === null);

    updateSubscriptionOnServer(subscription);

    if (isSubscribed) {
      console.log('User IS subscribed.');
    } else {
      console.log('User is NOT subscribed.');
    }

    updateBtn();
  });
}

Cuando el usuario hace clic en el botón, lo inhabilitas para asegurarte de que no pueda volver a hacer clic en él, ya que suscribirse a la mensajería push puede llevar algún tiempo.

Luego, llamas a subscribeUser() si el usuario no está suscrito. Para ello, deberás pegar el siguiente código en scripts/main.js:

function subscribeUser() {
  const applicationServerKey = urlB64ToUint8Array(applicationServerPublicKey);
  swRegistration.pushManager.subscribe({
    userVisibleOnly: true,
    applicationServerKey: applicationServerKey
  })
  .then(function(subscription) {
    console.log('User is subscribed.');

    updateSubscriptionOnServer(subscription);

    isSubscribed = true;

    updateBtn();
  })
  .catch(function(error) {
    console.error('Failed to subscribe the user: ', error);
    updateBtn();
  });
}

Analicemos qué hace este código y cómo suscribe al usuario para recibir mensajes push.

Primero, toma la clave pública del servidor de aplicaciones, que está codificada en Base64 de forma segura para URLs, y conviértela en un UInt8Array, ya que esta es la entrada esperada de la llamada a subscribe(). La función urlB64ToUint8Array() se encuentra en la parte superior de scripts/main.js.

Después de convertir el valor, llama al método subscribe() en el objeto pushManager de tu trabajador de servicio y pasa la clave pública de tu servidor de aplicaciones y el valor userVisibleOnly: true.

const applicationServerKey = urlB64ToUint8Array(applicationServerPublicKey);
swRegistration.pushManager.subscribe({
  userVisibleOnly: true,
  applicationServerKey: applicationServerKey
})

El parámetro userVisibleOnly es una garantía de que mostrarás una notificación cada vez que se envíe un mensaje push. Actualmente, este valor es obligatorio y debe ser verdadero.

Si se llama a subscribe(), se devuelve una promesa que se resolverá después de los siguientes pasos:

  1. El usuario otorgó permiso para mostrar notificaciones.
  2. El navegador envió una solicitud de red a un servicio push para obtener los datos necesarios para generar una PushSubscription.

La promesa subscribe() se resolverá con un PushSubscription si estos pasos se completaron correctamente. Si el usuario no otorga permiso o si hay algún problema para suscribirlo, la promesa se rechazará con un error. Esto te proporciona la siguiente cadena de promesas en tu codelab:

swRegistration.pushManager.subscribe({
  userVisibleOnly: true,
  applicationServerKey: applicationServerKey
})
.then(function(subscription) {
  console.log('User is subscribed.');

  updateSubscriptionOnServer(subscription);

  isSubscribed = true;

  updateBtn();

})
.catch(function(err) {
  console.log('Failed to subscribe the user: ', err);
  updateBtn();
});

Con esto, obtienes una suscripción y tratas al usuario como suscriptor, o bien capturas un error y lo registras en la consola. En ambos casos, llamas a updateBtn() para asegurarte de que el botón se vuelva a habilitar y tenga el texto adecuado.

En una aplicación real, la función updateSubscriptionOnServer() es donde enviarías tus datos de suscripción a un backend, pero, para el codelab, simplemente mostrarás la suscripción en tu IU. Agrega la siguiente función a scripts/main.js:

function updateSubscriptionOnServer(subscription) {
  // TODO: Send subscription to application server

  const subscriptionJson = document.querySelector('.js-subscription-json');
  const subscriptionDetails =
    document.querySelector('.js-subscription-details');

  if (subscription) {
    subscriptionJson.textContent = JSON.stringify(subscription);
    subscriptionDetails.classList.remove('is-invisible');
  } else {
    subscriptionDetails.classList.add('is-invisible');
  }
}

Probar

Ve a la pestaña Push Codelab, actualiza la página y haz clic en el botón. Deberías ver un mensaje de permiso como este:

Si otorgas el permiso, deberías ver User is subscribed registrado en la consola. El texto del botón cambiará a Disable Push Messaging y podrás ver la suscripción como datos JSON en la parte inferior de la página.

Código completado

Una cosa que aún no has controlado es qué sucede si el usuario bloquea la solicitud de permiso. Esto requiere una consideración especial, ya que, si el usuario bloquea el permiso, tu app web no podrá volver a mostrar la solicitud de permiso ni suscribir al usuario. Al menos debes inhabilitar el botón de envío para que el usuario sepa que no se puede usar.

El lugar obvio para controlar esta situación es la función updateBtn(). Todo lo que debes hacer es verificar el valor de Notification.permission, de la siguiente manera:

function updateBtn() {
  if (Notification.permission === 'denied') {
    pushButton.textContent = 'Push Messaging Blocked';
    pushButton.disabled = true;
    updateSubscriptionOnServer(null);
    return;
  }

  if (isSubscribed) {
    pushButton.textContent = 'Disable Push Messaging';
  } else {
    pushButton.textContent = 'Enable Push Messaging';
  }

  pushButton.disabled = false;
}

Sabes que, si el permiso es denied, el usuario no puede suscribirse y no puedes hacer nada más, por lo que inhabilitar el botón de forma permanente es el mejor enfoque.

Probar

Como ya otorgaste permiso para tu app web en el paso anterior, debes hacer clic en la i dentro de un círculo en la barra de URL y cambiar el permiso de Notificaciones a Usar la configuración predeterminada global (Preguntar).

Después de cambiar este parámetro de configuración, actualiza la página, haz clic en el botón Habilitar mensajería push y selecciona Bloquear en el diálogo de permisos. El botón se inhabilitará y mostrará el texto Mensajes push bloqueados.

Con este cambio, ahora puedes suscribir al usuario, ya que se tuvieron en cuenta las posibles situaciones de permisos.

Código completado

Antes de aprender a enviar un mensaje push desde tu backend, debes considerar qué sucederá cuando un usuario suscrito reciba un mensaje push.

Cuando activas un mensaje push, el navegador lo recibe, determina para qué trabajador de servicio es el mensaje push, activa ese trabajador de servicio y envía un evento push. Debes escuchar este evento y mostrar una notificación como resultado.

Agrega el siguiente código a tu archivo sw.js:

self.addEventListener('push', function(event) {
  console.log('[Service Worker] Push Received.');
  console.log(`[Service Worker] Push had this data: "${event.data.text()}"`);

  const title = 'Push Codelab';
  const options = {
    body: 'Yay it works.',
    icon: 'images/icon.png',
    badge: 'images/badge.png'
  };

  event.waitUntil(self.registration.showNotification(title, options));
});

Analicemos este código paso a paso. Agrega un objeto de escucha de eventos para detectar eventos push en tu trabajador de servicio:

self.addEventListener('push', ... );

(A menos que ya hayas trabajado con Web Workers, es probable que self sea nuevo para ti. En un archivo de Service Worker, self hace referencia al Service Worker en sí.

Cuando se recibe un mensaje push, se llama al objeto de escucha de eventos y se crea una notificación llamando a showNotification() en la propiedad registration del trabajador de servicio. showNotification() requiere un title. También puedes proporcionarle un objeto options para establecer un mensaje, un ícono y una insignia del cuerpo. (Al momento de escribir este texto, la insignia solo se usa en Android).

const title = 'Push Codelab';
const options = {
  body: 'Yay it works.',
  icon: 'images/icon.png',
  badge: 'images/badge.png'
};
self.registration.showNotification(title, options);

Lo último que debes abordar en tu control de eventos push es event.waitUntil(). Este método toma una promesa para permitir que el navegador mantenga tu service worker activo y en ejecución hasta que se resuelva la promesa pasada.

Para que el código anterior sea un poco más fácil de entender, puedes reescribirlo de la siguiente manera:

const notificationPromise = self.registration.showNotification(title, options);
event.waitUntil(notificationPromise);

Ahora que analizaste el evento de envío, probemos uno.

Probar

Con el control de eventos push en el service worker, puedes activar un evento push falso para probar qué sucede cuando se recibe un mensaje.

En tu app web, suscríbete a la mensajería push y asegúrate de ver User IS subscribed en la consola. En el panel Aplicación de Herramientas para desarrolladores, en la pestaña Service Workers, haz clic en el botón Push:

Después de hacer clic en Push, deberías ver una notificación como esta:

Nota: Si este paso no funciona, intenta anular el registro de tu service worker con el vínculo Unregister en el panel Application de Herramientas para desarrolladores, espera a que se detenga el service worker y, luego, vuelve a cargar la página.

Código completado

Si haces clic en una de estas notificaciones, verás que no sucede nada. Puedes controlar los clics en las notificaciones escuchando los eventos notificationclick en tu service worker.

Para comenzar, agrega un objeto de escucha notificationclick en sw.js:

self.addEventListener('notificationclick', function(event) {
  console.log('[Service Worker] Notification click received.');

  event.notification.close();

  event.waitUntil(
    clients.openWindow('https://developers.google.com/web')
  );
});

Cuando el usuario haga clic en la notificación, se llamará al objeto de escucha de eventos notificationclick.

Primero, el código cierra la notificación en la que se hizo clic:

event.notification.close();

Luego, se abre una nueva ventana o pestaña, y se carga la URL https://developers.google.com/web. Puedes cambiarlo si lo deseas.

event.waitUntil(
    clients.openWindow('https://developers.google.com/web/')
  );

event.waitUntil() garantiza que el navegador no finalice el service worker antes de que se muestre la nueva ventana o pestaña.

Probar

Intenta activar un mensaje push en Herramientas para desarrolladores y haz clic en la notificación. Ahora verás que se cierra la notificación y se abre una pestaña nueva.

Viste que tu app web puede mostrar una notificación con DevTools y analizaste cómo cerrar la notificación con un clic. El siguiente paso es enviar un mensaje push real.

Normalmente, esto requeriría enviar una suscripción desde una página web a un backend. Luego, el backend activaría un mensaje push realizando una llamada a la API del extremo en la suscripción.

Esto está fuera del alcance de este codelab, pero puedes usar el sitio complementario (web-push-codelab.glitch.me) para activar un mensaje push real. Pega la suscripción en la parte inferior de la página:

Luego, pégalo en el sitio complementario en el área de texto Subscription to Send To:

En Texto para enviar, agrega cualquier cadena que quieras enviar con el mensaje push.

Haz clic en el botón Enviar mensaje push.

Luego, deberías recibir un mensaje push. El texto que usaste se registrará en la consola.

Esto te permitirá probar el envío y la recepción de datos, y manipular las notificaciones como resultado.

La app complementaria es solo un servidor de nodos que usa la biblioteca web-push para enviar mensajes. Vale la pena revisar la organización web-push-libs en GitHub para ver qué bibliotecas están disponibles para enviar mensajes push por ti. Esto controla muchos detalles para activar los mensajes push.

Puedes ver todo el código del sitio complementario aquí.

Código completado

Lo único que falta es la capacidad de anular la suscripción de un usuario a las notificaciones push. Para ello, debes llamar a unsubscribe() en un PushSubscription.

De vuelta en tu archivo scripts/main.js, cambia el objeto de escucha de clics pushButton en initializeUI() por lo siguiente:

pushButton.addEventListener('click', function() {
  pushButton.disabled = true;
  if (isSubscribed) {
    unsubscribeUser();
  } else {
    subscribeUser();
  }
});

Ten en cuenta que ahora llamarás a una nueva función unsubscribeUser(). En esta función, obtienes la suscripción actual y llamas a unsubscribe() en ella. Agrega el siguiente código a scripts/main.js:

function unsubscribeUser() {
  swRegistration.pushManager.getSubscription()
  .then(function(subscription) {
    if (subscription) {
      return subscription.unsubscribe();
    }
  })
  .catch(function(error) {
    console.log('Error unsubscribing', error);
  })
  .then(function() {
    updateSubscriptionOnServer(null);

    console.log('User is unsubscribed.');
    isSubscribed = false;

    updateBtn();
  });
}

Analicemos esta función paso a paso.

Primero, obtén la suscripción actual llamando a getSubscription():

swRegistration.pushManager.getSubscription()

Esto devuelve una promesa que se resuelve con un PushSubscription si existe uno; de lo contrario, devuelve null. Si hay una suscripción, llamas a unsubscribe() en ella, lo que invalida el PushSubscription.

swRegistration.pushManager.getSubscription()
.then(function(subscription) {
  if (subscription) {
    // TODO: Tell application server to delete subscription
    return subscription.unsubscribe();
  }
})
.catch(function(error) {
  console.log('Error unsubscribing', error);
})

Si se llama a unsubscribe(), se muestra una promesa, ya que puede tardar un tiempo en completarse. Devuelves esa promesa para que el siguiente then() de la cadena espere a que finalice unsubscribe(). También agregarás un controlador catch en caso de que la llamada a unsubscribe() genere un error. Después de esto, puedes actualizar la IU.

.then(function() {
  updateSubscriptionOnServer(null);

  console.log('User is unsubscribed.');
  isSubscribed = false;

  updateBtn();
})

Probar

Deberías poder presionar Enable Push Messaging o Disable Push Messaging en tu app web, y los registros mostrarán que el usuario se suscribió y canceló la suscripción.

¡Felicitaciones por completar este codelab!

En este codelab, se mostró cómo comenzar a agregar notificaciones push a tu app web. Si quieres obtener más información sobre lo que pueden hacer las notificaciones web, consulta estos documentos.

Si deseas implementar notificaciones push en tu sitio, te recomendamos que agregues compatibilidad con navegadores más antiguos o que no cumplan con los estándares y que usen GCM. Obtén más información aquí.

Lecturas adicionales

Entradas de blog relevantes