Agrega notificaciones push a una aplicación web

Las notificaciones push brindan una manera simple y eficaz de volver a interactuar con 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 mensajería push
  • Cómo manejar los mensajes push entrantes
  • Cómo mostrar una notificación
  • Cómo responder a clics de notificaciones

Requisitos

  • Chrome 52 o versiones posteriores
  • Servidor web para Chrome o el servidor web que elijas
  • Un editor de texto
  • Conocimientos básicos de HTML, CSS, JavaScript y Herramientas para desarrolladores de Chrome
  • El código de muestra (consulta la 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
  • Descargue el archivo ZIP:

Descargar el código fuente

Si descargas la fuente como un archivo ZIP, descomprimirlo te brindará una carpeta raíz push-notifications-master.

Instale y verifique 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 la tienes, puedes descargarla de Chrome Web Store:

Cómo instalar el servidor web para Chrome

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

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

A continuación, verás este cuadro de diálogo que te permite 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. De esta manera, puedes entregar tu trabajo en curso a través de la URL que se muestra en la sección URL del servidor web del cuadro de diálogo.

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

Luego, para detener y reiniciar el servidor, desliza el botón de activación que se encuentra en Web Server: STARTED hacia la izquierda y de nuevo hacia la derecha.

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

00-push-codelab.png

Actualiza siempre el service worker

Durante el desarrollo, es útil para garantizar que tu service worker esté siempre actualizado y tenga los cambios más recientes.

Para configurar esta opción en Chrome, sigue estos pasos:

  1. Ve a la pestaña Push Codelab.
  2. Abre DevTools: Ctrl-Shift-I en Windows y Linux, Cmd-Option-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 se habilita, el service worker se actualiza forzosamente cada vez que se vuelve a cargar la página.

Código completado

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

Primero, debes registrar este archivo como tu service worker.

Tu página app/index.html carga scripts/main.js. Registras el service worker en este archivo de JavaScript.

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 el navegador admite los service workers y la mensajería push. Si son compatibles, el código registra tu archivo sw.js.

Pruébalo

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

Verifica en la consola de las Herramientas para desarrolladores de Chrome un Service Worker is registered message, de la siguiente manera:

Obtener claves del servidor de la aplicación

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

Aquí puede generar un par de claves públicas y privadas.

push-codelab-04-companion.png

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

const applicationServerPublicKey = '<Your Public Key>';

Importante: Nunca debes poner tu clave privada en tu aplicación web.

Código completado

Por el momento, el botón Habilitar de la aplicación web está inhabilitado y no se podrá hacer clic en él. Esto se debe a que se recomienda inhabilitar el botón push de forma predeterminada y habilitarlo después de saber que el navegador admite la mensajería push y 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 se suscribió
  • 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();
  });
}

El nuevo método usa swRegistration del paso anterior, obtiene la propiedad pushManager y llama a getSubscription() en él.

pushManager. getSubscription() muestra una promesa que se resuelve con la suscripción actual, si existe una. De lo contrario, muestra null. De esta forma, puedes comprobar 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 del botón 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();
})

Pruébalo

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, verás que el texto del botón cambia cada vez que te suscribes o anulas la suscripción.

Código completado

Por el momento, el botón Enable Push Messaging no es muy útil. 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, se inhabilita para asegurarse de que el usuario no puede hacer clic por segunda vez, ya que la suscripción a mensajes push puede demorar un poco.

Luego, llamas a subscribeUser() si el usuario no está suscrito. Para ello, pega 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();
  });
}

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

En primer lugar, toma la clave pública del servidor de la aplicación, que está codificada en base a URL de forma segura, y la conviertes en UInt8Array, ya que esta es la entrada esperada de la llamada subscribe(). La función urlB64ToUint8Array() está en la parte superior de scripts/main.js.

Después de convertir el valor, llama al método subscribe() en el pushManager de tu service worker y pasa la clave pública del servidor de tu app y el valor userVisibleOnly: true.

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

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

Si llamas a subscribe(), se muestra 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 a fin de obtener los datos necesarios para generar un elemento PushSubscription.

La promesa subscribe() se resolverá con un PushSubscription si estos pasos se realizaron de forma correcta. Si el usuario no otorga permiso o si hay algún problema en la suscripción del usuario, la promesa se rechazará con un error. Esto te brinda la siguiente cadena de promesa en el 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();
});

Así, obtendrás una suscripción y tratarás al usuario como suscrito, o bien detectarás un error y lo registrarás en la consola. En ambos casos, debes llamar a updateBtn() para asegurarte de que el botón se vuelva a habilitar y tenga el texto apropiado.

En una aplicación real, la función updateSubscriptionOnServer() es donde envías los datos de tu suscripción a un backend. Sin embargo, para el codelab, simplemente muestras 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');
  }
}

Pruébalo

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 el registro de User is subscribed 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

