HTML5 Applications Cache Refresh Mechanism

Quantum
Quest
Algorithms, Math, and Physics

HTML5 applications cache refresh mechanism

In HTML5 applications designed to work offline, caching assets through a Service Worker is essential for smooth, uninterrupted performance. However, this approach often brings up the challenge of ensuring users have access to the latest version of the application when updates are made, as cached files might not refresh automatically. I developed a method to maintain both the reliability of offline access and the ability to update cached assets whenever a new version is available.

Caching assets with a service worker

I start by defining the essential assets to be cached in my Service Worker. These assets include the main index.html file and any core CSS and JavaScript files. By caching these elements, I make sure the application can load properly even when the user is offline. Here is the code snippet for the initial cache setup:


const assetsToCache = [
  '/myapp/index.html',
  // other CSS and JS files
];

self.addEventListener('install', event => {
  event.waitUntil(
    caches.open('my-app-cache').then(cache => {
      return cache.addAll(assetsToCache);
    })
  );
});

In this setup, I register an install event for the Service Worker. When triggered, it opens a cache named 'my-app-cache' and stores the specified files. This provides basic offline functionality by loading cached files when the network is unavailable.

Enabling cache updates via version check

Although caching improves offline functionality, the drawback is that changes to index.html or other files may not reflect automatically for users, as the Service Worker continues serving the cached versions. To tackle this, I implemented a cache refresh mechanism by using a version check.

To trigger this update, I added a message listener in my Service Worker. This listener waits for a signal from the main application, which I send if I detect a newer version. Here’s the code in the Service Worker:


self.addEventListener('message', event => {
  if (event.data === 'refresh_cache') {
    event.waitUntil(refreshCache());
  }
});

function refreshCache() {
  return caches.open('my-app-cache').then(cache => {
    return cache.addAll(assetsToCache);
  });
}

When the main application detects a newer version of the app, it sends a 'refresh_cache' message to the Service Worker, which then runs the refreshCache function to fetch and cache the latest versions of the specified assets.

Checking for updates in the main application

To determine if a newer version of the application is available, I added a version check in the main application. I do this by fetching a version.json file from the server. Here’s the relevant code:


document.addEventListener('DOMContentLoaded', function() {
  const versionUrl = '/myapp/version.json';

  fetch(versionUrl)
    .then(response => {
      if (!response.ok) return;
      return response.json();
    })
    .then(data => {
      if (!data) return;
      const onlineVersion = data.version;
      const localVersion = localStorage.getItem('version');

      if (onlineVersion !== localVersion) {
        if (navigator.serviceWorker && navigator.serviceWorker.controller) {
          navigator.serviceWorker.controller.postMessage('refresh_cache');
          localStorage.setItem('version', onlineVersion);
        }
      }
    })
    .catch(() => {});
});

The version.json file contains the current version number of the application on the server, which I compare with the version stored in localStorage. When they don’t match, I trigger the 'refresh_cache' message to update the Service Worker and refresh the cached assets.

Prompting the user to reload for updated assets

Updating the cache is not enough on its own, as users might still be working with the previously loaded assets. To ensure they see the latest version, I added a notification step that prompts the user to reload the page. Here’s how I handled it:

In the Service Worker:


self.addEventListener('message', event => {
  if (event.data === 'refresh_cache') {
    event.waitUntil(
      refreshCache().then(() => {
        self.clients.matchAll().then(clients => {
          clients.forEach(client => client.postMessage('cache_refreshed'));
        });
      })
    );
  }
});

In the main application:


navigator.serviceWorker.addEventListener('message', event => {
  if (event.data === 'cache_refreshed') {
    if (confirm('A new version is available. Reload now?')) {
      window.location.reload();
    }
  }
});

After refreshing the cache, the Service Worker sends a 'cache_refreshed' message to the main application. When the application receives this message, it prompts the user to reload the page, ensuring they access the latest assets.

Conclusion

By using this cache refresh mechanism, I balance the benefits of offline access with the necessity of updating to the latest version. Users can continue working offline without interruption while receiving updates whenever they reconnect to the internet.

For more insights into this topic, you can find the details here.