Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Service worker - network first then cache with fallback to static page

I want to add service worker to my site to offer a good offline experience to my site users. (Site back-end is PHP)

I'm still new to Javascript promises and service workers, but here is what i reached so far :

my index.php page has this script to register service worker

<script> 
    if ('serviceWorker' in navigator) { navigator.serviceWorker.register('sw.js')};
</script>

and for service worker file i use the following code

var CACHE_STATIC_NAME = 'static-v74';
var CACHE_DYNAMIC_NAME = 'dynamic-v74';

self.addEventListener('install', function (event) {
  console.log('Installing Service Worker ...', event);
  event.waitUntil(
    caches.open(CACHE_STATIC_NAME)
      .then(function (cache) {
        console.log('Precaching App Shell');
        cache.addAll([
          'offline.php', // offline page
          'assets/bundle.css',
          'assets/bundle.js',
          'assets/images/logo.jpg',
          'assets/images/favicon.ico'
        ]);
      })
  )
});

self.addEventListener('activate', function (event) {
  console.log('Activating Service Worker ....', event);
  event.waitUntil(
    caches.keys()
      .then(function (keyList) {
        return Promise.all(keyList.map(function (key) {
          if (key !== CACHE_STATIC_NAME && key !== CACHE_DYNAMIC_NAME) {
            console.log('Removing old cache.', key);
            return caches.delete(key);
          }
        }));
      })
  );
  return self.clients.claim();
});

self.addEventListener('fetch', function(event) {
  event.respondWith(
    // Try the network
    fetch(event.request)
      .then(function(res) {
        return caches.open(CACHE_DYNAMIC_NAME)
          .then(function(cache) {
            // Put in cache if succeeds
            cache.put(event.request.url, res.clone());
            return res;
          })
      })
      .catch(function(err) {
          // Fallback to cache
          return caches.match(event.request);
      })
  );
});

in fact this code is working fine as expected :

(1) It registers the service worker & take copy of my static cache files. (2) When activating a new service worker it removes the old cache. (3) With every new request it fetches from network first & if succeeded it puts a fresh copy of request in cache. (4) If failed to make network request when offline it gives back the user a fresh copy of last network request.

what i want to achieve her is (in case - user is offline - and try to request a new page that isn't in cache, he should be redirected to the (offline.php) page which was saved in the static cache) like the following

// If both (network & cache) fail, show a generic fallback:
return caches.match('offline.php');

but i have a problem that i don't know where exactly should i add this line in my (sw.js) file within (fetch) step, i really tried reading more about service workers & Javascript promises and tried adding this line several times different place in my code, but every time neither make it work but misses up with the previous scenario or keep the scenario but fail to achieve this goal.

Really appreciate your kind help & many thanks in advance.

like image 988
Ramy Ahmed Avatar asked Apr 28 '18 09:04

Ramy Ahmed


People also ask

Can service workers access cache?

Using a Service worker you can easily set an app up to use cached assets first, thus providing a default experience even when offline, before then getting more data from the network (commonly known as Offline First).

What is service worker cache?

A service worker intercepts network-type HTTP requests and uses a caching strategy to determine what resources should be returned to the browser.

How do I create a dynamic cache in a service worker?

We ll put the request into the caches - caches. put with the response clone returned from the fetch promise. Return the response. If promise got rejected fetch will go to caches (pre-cache) to match the request URL request.


1 Answers

In your fetch event listener:

.catch(function(err) {
      // Fallback to cache
      return caches.match(event.request);
  })

caches.match(event.request) in fact returns a Promise that resolves to undefined if no match is found from the cache.

Check for undefined and return offline.php from cache:

return caches.match(event.request)
  .then(function(res){
    if (res === undefined) { 
      // get and return the offline page
    } 
    return res;
})
like image 189
pate Avatar answered Sep 23 '22 00:09

pate