How to create offline webapps on the iPhone
Recently Google launched their latest mobile version of Gmail optimised for iPhone and Android based browsers. One of the features that stood out was the offline access thanks to the browsers support of html5 application cache.
Documentation on the application cache feature supported in safari iPhone 2.1+ is scarce and of that documentation it doesn’t go into great detail. The best place to learn about this is on the Safari DevCenter under the mobile section, there it has 2 documents introducing the user to offline webapps; the first is a quick rundown on just the manifest file and the second article touches on a few more features available to offline webapps such as the javascript events for updating the cache when the user is online. We’ll delve into these later in the article but first let’s take a look at a working example.
To see the offline webapp in action, load the demo on your iPhone, then turn Airplane Mode on, re-open Safari and reload the demo. This time it will fetch the files from the cache that was created on the initial load.
How does it all work?
To get a basic offline webapp up and running is incredibly simple but it does have one caveat which tends to get a lot of people frustrated quickly when for some reason the offline feature isn’t working. We’ll explain that issue shortly.
The first thing we need to do is create what is called a cache manifest file which has references to all the resources we want to save. Whether it be JavaScript, CSS, images or html, below we can view the manifest file used in the demo.
Offline cache v1
html files
article.html
css files
assets/_styles.css
js files
assets/_javascript.js
images
assets/ico_ninja-star.gif
The manifest file is a simple plain text file that holds the file names you wish to cache, the paths are relevant to where you saved the manifest file which is recommended to be in the root of the site. This file will be saved as filename.appcache, where filename is anything you wish. Lines that start with a # are comments and are ignored by the cache.
Referencing the cache file
We now have our cache manifest file setup and named, the next thing we need to do is reference that file in the html so this offline access will function for the specified files in the manifest.
The manifest file is referenced in the html tag through the manifest attribute and just points to the filename.
Now the one issue that catches a lot of people when trying this out is not setting up the mime type correctly in their chosen web server. Whether it be IIS, Apache or something else, you’ll need to make sure that the .appcache file is fed through as text/cache-manifest.
For Apache you can add the text/cache-manifest to the mime.types file found in the conf folder in apache and add the following to the bottom of the file.
For IIS open up the IIS manager found in Control Panel > Administrative Tools > Internet Information Services double click the Mime Types icon in the manager and scroll down the list until you find the .appcache file and edit the mime type to have text/cache-manifest.
That’s it, to get a basic offline webapp to work is incredibly simple all it takes is the manifest file to specify which files to cache, declare it in on the tag using the manifest attribute and you have yourself a site still accessible even if the user has no network connection.
UPDATE: GeoNomad in the comments suggested a great tool to make sure your manifest file is being fed through with the correct mime type, Web Sniffer. I put my manifest file through the Web Sniffer tool, you can see that the Content-Type is set as text/cache-manifest.
Updating the cache
If you want to update the cache you either need to change/add file references to the manifest and then tell the cache to update. Updating a file that the manifest calls will not make the cache reflect the changes. The mozilla developer article on applicationCache makes a good suggestion to version your manifest file as the JavaScript events described below will only fire if there is a change to the manifest file. To update the cache we have:
This object has various stages depending on what the cache is doing which can be checked by using the .status method. There are 6 different stages:
- Status 0 (UNCACHED) is returned which means that there is no cache available
- Status 1 (IDLE) is returned means the cache you have is currently the most up-to-date
- Status 2 (CHECKING) is returned means there is a change in your manifest file and it is checking it for changes
- Status 3 (DOWNLOADING) is retuned means changes have been found and they are being added to your cache
- Status 4 (UPDATEREADY) is retuned means your new cache is ready to be updated and override your current cache
- Status 5 (OBSOLETE) is returned means your cache is no longer valid meaning it has been removed
These available statuses can be attached to the applicationCache object with event listeners so we can tell the web app what to do on what status. In our case we want to update the cache to reflect changes we have done.
function updateCache() { webappCache.swapCache(); }
webappCache.addEventListener("updateready", updateCache, false);
In the above example we attach an event listener to the applicationCache looking for the updateready status which means there is a change to the manifest file and our cache is ready to be updated we do so by using the swapCache() method.
There is 2 more event handlers which do not appear in the window.applicationCache.status these are part of the event summary. Those are error & progress. We’ll take a look at how to use the error event, the progress event is basically the same as the downloading event handler.
function errorCache() { alert("Cache failed to update"); }
webappCache.addEventListener("error", errorCache, false);
All the error event handler does is return the specified function we attached in the event listener when the cache is not available or does not exist.
Event summary
In the applicationCache we have various events available so when the cache is being updated, or as mentioned above when something goes wrong. We can attach to those events and keep the user informed on what is happening. These events will execute in specific order based on what is happening with the cache and therefore give us useful hooks to represent that back to the user.
Pete who commented below and needed a way of indicating to the user that the cache was downloading prompted me to look further into the events that get fired and which ones would be useful in this scenario. I created a demo which will indicate to the user that the cache is downloading by showing a loading icon and once the cache had finished downloading, the loader would be hidden.
The event order for this example was as follows:
- checking – The page loads and the cache is checked for any changes.
- progress – The download event actually gets fired before this but we don’t need to hook into that. The progress event will fire for each file that is referenced in your manifest until the cache has finished downloading.
- cached – The cache has successfully downloaded and the cached event is fired so we utilise that to hide the loader.
- updateready – This event is fired so we can then swap the old cache with the newly downloaded one.
In the above mentioned demo we also attached event listeners to the error and noupdate events so we can handle errors and if the cache is already up-to-date.
Is the user online
There is also a new method on the navigator object called onLine which returns a boolean value if there is a network connection or not.
In the article example the onLine method is used to update the title if the user is on/offline.
Only the beginning
The applicationCache is not yet finalised and is of course subject to change with newer and refined features. I’m sure there will be additions in the upcoming iPhone OS 3.0 release. If there are updates unfortunately these cannot be discussed until the NDA is lifted. But of course this won’t stop us speculating.
Some of the features available in applicationCache on firefox 3.1 and safari 4 will inevitably be added in future releases of the iPhone OS. Such as the NETWORK: and FALLBACK: sections in the manifest file, these allow for the developer to specify a file they wish to never cache or to have a file load if something fails with the first option.
Short sharp facts about the appcache: https://appcachefacts.info/