Algo que aún no has manejado es lo que sucede si el usuario bloquea la solicitud de permiso. Esto requiere una consideración única porque, si el usuario bloquea el permiso, tu aplicación web no podrá volver a mostrar el mensaje del permiso ni suscribirse al usuario. Debes al menos inhabilitar el botón push para que el usuario sepa que no se puede utilizar.

El lugar obvio para manejar esta situación es en la función updateBtn(). Lo único que debes hacer es verificar el valor 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, no se puede suscribir al usuario y no puedes hacer nada más, por lo que la mejor manera de inhabilitar permanentemente el botón es hacerlo.

Pruébalo

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 Notificaciones a Usar configuración predeterminada global (Preguntar).

Después de cambiar esta configuración, actualiza la página, haz clic en el botón Habilitar envío de mensajes y selecciona Block en el diálogo de permisos. El botón se inhabilitará y mostrará el texto Push Messaging Blocked.

Con este cambio, ahora puedes suscribir al usuario teniendo en cuenta las posibles situaciones de permisos.

Código completado

Antes de aprender a enviar un mensaje push desde tu backend, necesitas tener en cuenta lo que realmente sucederá cuando un usuario suscrito reciba un mensaje push.

Cuando activas un mensaje push, el navegador recibe el mensaje push, detecta para qué service worker es el push, activa a ese service worker y envía un evento push. Para escuchar este evento, debes mostrar una notificación.

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

Veamos este código. Estás escuchando eventos push en tu service worker agregando un objeto de escucha de eventos:

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

(A menos que hayas jugado con los trabajadores web antes, es probable que self sea nuevo). En un archivo de service worker, self hace referencia al service worker.

Cuando se reciba un mensaje push, se llamará al objeto de escucha de eventos y se creará una notificación llamando a showNotification() en la propiedad registration del service worker. showNotification() requiere un title; también puedes darle un objeto options para establecer un mensaje, un ícono y una insignia. (La insignia solo se usa en Android al momento de escribir).

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 cubrir en el control de tu evento de push es event.waitUntil(). Este método promete una habilitación para que el navegador mantenga activo tu service worker hasta que se resuelva la promesa que se pasó.

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

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

Ahora que revisaste el evento push, probemos un evento push.

Pruébalo

Con la administración de eventos push en el service worker, puedes activar un evento push falso para probar lo que sucede cuando se recibe un mensaje.

En tu aplicación web, suscríbete a los mensajes push y asegúrate de que veas User IS subscribed en la consola. En la sección Application del panel Application, haga clic en el botón Push:

Cuando hagas clic en Push, deberías ver una notificación como la siguiente:

Nota: Si este paso no funciona, intenta cancelar el registro de tu service worker con el vínculo Unregister del panel de la app 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, notarás que no sucede nada. Para controlar los clics en notificaciones, busca eventos de notificationclick en el 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.

El código primero cierra la notificación en la que se hizo clic:

event.notification.close();

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

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

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

Pruébalo

Vuelve a activar un mensaje push en las Herramientas para desarrolladores y haz clic en la notificación. Ahora verás el cierre de la notificación y una pestaña nueva abierta.

Ya viste que tu app web puede mostrar una notificación con DevTools y viste cómo cerrarla 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 activa un mensaje push realizando una llamada a la API al extremo de la suscripción.

Esto está fuera de alcance para 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éguelo en el sitio complementario en el área de texto Suscripción para enviar a:

En Texto a enviar, agrega cualquier string que desees enviar con el mensaje de aplicación.

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 debería darte la oportunidad de probar el envío y la recepción de datos, y de manipular notificaciones como resultado.

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

Puede 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 de push. Para ello, debes llamar a unsubscribe() en un PushSubscription.

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

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

Observa que ahora llamarás a una nueva función unsubscribeUser(). En esta función, obtendrás la suscripción actual y llamarás a unsubscribe(). 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.

En primer lugar, llama a getSubscription() para obtener la suscripción actual:

swRegistration.pushManager.getSubscription()

Esto muestra una promesa que se resuelve con PushSubscription, si existe. De lo contrario, muestra null. Si hay una suscripción, llamas a unsubscribe(), que hace que PushSubscription no sea válida.

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

Llamar a unsubscribe() muestra una promesa, ya que puede tomar un tiempo en completarse. Muestras esa promesa para que el próximo then() de la cadena espere a que unsubscribe() finalice. También agregarás un controlador de captura en caso de que llamar a unsubscribe() genere un error. Luego, podrás actualizar la IU.

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

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

  updateBtn();
})

Pruébalo

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

¡Felicitaciones por completar este codelab!

Este codelab te 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 quieres implementar notificaciones push en tu sitio, puede que te interese agregar compatibilidad con navegadores anteriores o navegadores que no cumplan con los estándares que usan GCM. Obtén más información aquí.

Lecturas adicionales

Entradas de blog relevantes