Development and UX from Michael Mahemoff. Maker of Player FM. Previously: Google, BT, O'Reilly author.

Menu

Offline Apps with Application Cache: Quickstart, Tips, and Deep Dive

I’ve been mucking with AppCache, aka ApplicationCache. It’s the secret sauce that lets you build offline apps, which is great for performance and fabulous for pretending to be an iPhone app when you’re not.

Quickstart an Offline App

As with most HTML5 technologies, the basic usage is quite simple:

Create a trivial trio of HTML, CSS, and JS files, maybe with an image or two. (Or reuse an existing toy app.)

Make the manifest file. It’s just the literal phrase “CACHE MANIFEST” on the top line (I know, bit random), followed by the list of files in your app:

CACHE MANIFEST
clock.html
clock.css
clock.js
clock.png

Mark it as MIME type text/cache-manifest. If you’re SSHing to a virtual host (like I did with Dreamhost), all you need here is a one-line .htaccess file:

AddType text/cache-manifest manifest

That’s it. Now those files above will be cached locally and won’t need to be downloaded again next time. To test it, load the site, shut down your net connection, and reload. It’s still there innit.

It works the same on your app-phone – on iPhone or Android (or others maybe), load the site, switch to airport mode, go back to the browser and reload … the site’s still there. Where it gets really fun is making it into a regular app:

On iPhone, bookmark the site with the Safari “+” button, choose “Add to Home Screen”, and the app will be sitting alongside all your other apps on the home screen.

On Android, bookmark the app in the browser. Hold your finger down somewhere empty on the home screen, choose add “Shortcuts” | “Bookmark”, and choose the bookmark you’ve made.

You could do the same with any website of course, but it’s particularly cool when the website is an offline one…it works just like a regular app!

Practical Tips and Troubleshooting

Purging the Cache

In practical terms, you are quickly going to come up the no. 1 problem of developing against a cache. How do you purge it whenever you update your code?

Simple. You only need to make an arbitrary change to the cache manifest file, so just include a version number in the comment:

CACHE MANIFEST

v1

clock.html
clock.css
clock.js
clock.png

Keep revving it up every time you change any file, because changing those files alone won’t have any effect. And if it’s getting serious, you’ll want to practice the principles of DRY and continuous integration by automating this process.

Ridding yourself of Errors

An unfortunate thing happens when there’s a downloading error: The whole thing silently fails. Until debugging tools get better, if you find your app’s not updating, you ought to check each of the resources specified in the manifest really, really, carefully.

Cached Resources

Cache manifests are not exempt from standard caching rules, so the manifest itself, as well as other files might be cached. Pain. A good solution is to use the following Apache directives to .htaccess:

ExpiresActive On
ExpiresDefault

That MIME Type

Things might fail if the Cache Manifest MIME type is wrong, so use your browser’s debugging tools or a service like Web Sniffer to check it (thanks CSS Ninja).

The Wording

The wording can also trip you up. Be really sure it’s exactly “CACHE MANIFEST” at the start of the file. Again, better debugging tools in the future will help here.

The Fancy Stuff

All of the above is enough to get up and running with an offline app, and maintaining it too. But wait, there’s complexity too!

Fine Control Over What Gets Cached

The cache manifest file actually allows three kinds of resources:

Online resources that can be cached (CACHED). As shown in the example above, it’s the stuff that’s available for caching.

“Fallback” resources in case a file isn’t cached (FALLBACK). These are like catch-all email addresses; any resource that’s not cached, is a candidate for a fallback. There’s a pattern-matching algorithm to map resources (which mightn’t have been cached) to other resources (which hopefully have been cached). For example, you probably wouldn’t cache every wikipedia article, so the fallback section would ensure a special “offline.html” page is shown for those missing articles.

Online-only resources (NETWORK) Resources which should never be cached, e.g. it would be a good idea not to cache stock prices if people were making million-dollar decisions on the back of what they see on your possibly-cached web application.

The syntax (adapted from the WHATWG example):

CACHE MANIFEST

a comment

v37

(version number unnecessary

but recommended for cache purposes)

the following "CACHE:" is optional;

a special case because top of file

is cache by default

CACHE:

images/sound-icon.png
images/background.png

note that each file has to be put on its own line

FALLBACK:
/ /sorry-i-am-offline.html

NETWORK:
/stocks/*

Checking if We’re Online

window.navigator.onLine … try it now in your console (and try it again with your connection off).

Monitoring State and State Changes

What do you notice about all the app cache stuff above? It’s all declarative, no Javascript. But you can go on to do extra stuff with Javascript too, via the Application Cache API. (I am somewhat skeptical about the applications for this API, since simple and declarative is the way to go for something as potentially hairy as offline…but that’s for another rant.)

The window is effective tied one-to-one to the cache object, so you can just access the cache with window.applicationCache. For example, you can get cache state with window.applicationCache.status, try it now in your console. It will probably be zero…the possible values range from 0 to 5, representing:
UNCACHED, IDLE CHECKING, DOWNLOADING, UPDATEREADY, OBSOLETE. (These constants are also available on the cache object, i.e. window.applicationCache.UNCACHED is 0.)

Forcing Updates

applicationCache.update() will force an update, i.e. start downloading the application again if the manifest has changed. However, it won’t suddenly switch over after downloading. To do so would be to pull the rug from under your live, running, application. It will be there next time. But if you do want it right away, use applicationCache.swapCache().

Offline Mobile Apps: It’s More than Application Cache

Application cache certainly is a critical feature of mobile apps; it’s a basic user expectation that a mobile app starts right away, unlike a complex web app which must be downloaded. However, there are other things too. For example, offline storage. That’s another feature of HTML5, comes in many incaranations, and something for another day. The other really big deal is the UI. Of course, you need to simulate certain UI features of the native apps if you want your web app to go native. Hence, libraries like IUI, jQTouch, and Apple’s secret PastryKit. On the input side, your UI must also handle touch events, something some of the aformentioned brand of libraries will support. Finally, there will be specific features of the platform that can help; for example, setting the home screen icon and going full-screen so the browser chrome doesn’t show up.

it’s worth noting that window.navigator.onLine under FF doesn’t currently return the state of the computer connection but if the “offline mode” of Firefox has been checked …
in this state it’s pretty useless but I heard that they will fix that very soon

G’Day

Welcome to Michael Mahemoff's blog, soapboxing on software and the web since 2004. I'm presently using HTML5 and the web to make podcasts easier to share, play, and discover at Player FM. I've previously worked at Google and Osmosoft, and built the Ajax Patterns wiki and corresponding book, "Ajax Design Patterns" (O'Reilly 2006).
For avoidance of doubt, I'm not a female, nor ever have been to my knowledge. The title of this blog alludes to English As She Is Spoke, a book so profoundly flawed it reminded me of the maturity of the software industry when this blog began in 2004. I believe the industry has become more sophisticated since then, particularly the importance of UX.
Follow @mahemoff