As you probably know we’re at liip are doing the HTML5 based Webapps. Now our customer wishes to have his iPhone / iPad HTML5 to be accessible offline.
As I found this topic super interesting I started writing a proposal how in my opinion this goal could be achieved best. As our content is highly dynamic (news articles) the normal html5 cache manifest just won’t cut it.
Off course we will store css, js and static images in the cache manifest. But from what I read it is just not the right place for highly dynamic content.
Now let’s cut to the cheese and come to my plan:
Caching the Page
Responses from the backend will be stored by their url in the local storage. Example:
key => /2011/09/05/schweiz value => <html><body>foo bar page content</body></html>
We wrap the YUI IO Object and upon each request we check if the requested URL is not already stored in our local storage. If so we will call the callback function directly and feed it the response from the local storage. That way the user will have a super fast experience if the response is already in the cache.
Caching the Images
Caching the images is another problem as the browser gets their content on his own. They also get stored by their path in the local storage. Example:
key => /image/large_cropped/article/2011/09/05/N_IVTZU/image/IWIGD-ein-davoser-als-ehrenbuerger-des-suedsudans
The value in this case needs to be base64 encoded value of the binary stream. The backend image API will offer the option to request the image base64 encoded rather than just returning the normal image.
The pictures nodes that are not fed by the cache manifest won’t have a src tag anymore on the iPad / iPhone view. Their url is stored in some other attribute. In this example I’ll use base64Src=”" as the attribute. This will prevent the browser from loading the pictures whenever it’s actually online. That way we prevent the browser from fetching content we already have.
Once a request is loaded / delivered from the cache, we will loop through all the tags and populate the images. To do that we can simple read the base64Src attribute. If they’re not yet cached we will request them through XHR. Afterwards we can fill the images with a little javascript trick:
document.getElementById("hero-graphic").src='data:image/jpeg;base64,' +
localStorage.getItem('/image/large_cropped/article/2011/09/05/N_IVTZU/image/IWIGD-ein-davoser-als-ehrenbuerger-des-suedsudans')
Prefetching
All this fancy caching won’t give much benefit if we don’t build a prefetching mechanism. Once the page is fully loaded and in a somewhat “idle” state we will start prefetching content.
To know what should be prefetched, we will request a list from the backend which contains the urls of all pages that need to be prefetched as well as all the necessary images. Doing it like that gives a few very nice possibilities:
- The backend is in control which pages are stored localy, so it can easily change every day.
- We fetch content sequentially, this means in case of interruption the user doesn’t lose all of his download, just the data from the currently active request.
- As we already know how many articles and images we have to fetch we can calculate a quite accurate progress bar
- We have the possibility to pause the downloads at any time. The user would only lose the current request.
This whole approach would give us a solid prefetching without having to touch much of existing code. We can also implement nice UI features like the ability to see the download status as well as making it configurable how much content should be stored.
The only problem I see currently is that we don’t have a way to tell if the iPhone / iPad is online or offline. We could try to detect that based on the IP the request has in the backend or in the time it takes to prefetch the first article. Sadly there’s no isWifi boolean exposed to javascript as far as i know.
What do you think about it? Any comments, corrections or better approaches are appreciated.
It‘s quite in here! Why not leave a response?