Workbox - Background Sync - Offline Post - Replay Events when the browser is back online doesn't get triggered

I am using workbox v4.3.1 to provide offline capability to the users of the web application.

While everything works perfectly in Chrome as you would expect a PWA to work (i.e everything is cached locally, all the updates from the APP are captured in IndexedDB and synced back to the server when the application is back online.

However the major use case for me is to provide support for iOS Safari and as a PWA.

While all the pages are cached locally using the Service Worker in Safari and all the offline updates are also captured in the indexed DB as shown below,

enter image description here

However, when the connection returns online, the sync event is not triggered by the browser (Safari in this case). While background sync is not supported natively by Safari, I would expect that when I refresh the page, SW initialisation should trigger the sync event manually if it finds some data to be refreshed to the server in the indexed DB.

But this is not happening and I tried to manually listen for the "message" - "replayRequests" and then replay the requests - that did not work as well.

Any help here would be appreciated. Here is the service worker code for reference.

// If we're not in the context of a Web Worker, then don't do anything
if ("function" === typeof importScripts) {
  importScripts(
    "https://storage.googleapis.com/workbox-cdn/releases/4.3.1/workbox-sw.js"
  );

  //Plugins
  // Background Sync Plugin.
  const bgSyncPlugin = new workbox.backgroundSync.Plugin("offlineSyncQueue", {
    maxRetentionTime: 24 * 60
  });

  // Alternate method for creating a queue and managing the events ourselves.
  const queue = new workbox.backgroundSync.Queue("offlineSyncQueue");
  workbox.routing.registerRoute(
    matchCb,
    workbox.strategies.networkOnly({
      plugins: [
        {
          fetchDidFail: async ({ request }) => {
            await queue.addRequest(request);
          }
        }
      ]
    }),
    "POST"
  );
  // CacheKeyControlPlugin
  const myCacheKeyPlugin = {
    cacheKeyWillBeUsed: async ({ request, mode }) => {
      normalizedUrl = removeTimeParam(request.url);
      return new Request(normalizedUrl);
    }
  };

  if (workbox) {
    console.info("SW - Workbox is available and successfully installed");
  } else {
    console.info("SW - Workbox unavailable");
  }

  //Intercept all api requests
  var matchCb = ({ url, event }) => {
    // Filter out the presence api calls
    return url.pathname.indexOf("somethingidontwanttocache") == -1;
  };

  function removeTimeParam(urlString) {
    let url = new URL(urlString);
    url.searchParams.delete("time");
    return url.toString();
  }
  /* //Pre cache a page and see if it works offline - Temp code
  workbox.precaching.precache(getPageAPIRequestURLs(), {
    cleanUrls: false
  }); */
  workbox.routing.registerRoute(
    matchCb,
    new workbox.strategies.CacheFirst({
      cacheName: "application-cache",
      plugins: [myCacheKeyPlugin]
    })
  );

  self.addEventListener("message", event => {
    if (event.data === "replayRequests") {
      queue.replayRequests();
    }
  });
}

1 answer

  • answered 2019-09-10 16:26 Jeff Posnick

    workbox-background-sync emulates background sync functionality in browsers that lack native support by replaying queued requests whenever the service worker process starts up. The service worker process is meant to be lightweight and short lived, and is killed aggressively when there's a period of time without any events, and then is started up again in response to further events.

    Reloading a web page may cause the service worker process to start up, assuming it had previously been stopped. But if the service worker is still running, then reloading the page will just cause a fetch event to be fired on the existing process.

    The interval at which a service worker process can remain idle before it's killed is browser-dependent.

    Chrome's DevTools offers a method of inspecting the state of a service worker and starting/stopping it on demand, but I don't believe Safari's DevTools offers that functionality. If you wanted to guanratee that a service worker was stopped and then start it up again, I would quit Safari, reopen it, and then navigate back to your web app.