The pitfalls of Html5 applicationCache and how to avoid it

There are only a handful of methods for applicationCache, but getting it to work the first time isn’t always easy. Some well-written tutorials are available such as this one:

   http://www.thecssninja.com/javascript/how-to-create-offline-webapps-on-the-ip...

So, I am going to focus on the pitfalls you might encounter and need help with. Code to avoid most of the pitfalls can be found at the end of this article.

Pitfall 1: Didn't know you need to call update() method

14.00 Normal 0 false false false EN-US X-NONE X-NONE

After the application loads the first time, you need to call update() every time. Otherwise the web browser might (or might not) check the cache. If the implementation doesn't, you will be stuck with the old application forever.

Pitfall 2: Misunderstood update() method will reload the app

The update() method only updates the cache—not the web application or anything that is loaded. This is obvious when you get it working for the first time. The easiest way to update the page when the cache is ready is by reloading it:

location.reload(); 

14.00 Normal 0 false false false EN-US X-NONE X-NONE

It is also desirable to let users decide whether they want to reload the application when the cache is first updated. If you are not careful you might lose user data in the session.

Pitfall 3: Poll and event, you need both

At first, applicationCache may appear to be unreliable because updating the cache is a process that takes time, and happens in the background. You can either poll it repeatedly on an interval, or you need to poll the status AND register a listener. If you register for the event after CACHEREADY, you will not be called.

Pitfall 4: The cake is ready in-between the poll and event registering, so you missed it

You might poll the status and if it is not ready, listen to the CACHEREADY event. But, in between the poll and registering of the event you might miss the CACHEREADY event. To avoid it you need to double check this:

    if (appCache.status === appCache.UPDATEREADY) {
         confirmAndUpdate();
    } else {
        appCache.addEventListener('updateready', confirmAndUpdate, false);
        if (appCache.status === appCache.UPDATEREADY) {
            confirmAndUpdate();
        }
    }

Pitfall 5: Update() was called before

This is another pitfall that may make you believe applicationCache is unreliable. If update() is called before the application is reloaded (either programmatically or by the user), the cache might be updated and become CACHEREADY, even if your application has not yet called update() this time.
Therefore, you should not assume registering for the event before calling update() is sufficient. You must still check this:

     (appCache.status === appCache.UPDATEREADY)

Pitfall 6: Reload before CACHEREADY

Again, Cache updates take time. If you reload the application after calling update(), but before CACHEREADY, your application might run on the old cache. If you continue doing that, your device will not have time to update the cache and you may still run on the old cache even after many reloads.

Taking a break

The gist (at the end of the post) solved all these timing pitfalls, so do use it.

Pitfall 7: Manifest type (config on the web server)

This is covered by all tutorials, but still easy to miss—and very easy to forget on the production server! With apache, a one-liner in .htaccess will do it.

    AddType text/cache-manifest .manifest

With tomcat it must be the server's web.xml (webapp's web.xml didn't work).

    <mime-mapping>
        <extension>manifest</extension>
        <mime-type>text/cache-manifest</mime-type>
    </mime-mapping>

Pitfall 8: Missing files in Manifest

In Webkit implementations at least, even when you are connected, a file that is not included in the manifest will not be loaded. Each file you use (either statically linked or loaded by ajax) must be included. Missing files will not be cached, nor loaded.

I like this failfast implementation of WebKit (I didn't' test this negative case in other major browsers). It creates fewer surprises during deployment.

===== the code =====

    github gist

4 responses
Thanks for figuring this out. Here's a simplified version w/o the debugging info that I've added to the onReady function of my cached applicstions:

function handleAppCache() {
if (applicationCache == undefined) {
return;
}

if (applicationCache.status == applicationCache.UPDATEREADY) {
applicationCache.swapCache();
location.reload();
return;
}

applicationCache.addEventListener('updateready', handleAppCache, false);
}

See an example at http://scratch.pageforest.com

Thanks, Mike! It is much better.
This works fine in Chrome for Windows but in Chrome for Android never update the application
Thank you for posting this. I don't see the difference between pitfalls 4 and 5, but maybe I will one day. More importantly though, your gist does not address pitfall 4; you do not check the status after registering for the event. Also, you're checking whether window.applicationCache is undefined inside of an event listener attached to that very object.