Awkward Change Flow or: How I Learned to Stop Worrying and Love the Application Cache

Snappy title, yes...? Let's just move on...

The application cache API introduced by HTML5 is a very powerful tool which is particularly useful for web applications whose intended primary use is on mobile devices. Not only can it significantly speed up the load time of your app, but if implemented correctly you can make your application work quite well even when the user is not connected to the Internet. I'm not going to write too much about how the application cache works as it has already been covered very well elsewhere. If that's what you're looking for I'd recommend the following articles:

These articles describe the API really well, and also notes several of the "gotchas" that might trip you up if you're not aware of them. If not taken into serious consideration these gotchas can make further development of your application turn into a painful and frustrating experience. So, I wanted to document a way of easing the pain.

Automatically updating the cache manifest file when files change

One of the more annoying quirks of developing with the application cache is that it has no mechanism for detecting updates to already cached resources. It doesn't re-cache any of the files in the manifest till the contents of the manifest itself has changed. The way most people get around this is by adding a comment to the top or bottom of the manifest with a timestamp or revision number. However, constantly updating this comment every time you want the browser to recache your resource will get tedious really quick.

So, we need a way of automatically updating this comment in the manifest whenever a change has been detected in one of our cached files! To do this I prefer to use Grunt, which is a tool used to run automated tasks. It has a bunch of useful plugins, and for our purposes we need the grunt-manifest and grunt-contrib-watch packages. grunt-manifest enables you to define your cache manifest configuration in your Gruntfile as a JSON object and regenerate on demand, while grunt-contrib-watch will run any task you specify when any of the files it's watching is updated... You can probably see where this is going...

To install all the necessary packages, just run the following commands in your project folder (assuming you already have installed npm):

npm install -g grunt-cli  
npm install grunt  
npm install grunt-manifest  
npm install grunt-contrib-watch  

Next, create a Gruntfile.js in your project root, which is where we will define our automated tasks:

module.exports = function(grunt) {

    // Load plugins
    grunt.loadNpmTasks('grunt-manifest');
    grunt.loadNpmTasks('grunt-contrib-watch');

    // Make a list of paths/files to cache
    var cache = ['./public/js/*', './public/img/*',
                 './public/css/*'];

    grunt.initConfig({

        // Define how your manifest file should be constructed
        manifest: {
            generate: {
                options: {
                    network: ['http://*', 'https://*'],
                    timestamp: true
                },
                src: cache,
                dest: 'manifest.appcache'
            }
        },

        // Now setup the watch task to monitor the same files
        // that are referenced in the CACHE section of your
        // manifest, and fire the manifest task whenever
        // a change is detected
        watch: {
            scripts: {
                files: cache,
                tasks: ['manifest']
            }
        }
    });

};

Here we define two tasks, manifest and watch. The manifest task will generate a new manifest file and update the timestamp in the comment at the top of the file. The watch task will monitor the files specified, and is configured to perform the manifest task when a change has been detected. So, now we can have the manifest be automatically generated whenever a file changes by running the command grunt watch. This task can yet again be made part of a separate run-dev task which could also fire up a local node-server, live-reload, automatic LESS compilation, etc. to remove any other tasks you do manually when starting up a development server.

Frederik Nakstad

Read more posts by this author.

Tokyo, Japan