Taking a web app offline (including iPhone support)

As part of having Oplop take advantage of as much cool HTML5 features as possible and being available as widely as possible, I decided to add offline support and to add iPhone OS support as a home screen app.

To take a web application offline requires you provide a cache manifest file. The file must be linked to in the html tag through the manifest attribute:

The cache manifest file allows you to specify what files to cache locally, what URLs to allow through, and a URL namespace where you can have cached fallback files (e.g. always show a "disconnect" icon for large files you don't cache). You can read the HTML5 spec on the format of the file, but the key thing I picked up on is the NETWORK section of a cache manifest file.

Ignoring the fact that you cannot use a CDN for JavaScript libraries anymore, the trickiest thing I found when writing the cache manifest file was making sure to whitelist all dynamic URL requests in the NETWORK section that a web app might possibly use. In Oplop's case I needed to whitelist the URL that Google Analytics loads from and the URL they send data back to the server with. The way I figured out what URLs I was missing was to load Oplop in Safari and to bring up the Activity window. That will show all URLs that failed loading because they were not whitelisted in the cache manifest.

After that the biggest annoyance with a cache manifest is it must be served as text/cache-manifest. This is a pain as the file is essentially static, but since it doesn't have a standard file extension it means web servers that set the content type for static file loading cannot do so. That means having to write a bit of code to explicitly serve the static file with the proper MIME type.

Another tip is to add an MD5 hash to the file in a comment based on what is being cached. Browsers detect when to download new files by checking if the cache manifest file has changed. By having a comment with the hash of what the cache should contain you can make sure that browsers update properly. I wrote a Mercurial hook that automatically regenerates the static file when a commit changes a cached file so I don't have to think about updating it.

A perk of having an offline web app is you can then make it work under the iPhone OS as an app without having an Internet connection.  By creating a web clip icon (the name of the icon a web app can have on your home screen) you can make it easy to launch a web app. And by hiding the address bar in the web app you can really obscure that the app is from the web and not native.

What this all means is that Oplop now can be run as an iPhone app right off your home screen. You can also run it while offline under Firefox. I do want to hide external links like "help" when offline so users don't accidentally expect them to work (requires doing the proper thing upon load and then registering a callback to change the state when going offline or online), but that is just a nicety thing and not required for basic use.