Cómo usar un Service Worker para administrar notificaciones

Kate Jeffreys
Kate Jeffreys

En este codelab, usarás un service worker para administrar notificaciones. En estas instrucciones, se supone que ya conoces los service workers y los conceptos básicos para solicitar permiso de notificación y enviar notificaciones. Si necesitas un repaso sobre las notificaciones, consulta el codelab de Cómo comenzar a usar la API de Notifications. Para obtener más información sobre los Service Workers, consulta la Introducción a los Service Workers de Matt Gaunt.

Familiarízate con la app de ejemplo y el código de inicio

Comienza por ver la app en vivo en la nueva pestaña de Chrome:

  1. Presiona "Control + Mayúsculas + J" (o "Comando + Opción + J" en Mac) para abrir DevTools.
  2. Haz clic en la pestaña Consola.

  3. Asegúrate de que la opción Info esté seleccionada en el menú desplegable Levels junto al cuadro Filter.

  4. En la consola de Herramientas para desarrolladores de tu app en vivo, deberías ver un mensaje de la consola:

    TODO: Implement getRegistration().

    Este es un mensaje de un código auxiliar de función que implementarás en este codelab.

Ahora, veamos el código de la app de ejemplo.

  1. Echa un vistazo a public/index.js:

    • Hay cuatro stubs para las funciones que implementarás: registerServiceWorker, getRegistration, unRegisterServiceWorker y sendNotification.

    • La función requestPermission solicita el permiso del usuario para enviar notificaciones. Si realizaste el codelab de introducción a la API de Notifications, notarás que aquí se usa su función requestPermission. La única diferencia es que ahora también actualiza la interfaz de usuario después de resolver la solicitud de permiso.

    • La función updateUI actualiza todos los botones y mensajes de la app.

    • La función initializePage realiza la detección de funciones para la capacidad del Service Worker en el navegador y actualiza la interfaz de usuario de la app.

    • La secuencia de comandos espera hasta que se carga la página y, luego, la inicializa.

  2. Abre public/service-worker.js.

    Como sugiere el nombre, agregarás código a la app para registrar este archivo como un service worker.

    Aunque la app aún no usa el archivo, este contiene código inicial que imprimirá un mensaje en la consola cuando se active el trabajador de servicio.

    Agregarás código a public/service-worker.js para controlar las notificaciones cuando el service worker las reciba.

Registra el service worker

En este paso, escribirás código que se ejecutará cuando el usuario haga clic en Registrar el service worker en la IU de la app. Este código registrará public/service-worker.js como un trabajador de servicio.

  1. Abre public/index.js. Reemplaza la función registerServiceWorker por el siguiente código:

    // Use the Service Worker API to register a service worker.
    async function registerServiceWorker() {
      await navigator.serviceWorker.register('./service-worker.js')
      updateUI();
    }
    

    Ten en cuenta que registerServiceWorker usa la declaración async function para que el manejo de promesas sea más conveniente. Esto te permite await el valor resuelto de un Promise. Por ejemplo, la función anterior espera el resultado del registro de un trabajador de servicio antes de actualizar la IU. Consulta await en MDN para obtener más información.

  2. Ahora que el usuario puede registrar un service worker, puedes obtener una referencia al objeto de registro del service worker. En public/index.js, reemplaza la función getRegistration por el siguiente código:

    // Get the current service worker registration.
    function getRegistration() {
      return navigator.serviceWorker.getRegistration();
    }
    

    La función anterior usa la API de Service Worker para obtener el registro actual del service worker, si existe. Esto hace que obtener una referencia al registro del service worker sea un poco más conveniente.

  • Para completar la funcionalidad de registro del service worker, agrega código para cancelar el registro del service worker. Reemplaza la función unRegisterServiceWorker por el siguiente código:

    // Unregister a service worker, then update the UI.
    async function unRegisterServiceWorker() {
      // Get a reference to the service worker registration.
      let registration = await getRegistration();
      // Await the outcome of the unregistration attempt
      // so that the UI update is not superceded by a
      // returning Promise.
      await registration.unregister();
      updateUI();
    }
    

En la pestaña en la que ves la app publicada, vuelve a cargar la página. Los botones Registrar service worker y Cancelar el registro del service worker ahora deberían funcionar.

Envía notificaciones al service worker

En este paso, escribirás código que se ejecutará cuando el usuario haga clic en Enviar una notificación en la IU de la app. Este código creará una notificación, verificará que se haya registrado un trabajador de servicio y, luego, enviará la notificación al trabajador de servicio con su método postMessage.

Abre public/index.js y reemplaza la función sendNotification por el siguiente código:

// Create and send a test notification to the service worker.
async function sendNotification() {
  // Use a random number as part of the notification data
  // (so you can tell the notifications apart during testing!)
  let randy = Math.floor(Math.random() * 100);
  let notification = {
    title: 'Test ' + randy,
    options: { body: 'Test body ' + randy }
  };
  // Get a reference to the service worker registration.
  let registration = await getRegistration();
  // Check that the service worker registration exists.
  if (registration) {
    // Check that a service worker controller exists before
    // trying to access the postMessage method.
    if (navigator.serviceWorker.controller) {
      navigator.serviceWorker.controller.postMessage(notification);
    } else {
      console.log('No service worker controller found. Try a soft reload.');
    }
  }
}

Esto es lo que hace ese código:

  • sendNotification es una función asíncrona, por lo que puedes usar await para obtener una referencia al registro del service worker.

  • El método postMessage del service worker envía datos de la app al service worker. Consulta la documentación de MDN sobre postMessage para obtener más información.

  • El código verifica la presencia de la propiedad navigator.serviceWorker.controller antes de intentar acceder a la función postMessage. navigator.serviceWorker.controller será null si no hay un service worker activo o si se forzó la actualización de la página (Shift+Recargar). Consulta la documentación del controlador ServiceWorker en MDN para obtener más información.

Cómo controlar las notificaciones en el service worker

En este paso, escribirás código en el trabajador de servicio que controlará los mensajes que se publiquen en él y mostrará notificaciones al usuario.

Abre public/service-worker.js. Agrega el siguiente código al final del archivo:

// Show notification when received
self.addEventListener('message', (event) => {
  let notification = event.data;
  self.registration.showNotification(
    notification.title,
    notification.options
  ).catch((error) => {
    console.log(error);
  });
});

A continuación, se incluye una explicación breve:

  • self es una referencia al propio trabajador de servicio.

  • Si bien el service worker ahora controla la visualización de las notificaciones, la IU principal de la app sigue siendo responsable de obtener el permiso de notificación del usuario. Si no se otorga el permiso, se rechaza la promesa que devuelve showNotification. El código anterior usa un bloque catch para evitar un error de rechazo Promise no detectado y controlar este error de una manera un poco más fluida.

Continúa con el siguiente codelab de esta serie: Compila un servidor de notificaciones push.