DEV Community

Nicholas Mendez
Nicholas Mendez

Posted on • Updated on

Background Events in PWAs

Introductions 👋🏽

Hi, I'm Nicholas Mendez a Caribbean-based Web Developer hailing from the country of Trinidad and Tobago 🇹🇹. I am a die-hard advocate for web apps and as such, I am always on the lookout for new cutting-edge capabilities making their way to the platform. These APIs can seem daunting to many so it is my hope that I can break it down with examples and explanations.

Background Events 🎉

In the traditional web model, anything that happens on the app is usually initiated by the user or client.

For example: When you request a page on a movie app; it may fetch movie data from the server. You can add a refresh button so it can retrieve any updates since the page load.

However, the service worker technology of Progressive Web Applications can be used to perform background operations even when the app isn't open or even online (°ロ°)!

This is useful if you want to trigger a time-based event in the background such as:

  • Updating an offline cache
  • Pushing new state from the server to the app
  • Triggering a reminder notification

Note This article will not provide complete code examples but instead highlight the main API calls, you can refer to the references below for an in-depth look.

Methodologies 💡

Currently, there are three ways (AFAIK) in which you can achieve this.

Push Notifications

Push notifications allow you to send push messages from the server to one or many clients. Firebase Cloud Messaging (FCM) is a free service you can use to deliver push messages to devices. These messages appear as push notifications on the device (once permission has been granted beforehand). View a demo. Notifications can also be configured to be silent data-messages so your app can receive data from the server without alerting the user.

//using fcm in sw.js (service worker)
//note: these URLs only resolve if app is hosted on firebase
//use cdn URLs if app is hosted elsewhere
importScripts('/__/firebase/7.12.0/firebase-app.js');
importScripts('/__/firebase/7.12.0/firebase-messaging.js');
importScripts('/__/firebase/init.js');

const messaging = firebase.messaging();

messaging.setBackgroundMessageHandler(function(payload) {
  console.log('[firebase-messaging-sw.js] Received background message ', payload);
  // Customize notification here
  // do other data fetching here too
  const notificationTitle = 'Background Message Title';
  const notificationOptions = {
    body: 'Background Message body.',
    icon: './assets/img/192.png'
  };
  //if we want to alert the user else omit
  return self.registration.showNotification(notificationTitle, notificationOptions);
});

Enter fullscreen mode Exit fullscreen mode

Periodic Background Sync

This method is useful when you want to implement a best-effort approach to fetching data when the client isn't always guaranteed to have a network connection. The API allows you to configure the ServiceWorker to schedule some work at a time interval such as showing notifications or retrieving data from the server every 24 hours.

If you want to schedule a one-off event you can configure the service worker to unregister the periodic sync after it triggers for the first time.

//request permission and register periodicSync in ./main.js
// Check if service workers are supported
if ('serviceWorker' in navigator) {
  const registration = await navigator.serviceWorker.ready;
  // Check if periodicSync is supported
  if ('periodicSync' in registration) {
    // Request permission
    const status = await navigator.permissions.query({
      name: 'periodic-background-sync',
    });
    if (status.state === 'granted') {
      try {
        // Register new sync every 24 hours
        await registration.periodicSync.register('news', {
          minInterval: 24 * 60 * 60 * 1000, // 1 day
        });
        console.log('Periodic background sync registered!');
      } catch(e) {
        console.error(`Periodic background sync failed:\n${e}`);
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Then in the service worker:

//setup handler in sw.js
self.addEventListener('periodicsync', (event) => {
  if (event.tag === 'news') {
    console.log('Fetching news in the background!');
    event.waitUntil(fetchAndCacheNews());
    //for one off event
    registration.periodicSync.unregister('periodicsync');
  }
});
Enter fullscreen mode Exit fullscreen mode

Notification Trigger

If you wish to schedule a local notification from the device even while offline, notification triggers are your best bet. Instead of being received as a push message from the server, you can have your app flash a 'local' notification message even if there is no internet connection. This can be useful for reminders and alarms that do not require new data from the server. You just need to provide a timestamp to the notification API and the message will alert the user when that time arrives.

const createScheduledNotification = async (tag, title, timestamp) => {
  const registration = await navigator.serviceWorker.getRegistration();
  registration.showNotification(title, {
    tag: tag,
    body: "This notification was scheduled 30 seconds ago",
    showTrigger: new TimestampTrigger(timestamp + 30 * 1000)
  });
};
Enter fullscreen mode Exit fullscreen mode

I have a full demo of Notification Triggers available on Repl.it

Wrapping Up 🎁

Thanks for taking the time to read this long post. If there's anything I left out or mistaken please sound off the comments below!

References 📚

Code snippets for Periodic Sync and Notification Triggers are cited form here:

web.dev: Periodic Background Sync
web.dev: Notification Triggers
Google Developers: Push Notifications

Top comments (0)