How to create offline webapps on the iPhone

Apr 28
 
 
 
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.

Update: Added additional information about the event summary and the order in which events get executed. Added link to useful tool for sniffing file Content-Types.

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.

CACHE MANIFEST
 
# 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.manifest, 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.

<html manifest="thecssninja.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 .manifest file is fed through as text/cache-manifest. For example in IIS the .manifest mime type is actually already declared to serve .manifest files as application/x-ms-manifest for offline webapps to work this will need to be changed to mime type 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.

# html 5 application cache - offline access
text/cache-manifest			manifest

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 .manifest file and edit the mime type to have text/cache-manifest.

IIS 7.0 Manager Mime Types

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 <html> 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:

window.applicationCache;

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.

var webappCache = window.applicationCache;
 
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.

var webappCache = window.applicationCache;
 
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.

navigator.onLine

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.

 
 

Post filed under: css, javascript, xhtml.

Skip to comment form.

Comments

  1. Very interesting … but is there a limit as to what can be cached? Just tried based on your model, total site is 5 mb size with hundreds of small files (4 – 15 kb). It just will not cache, while your sample on the same server performs well.

    cheers

    pete, June 17th, 2009
  2. No I haven’t come across a size limit nor have I found any references anywhere to indicate that there would be a size limit. One thing that did stop my web app from caching was misspelling file name in the manifest, have you made sure your manifest hasn’t got any spelling mistakes? A good way to debug any issues is to attach an event listener to the error event and see if it’s failing the cache, you can also check the status of the applicationCache and see what it’s returning. Take a look at the JavaScript file I’m using for the example it has a switch statement in their and logs useful messages to the browser console to see what the status is.

    The Css Ninja, June 18th, 2009
  3. Hi, I learned last night that the current cache limit for webapps is supposed to be 5mb. I reduced some stuff, so that took care of one thing.

    My true problem seems to be, when I go into a hierarchy. All links straight off the index.html page work perfectly well, but when I do the following, safari on the iPhone (not on the mac) crashes gloriously:

    FILE: my.manifest
    Content:
    CASH MANIFEST
    this.css
    that.js
    Vegetables/AtoZ.html
    Vegetables/Spinach.html
    Vegetables/Chickpeas.html

    FILE: index.html
    Links to:
    Vegetables/AtoZ.html
    Vegetables/Spinach.html
    Vegetables/Chickpeas.html

    Offline all three links work fine, however:

    When I tap on Vegetables/AtoZ.html and from within there want to get to the Spinach or Chickpea file Safari iPhone crashes.

    It is as if there was an undocumented “it only works on the the first hierarchy level”.

    Have you tried a second or third level hierarchy with manifest yet? Be interested to hear from down under.

    Cheers

    pete, June 19th, 2009
  4. Just quoting your email so the comments make sense.

    Hi, just put 300 files flat next to index.html and voilà …. all works fine. Looks like it currently only likes it “flat”…

    So your saying you are just referencing all the files directly and not listing their folders names before and that caches all of them? Can you provide a link to a test version that crashes the browser and one that works?

    …Tell me: When the phone or touch is online and checks for an update manifest, etc. can one let the user know that that is what it is doing and allow him to stop it or not? Or just advise?

    You could in theory attach to the downloading event handler for the cache webappCache.addEventListener(“downloading”, promptUser, false); which would call a prompt to ask a user if they would like to cache the site. Then depending on their answer either continue or throw an exception.

    The Css Ninja, June 19th, 2009
  5. I have a question on the applicationCache status, having copied your js sample:

    Changing a file and changing the manifest I get Status 2 (checking) in an alert box which I place at the top:

    (function(){
        var webappCache = window.applicationCache;
     
        alert(webappCache.status);
        ...
    })();

    Afterwards the newly modified file is being displayed.

    At which point can I get in with an alert box to actually confirm that the cache has indeed been updated?

    I tried the following, but none of the desired alert boxes trigger:

    function updateCache() {
        if(webappCache.status == 4)
        {
            alert ("We wil update. Pre");
        }
        webappCache.swapCache();
        /* ahora = new Date() */
     
        if(webappCache.status == 4)
        {
            alert ("We have updated. Post");
        }
    }

    BTW: The hierarchical problem seems to be solved. Somebody else looked into it and it might have been, because the manifest contained files which did not exist anymore. Preciser analysis in a few days.

    pete, June 23rd, 2009
  6.  if(webappCache.status == 4)
    {
    	alert (”We wil update. Pre);
    }

    That will always return false, what you need to do is attach an event listener to the status itself. Status code 2 will always return first if there is a change to the manifest, followed by downloading and updateready. Just do the following:

    webappCache.addEventListener("updateready", updateCache, false);

    This will fire the updateCache() function and alert you that the cache has been updated.

    BTW: The hierarchical problem seems to be solved. Somebody else looked into it and it might have been, because the manifest contained files which did not exist anymore. Preciser analysis in a few days.

    Yep same issue I had when doing the demo, although I misspelt my file names therefore it was looking for files that didn’t exist.

    The Css Ninja, June 24th, 2009
  7. Okay.

    You Ninja. Me not even a half boiled rice corn.

    Will this give me the right message upon update?

    (function(){
        var webappCache = window.applicationCache;
        function updateCache()
        {
            if(webappCache.status == 4)
            {
                alert ("We just updated.");	
            }
            webappCache.swapCache();
        }
        function errorCache()
        {
            console.log("We are viewing it offline.");
        }
        window.addEventListener("load", loaded, false);
        webappCache.addEventListener("updateready",updateCache,
        false);
        webappCache.addEventListener("error", errorCache, false);
    })();
    pete, June 24th, 2009
  8. Lol, we’ve all got to start somewhere.

    Your code looks fine only change I would make is removing the if statement in your updateCache method as that only gets fired when the updateready status is passed to listener which is already status 4 so no need to check for it again. Your onload event listener doesn’t need to be there if your not calling anything after load.

    The Css Ninja, June 25th, 2009
  9. tks a lot. and now an off-topic one: the app works fine when i run it on my local server and pick it up with the iphone as part of the local network. the moment i upload the whole thing to the external providers place no caching happens. i am told it was “bad formatting” of the manifest file and that the provider reformatted it, but nothing changes the non-workability of caching on the distant server. what could be the right debug procedure? .. email me privately, if you wish.

    pete, June 27th, 2009
  10. Have you set up the .manifest extension to be fed through as text/cache-manifest in your mime types on the server your uploading to? If it’s working on your local there should be no problem with your manifest file.

    The Css Ninja, June 27th, 2009
  11. on the external one the declaration is in an htaccess file.

    pete, June 27th, 2009
  12. I did have trouble setting the mime type through my htaccess file on my hosting, I ended up setting the mime type through my admin control panel. Just do a check for the applicationCache status to see what it’s returning on your server, if you get 0 it would most likely mean the mime type isn’t setup correctly on the server.

    The Css Ninja, June 27th, 2009
  13. problem sorted. cause: reference in an html file to a css class which was no longer defined …. would you like a little js-contract? need a progress indicator during download so people know it is happening and do not interrupt too early (as downloads are approx. 4mb).

    pete, July 5th, 2009
  14. If you need to indicate that something is happening I would show an animated gif icon and display it when the cache is downloading, then use the updateready event to hide once the download is done e.g.

    var webappCache = window.applicationCache;
     
    function downloadingCache()
    {
    	// Show animated gif
    }
    function updateCache()
    {
    	webappCache.swapCache();
    	// Hide animated gif
    }
     
    webappCache.addEventListener("downloading", downloadingCache, 
    false);
    webappCache.addEventListener("updateready", updateCache, 
    false);

    This won’t give you exact progress, but it will indicate to the user that something is happening and if you position a container over the top of the site with the animated gif this will stop the user from doing anything until it’s loaded.

    The Css Ninja, July 5th, 2009
  15. I implemented this and it works amazingly well, in Safari. But what I really want is for it to work with UIWebView, and it doesn’t seem to be doing that – does anyone have any idea on putting the two together? Any success stories?

    Gavril, July 10th, 2009
  16. Just got a brand new 3GS and trying to get some off-line webapps going. I’ve been having some trouble with mine not working offline and just tried yours with the same results.
    I notice through using a proxy that the phone is never requesting the “manifest” file. Seems like a bug in the phone?
    What versions have you had this working on?

    Mark, July 12th, 2009
  17. @Gavril – I dont see why it wouldn’t work in a native app as it would essentially be calling an instance if safari in your app. I’d be interested to know if you find the solution.

    @Mark – That’s strange I have the 3GS also and it’s working fine. Try enabling the debug bar in safari, the demo logs messages to the console it should let you know if something has gone wrong.

    The Css Ninja, July 12th, 2009
  18. So I try to get the anim.gif to run by doing this:

    function downloadingCache()
    {
    	// Show animated gif
     
    	function makewindow() {
    		newWin = open("", "displayWindow", "height=400,width=600");
    		newWin.document.open();
    		with (newWin.document) {
    			write("On the fly");
    			write("This window made in js ...");
    			write("&lt;img src='imgs/anim.gif'");
    			write("");	
    		}
    	}
    }

    After that one I have this:

    function updatedCache()
    {
    	// Hide animated gif
    	webappCache.swapCache();
     
    	alert ("The app has been updated. Please refresh to use the 
    		latest version");
    }

    The log file gives me the protocol “Downloading” but does not show the page i create to show the anim.gif

    and also I get this javascript error message:

    an error for line “webappcache.swapCache()” -> INVALID_STATE_ERR: DOM Exception 11

    Bit stuck now. Still half-boiled rice corn. ;-)

    pete, July 14th, 2009
  19. Why are you trying to open a new window? I put together a quick demo that simulates lots of content being cached. It will show a loading icon when the downloading status is passed to the applicationCache when the cache has finished downloading and the updateready status is returned the loading icon will be hidden. I delayed the hiding of the loading icon for 2 seconds using a setTimeout, for your implementation I would just do the hiding in the updateCache function.

    The Css Ninja, July 15th, 2009
  20. Learning, master, learning … I thought I need to open a window so I can close the thing again afterwards. Obviously that is not the case, so I’ll have to learn how to do that. — In the demo, thank you very much indeed for your time, the icon does not disappear however. It runs and runs and runs. ;-)

    pete, July 15th, 2009
  21. Ok so the demo should work as expected now, thanks to your scenario I have discovered quite a few more useful bits of information. I’m going to update the article in the coming days and shed some more light on those discoveries.

    The Css Ninja, July 16th, 2009
  22. Wonderful! The demo works in showing the gif.

    Now where would I go and look for a mistake I made, when in the next step in the line

    “webappCache.swapCache();”

    I get this error message: INVALID_STATE_ERR: DOM Exception 11

    pete, July 20th, 2009
  23. Can’t really say without actually looking at the site. Based on the W3C explanation of the exception.

    If an attempt is made to use an object that is not, or is no longer, usable.

    Looks like you’re potentially trying to access webappCache object outside of it’s current scope.

    The Css Ninja, July 20th, 2009
  24. Seem to have solved the EXCEPTION 11 error:

    I deleted under

    function updateCache() {

    this:

    webappCache.swapCache();

    … and inserted

    loader.display = “none”;

    … plus an alert box to advise the user that the update has been completed.

    The modified files have obviously been downloaded to the iPhone cache and the app works. No “swapCache” required. — This is good, but what exactly happened?

    PS: Is the gif your copyright or public domain?

    pete, July 28th, 2009
  25. Hmm the swapCache() method is what changes over the current cache with the updated cache. Removing that will never update the cache, even though it has detected a changed, downloaded it and fired the alert to inform the user that this has happened without swapCache all of that work will be for nothing. If you reload it will load the old cache as the new cache was never swapped.

    The gif was generated from ajaxload.info just generate your own one there to better suit your web app design.

    The Css Ninja, July 28th, 2009
  26. Well that IS the strange thing: SwapCache() is off, after the update and subsequent turning on of flight mode on the iPhone all changed files appear as changed, all other files can be called without problems. — Really strange. As if progresscache was performing an immediate caching.

    pete, July 28th, 2009
  27. Ah but that’s the issue, SwapCache is only called if the user already has a cache. If there is an update SwapCache will swap the old cache with the newly downloaded one. If you try to push out an update to one of your cache files all the events will fire and it will act like it’s downloading a new cache but without the final SwapCache method in the onupdateready event it will never change over and the user will be stuck with the original.

    The Css Ninja, July 29th, 2009
  28. UPDATED: Updated article to reflect some of the ideas expressed in the comments.

    The Css Ninja, August 1st, 2009
  29. There still seems to be one problem somewhere along the line with “some” users.

    While 4 testers have no problems updating, one tester with an up to date (3.0) iphone 3G reports that the update messages goes on and on and on. He finally escapes the scene, restarts the iPhone and everything appears to have been updated. — This is something we talked about before. It is a hangup at the swapcache point.

    The javascript call now comes at the end of the html file (just before ) so the loader div is before it.

    So it works almost as it should, but this one tester gives me a mystery, and as I had the same phenomenon before I just wonder what is not known about updatecache which could cause this.

    As a separate note: Your in-depth insight into these new features and your helpfulness is very much appreciated on this side of the globe. Thanks.

    pete, August 1st, 2009
  30. Hi, I’m probably the noob-iest one here, but I’m trying to make my soundboard web app work while offline. I haven’t gotten it to work with my 50+ images, but I’m willing to pursue it once I’m sure that it can cache mp3 files. Is MobileSafari capable of caching mp3s specified in the manifest?

    Thanks for all your help, your site has been a great resource!

    Harry Shamansky, August 19th, 2009
  31. I believe based on previous comments there is a size limit of 5mb, though this is unconfirmed. If your total assets are higher than that it may not cache or will return an error. You should be able to cache any file regardless of format, it will depend more on the file size than type.

    You’re very welcome, thanks for reading.

    The Css Ninja, August 19th, 2009
  32. That makes sense, though I’ve also heard that the user is prompted if the cache is over 5mb.

    On an unrelated note, there isn’t a way to play a short sound without the full screen player opening in a web app, right? It’s not a big deal, as it opens and closes pretty quickly, but it still would be great if there were an alternative.

    Harry Shamansky, August 19th, 2009
  33. I’m not sure if the iPhone prompts the user for more space, I know the desktop does.

    Yeah audio and video on the iPhone will only play through the fullscreen player no way around it.

    The Css Ninja, August 19th, 2009
  34. This is great stuff!
    I got this working perfectly in the regular browser but have had no success getting it to work within a UIWebView. It returns Status 0 all the time within the view.
    If you or anyone has had any luck please let me know! Thanks for the great write up.

    Josh, August 20th, 2009
  35. Yeah a few people have mention not being able to get it working in UIWebView which is strange. Will keep you posted if I find out anything.

    The Css Ninja, August 21st, 2009
  36. Hi there,
    I’m trying to set this Cache thing up and I can’t seem to make it work.

    I made the cache.manifest file and it’s correct.
    I added the MIME type in my mime.conf file (Apache locally installed on my iMac).

    I load the app from Safari on the iPhone, save it on my homescreen. But everytime I load it up, i get the spinner in the status bar RELOADING everything.

    It’s not a matter of life and death right now, as I can run the “App” locally instaled on the iPhone through SSH, but it would be cool to cache everything on the phone.

    Alex Buga, September 2nd, 2009
  37. Hi Alex,

    Have you attached the various events to your applicationCache object to see if you are getting an error in the caching. Make sure in your cache file that there are no misspelt assets or non existent references in there otherwise it won’t cache.

    The Css Ninja, September 2nd, 2009
  38. Hi Ninja,
    do you have any news about the UIWebView not working with this method?

    Robert Selldorf, September 2nd, 2009
  39. Hi Robert,

    I did post on a thread in the Apple developer forums asking why the appCache wouldn’t work in UIWebView and it doesn’t look good. I haven’t got official word from an Apple employee, but from what people are saying it’s not supported.

    The Css Ninja, September 2nd, 2009
  40. hi!

    1- I have a problem with your demo, with firefox 3.5.. when I reload the page, i get ”

    Content Encoding Error
    The page you are trying to view cannot be shown because it uses an invalid or unsupported form of compression.

    2- I just created a manifest file on my website. The firefox preference menu show “mysite” in the list of cached sites… Does it mean the mime type is set?
    it’s 0bytes, though

    thanks cssninja!

    yvan, September 6th, 2009
  41. edit : answer for #2 – the mimetype wasnt set but I could edit .htaccess

    yvan, September 7th, 2009
  42. Is that an issue with the demo I’m hosting on my site or is it the demo files you have downloaded? I’m using FF 3.5.2 and I don’t get that error, I do however get a JavaScript error because of the google analytics in the page but that doesn’t stop the page from caching.

    The Css Ninja, September 7th, 2009
  43. I have had no problem creating offline apps this way, but I have a problem getting rid of them.

    If I delete the icon from the screen and then access the same URL in safari, it starts to load and then crashes. (OS 2.2).

    I have tried resetting the iPod but the crash persists. Clearly something remains in safari that tells it to look in the now non-existent cache.

    I have tried clearing the History, but no joy.

    Is there any advice on how to remove offline apps properly once you have created them?

    TIA

    GeoNomad, September 8th, 2009
  44. Since this thread is still current, I think it’s worth mentioning that Dashcode *3.0* (it comes with Snow Leopard when you install XCode) has an option to “make your webapp available offline”

    It basically does all the caching work for you. I have yet to see whether it works for me, but I’ll find out soon.

    Harry Shamansky, September 8th, 2009
  45. Just so you’re aware you don’t need to add the website to your home screen for the cache to work.

    To clear your cache close the window in safari then quit the app go to Settings > Safari > Clear Cache, that should kill off the cache. Crashing seems a bit extreme it should throw an exception if the cache is invalid or non-existent. Perhaps that is a bug in 2.2?

    The Css Ninja, September 8th, 2009
  46. That’s great news, I imagine it would purely create a manifest file which references all your assets.

    Would be interesting to know if it attaches any listeners for you to do things like progress etc.

    The Css Ninja, September 8th, 2009
  47. Well, it’s still very basic. Dashcode won’t even show you or let you edit the manifest file until after you’ve saved to disk or exported. Less room for error this way, I suppose.

    Harry Shamansky, September 8th, 2009
  48. Clearing the cache did the trick. In fact I thought of it during the night and did it this morning before I came back here. Don’t know why I didn’t try that before.

    I think it did throw an exception. For a brief second there was an Error Message bar, but Safari crashed before I could read it. Been too lazy to look in the crash log to see if there was anything relevant. Maybe I will later.

    Yes, I had noticed that the cached version is used even if you type the URL offline, so there is benefit and danger to using the manifest without paying attention.

    Thanks.

    GeoNomad, September 8th, 2009
  49. If the only way to really remove an offline web application is to clear the cache, that means that all offline web apps will also be removed.

    Is there really no way to remove a single app?

    This seems like a serious problem. I already have several offline webapps on my iPod and expect many more.

    GeoNomad, September 9th, 2009
  50. It’s still not right that the browser would crash just because there is an error.

    Unfortunately clearing the cache is an all or nothing process, much like resetting the location services does it for every single app rather than individually. Seems they should really make it work like the notifications does.
    Even with finer control over cache clearing it would still be at the app level and not the individual cached site level.

    If the cache returns a 404 or 410 the obsolete event is fired and the browser will naturally delete the cache, that could be an alternative to going through the menu?

    The Css Ninja, September 9th, 2009
  51. After a few hours of bug fixing I got mine running except for one thing. I like to cache some .mov files. I kept it all under 5 MB, but in Mobile Safari it gives me a “broken” play button. It works fine in firefox for example. I can’t find it anywhere on the web, but I think the native iPhone players do not use the files stored by Safari and look for the online files, right?

    Matthijs, September 9th, 2009
  52. I’m not entirely sure what you mean? Is the cache not working on .mov files?

    The Css Ninja, September 10th, 2009
  53. Just an update to everyone, I got official word from an Apple employee that the applicationCache is not supported in the UIWebView. But they did recommend another forum post that shows you how to have a website run locally using a different technique, in short put your files in the Resources folder http://retromaccast.ning.com/profiles/blogs/iphone-programming-for-apple

    The Css Ninja, September 10th, 2009
  54. What I mean is the .mov file is cached by the browser (at least that is the case on a regular browser as I can play the movie while offline) but when I want to play the movie by clicking a link in the cached html files on the iPhone, the native application for video playback will open and I think it will look for the file on the original URL and not the file Safari cached.

    About the “Resources” solution above. It is quite nice but the great thing about UIWebView is that you do not need to distribute an app.

    Matthijs, September 10th, 2009
  55. Have you tried using a smaller filesize video, or just the video by itself? Perhaps there is a size limit, this is only speculation.

    The Css Ninja, September 10th, 2009
  56. The video is just 1.4 MB, so it should not be a problem. In Firefox it is easy to see how much data is in the cache for each domain (firefox>preferences>advanced>network) and there the movie is included. I think only Safari can use the cache and not other programs (like the video player)

    Matthijs, September 10th, 2009
  57. I wrote last week that your demo, here, crashed my firefox 3.5.2.

    I upgraded to 3.5.3 today and it still does… Well.

    It might be a bug specific to the linux version.

    I’d didn’t dive into this technique yet, so before I do, I’d like to know if the caching speeds up an online web-application with the iphone (dynamic website with a lot of content fetched online)? What’s the best way to do? cache as many files as possible? the javascripts?

    yvan, September 11th, 2009
  58. That’s a very good point. I’m sure it could be due to the fact that video and audio tags in iPhone Safari don’t act the same way as a desktop browser and would probably bypass the cache. Will investigate further, but good find.

    The Css Ninja, September 11th, 2009
  59. Must be a bug with the linux version It seems fine on 3.5.3 in Vista & 7.

    The whole point of the applicationCache is purely for offline access rather than speeding up the web app. Though it would make the initial visit to the site much snappier as you can cache all the pages before the user will get there. Caching all the static content would definitely speed up the responsiveness, but if you have a lot of assets you wish to cache it can make the first time visit very slow.

    The Css Ninja, September 11th, 2009
  60. @Matthijs
    My recent experience also suggests that embedded video is not possible offline… and that was the whole point of trying to make this small offline app.

    I still hold a small flicker of hope… the iphone… no video… doesn’t sound right to me.

    I did get the rest of it working offline after some problems… being that the manifest is aggressively cached and after fixing a small problem in it I had to rename it to make offline work at all. That was my biggest caveat.

    Martin Westin, October 19th, 2009
  61. First thing I tried when I read this was downloading the demo.
    It didn’t work on the iPhone, but the original demo did!!!
    Is it because of my server?
    The demo is on: http://projecten.maxmakes.nl/cache11/index.html

    Max

    MaxMakes, October 26th, 2009
  62. Looking at the console.logs it’s returning a status of uncached, if it was working it would return status checking. It’s also firing the error method so you may want to print whats happening.

    function errorCache(error)
    {
    	console.log(error);
    	console.log("You're either offline or something has 
    		gone horribly wrong.");
    }

    Just add error to the error method and log it to the console, do this in Firefox with firebug so you can get more in-depth results.

    I would start stripping back items in the manifest and see if that works.

    The Css Ninja, October 26th, 2009
  63. I tried this but he now said it is an object event.
    How can I get the error.
    You can see the problem in the example of my previous message.
    Thanks for helping me

    Max

    MaxMakes, October 26th, 2009
  64. http://projecten.maxmakes.nl/cache11/thecssninja.manifest

    is returning content-type: text/plain so it won’t work.

    You can check the headers using this handy tool: http://web-sniffer.net/

    FYI, another working example is at http://benlo.com/jaipho – an offline slide show builder.

    GeoNomad, October 26th, 2009
  65. @GeoNomad – Thanks for the handy tool. Will have to do an update to the article as quite a few people get stuck on setting the right mime type.

    The Css Ninja, October 27th, 2009
  66. The mime type can be tricky, especially if you don’t have access to the server configuration. But a nice trick is to use a php script to generate the whole file, including the header. Safari is happy with a manifest file such as manifest.php and as the php can generate the content on the fly, life is easy.

    Also, the php script can generate a random version number while debugging, so you can just forget about the whole manifest thing. Just remember to load twice every time. If you want a copy of one of my scripts, just email me.

    For small webapps, this method means that content is always downloaded when online, and always works offline with the last load. No fuss, no muss.

    GeoNomad, October 27th, 2009
  67. My company has been working on an issue with UIWebViews and caching and the solution was to build our own caching that reads the manifest.

    On a separate issue – I am writing a web app and trying to cache an mp4 file and a pdf. The cache is under 5 megs. It seems the pdfs and the mp4s just will not cache. With pdf’s, it’ll try to go to the URL regardless of cache, and with the mp4, it tells me it can’t play the file. Anyone have a solution or see anything like this?

    Mike D, December 5th, 2009
  68. Yeah the official word from Apple is that you cannot use applicationCache in UIWebViews.

    The reason video/audio/pdf won’t cache is because they are loaded in external applications e.g. HTML5 videos get loaded in the media player and will always pull the source from the server. Same goes for audio and pdf.

    The Css Ninja, December 5th, 2009
  69. Actually i was able to get a pdf to cache by loading it in a base64 encoded string (data:), but the results were REALLY slow. still, for those who really need it, may be a worthwhile solution – didn’t work for video though.

    Mike D, December 5th, 2009
  70. You might try serializing with toDataURL and saving to LocalStorage.

    GeoNomad, December 5th, 2009
  71. Hey does anyone know if its necessary to create a manifest file if you are using Dashcode 3.0 to create a web app for the iPod????

    ryan, December 9th, 2009
  72. Depends if you are going to want it to work offline, without a manifest file offline will not work. Dashcode 3.0 allows you to tick a box to make a webapp function offline, all that does it creates a manifest file for you listing all your assets. I’m not sure how dashcode allows you to handle errors/events for the applicationCache if at all.

    The Css Ninja, December 9th, 2009
  73. I have downloaded the sample zip file and after I add an htaccess file it works fine.. However, I can not add images to the small website? I did have everything working for quite sometime and had many images working, yet i keep getting a safari error when i turn off wi-fi on my ipod touch. Please help!

    my .htaccess file just contains the text below:

    AddType text/cache-manifest .manifest

    I am super frustrated as I had everything working, yet now i can not get it to work anymore…..

    Stephen, December 14th, 2009
  74. Hard to tell without some sort of example or link?

    The Css Ninja, December 14th, 2009
  75. Here is the project I am working on right now.. But I have changed the manifest file, but even with the manifest file containing all the images it still breaks.. link is here.

    http://www.stephencarr.net/ipod/nursing/nursing.html

    Stephen, December 15th, 2009
  76. You’re image doesn’t exist, http://www.stephencarr.net/ipod/nursing/images/bg_full_screen.png, I get a 404. If you reference files that the cache can’t resolve the offline caching will fail.

    The Css Ninja, December 15th, 2009
  77. It works NOW!!!! Thank you! You were correct, I was referencing a wrong background image. I corrected that and it still didn’t work, so I simply erased everything off the server just to be sure there weren’t any bad files floating around, and re-uploaded and now we are good to go! That was a pain!! But totally worth it, thanks again man!

    Stephen, December 15th, 2009
  78. I’ve gotten the app manifest and offline caching working, many thanks for the article! I’m able to bookmark my site/app to the home screen and load it from there. But, once opened from the home screen, certain links will jump out and open in mobile safari – despite the fact that i preventDefault() on all link clicks!

    I have an onclick event handler bound at the body level. Using event delegation, it catches any click on any link, looks at its href, and dynamically calls a templating function to update the page. I call preventDefault() on the event object – for *some* of my links this works, and the page is updated with my template. For the links that result in a hit against the local database before outputting the results of the template, the links are opened in mobile safari.

    In desktop safari, all the links work even when i’m offline – something is happening that’s mobile safari specific.

    Any thoughts on why some links would open offline, but others not? None of the link URLs in question are listed in the manifest file, but they don’t need to be since the link action is prevented

    Jo, December 20th, 2009
  79. It’s hard to say without seeing exactly what you are doing.

    You should check out iUI or jQTouch and see how they handle page loads. I’d imagine they would be hijacking the links like you are but there might be a quirk to get around the issues you’re having.

    The Css Ninja, December 20th, 2009
  80. what do these links point to? are they all just regular http? some other protocols are not supported in the same manner in full screen mode as they are in mobile safari. Another reason might be your target (if any). Also, AJAX requests are not supported for caching

    Mike D, December 20th, 2009
  81. can you please help me … am new to web apps but produced one using Dashcode 2.04 as only have leopard not snow leopard and need to now make the app available for offline use. Can someone tell me how to do this ? I can see from another blogg that Dashcode 3 does this for you . But i do not want to do the whole app again . Any offers on this? would be very grateful if anyone can help as there is not a lot of info on dashcode around. I know i need to create manifest but need simple guide after producing the files and deploying. thank you

    nerissa, January 2nd, 2010
  82. I don’t use dashcode so I can’t help you there. I imagine updating your dashcode version won’t require you to do the web app again.

    The Css Ninja, January 2nd, 2010
  83. hi there sorry just if you could let me know how to make a manifest from the files I have deployed from Dashcode anyway please. Is it the same process you have above. thanks

    nerissa, January 3rd, 2010
  84. Thanks, worked flawlessly.

    Dae, January 9th, 2010
  85. hi all,

    just a question:

    what’s the easyest way to let the user “switch” from “local” and “not local” (without manifest)?

    (allowing to download the app “data” only on request?)

    using PHP would be useful to include() or not the .manifest, but i wonder how to “persist” the state because PHP is processed on the server-side before client execution…

    thank you in advance for any advice…

    Joachim, January 15th, 2010
  86. cookies?

    Joachim, January 15th, 2010
  87. @Joachim – cookies could be a good option you can then check with PHP if it’s local or not.

    The Css Ninja, January 15th, 2010
  88. thanks! this was my initial idea… I was wondering if there are other (better?) options…

    I’ll try it this way :)

    have a nice day
    Joachim

    Joachim, January 15th, 2010
  89. Cookies and a php controlled manifest works well.

    It is possible to implement a user button in the app to clear the cache which sets a cookie which causes php to return an empty manifest. Javascript can be used to force immediate reloads to complete the implementation.

    It can get hairy debugging the sequence but once you have it working it seems to be reliable.

    GeoNomad, January 16th, 2010
  90. I’ve got a question for Mike D.
    I’ve been busy creating a very cool webapp and now I want to get it in the apple store.
    Can you explain what you mean with: “build our own caching that reads the manifest”.
    Thanks

    Max

    maxmakes, January 17th, 2010
  91. Hi Ninja and GeoNomad,

    I’m developing an iPhone web app that utilizes cached data. The dialog here has been invaluable to debug various problems I’ve encountered.

    I, too, have used a dynamically generated manifest file, but even though the content type is clearly “test/cache-manifest” (thanks web-sniffer.net), the status of the applicationCache is Uncached. Static manifest files work just fine.

    Geonomad, did you set any other headers other than the Content-Type?

    Thanks

    CrazyIvan

    CrazyIvan, January 19th, 2010
  92. Hi CrazyIvan, if you’re are still getting uncached status even though the mime type is correct. This usually means your are referencing an asset that doesn’t exist or you have the wrong path to one or more of your assets.

    The Css Ninja, January 19th, 2010
  93. No other headers were needed.

    As Css Ninja says… look for non-existent items.

    When debugging, if something had gone wrong earlier, sometimes a respring of the iPhone was necessary to get cacheing to work again.

    GeoNomad, January 19th, 2010
  94. No problems with the files listed in the manifest.

    There was a newline before the CACHE-MANIFEST header. I was using Web-sniffer.net, but I didn’t have “Raw HTML View” selected. Without that option checked, the newline is not displayed.

    I knew that CACHE-MANIFEST had to be the first line, but the way I had written the ASP created a “hidden” newline. See below:

    <%
      Response.ContentType = "text/cache-manifest"
    %>[CRLF]
    CACHE-MANIFEST[CRLF]
    [CRLF]
    #yada[CRLF]
    #yada[CRLF]
    #yada[CRLF]
    

    The altered layout below corrected the issue:

    <%
      Response.ContentType = "text/cache-manifest"
    %>CACHE-MANIFEST[CRLF]
    [CRLF]
    #yada[CRLF]
    #yada[CRLF]
    #yada[CRLF]
    

    Thanks,

    CrazyIvan

    CrazyIvan, January 20th, 2010
  95. Hi Ninja,
    Your demos and this discussion have been invaluable.

    I am using an iTouch 3.1. In my tests I can open both a .pdf and a MS Word .doc file in mobile Safari while on line. If I list only the .pdf in the cache manifest file, it gets cached with no errors. This is good.

    If I list the .doc file in the manifest, I get an error event when loading the cache and the .doc file is not cached, even though it views OK when on-line. The manifest file is super simple and there are no typos. Here is the file:

    CACHE MANIFEST
    index.html
    cache_debug.js
    test.html
    test.pdf
    test.doc

    Leave out ‘test.doc’ from the manifest and everything is cached without errors. Put it in and we get a cache error even though the server downloaded with 200 OK.

    Here is the test with the .doc in place. Open the .doc while on line, it works, but it is not cached.
    http://sky-report.com/test/cache-test/index.html

    Have you heard any more on this? Does safari only cache a limited set of file types? It is strange that it fails to load the .doc into the cache.

    Also, do you know how to see the specifics of the error event? I am trying to see why safari is throwing an error on the .doc.

    THANKS!

    Pb, January 29th, 2010
  96. That’s an interesting find, there could be an undocumented list of files that will not be cached. So far we know audio, video and now doc files don’t cache. The audio and video is due to them opening in an external player but doc I’m not sure why it wouldn’t cache. I tested the page in firefox and everything caches fine including the .doc file, this leads me to believe mobile safari may have a balcklist of files it won’t cache?

    One theory could be that it won’t cache propriety formats, which would make all office documents fail when trying to cache. You could try saving the .doc as .rtf instead and see if that works?

    The Css Ninja, January 30th, 2010
  97. Hi Ninja,

    I have updated my test at
    http://sky-report.com/test/cache-test/index.html
    with an .rtf file instead of the doc. It behaves the same. I have some cache event logging so you can watch. It gets the file with 200 OK, but the DOMApplicationCache throws an error as soon as it hits a .doc, .rtf, .xls, and .ppt. Which are all files that can be opened by safari while on-line.

    So here is another theory. Mobile Safari may have a plug-in structure similar to other browsers, so it will render files of different types using its plug-in. We see that with PDF, audio and video. I believe in iPhone 2.x, the PDF would not cache for offline viewing.

    So this problem may not be a Safari problem in-as-much as a plug-in not being cache-aware.

    I think safari can also open some apple formats, I will try those also. More theories, testing and comments are welcome.

    BTW – how do I submit these questions to Apple? I have the free Dev registration but not the $99 yet. THANKS NINJA – YOU ROCK!

    Pb, January 30th, 2010
  98. That sounds quite plausible that the plugins may not be cache aware for certain file types.

    You can try the devforums, not sure if you need a paid account to get access. You could also file a bug.

    The Css Ninja, January 30th, 2010
  99. Hey Ninja,

    Since I couldn’t get this to work, I downloaded the source and uploaded it up another server. When o Reyes to so this again, it didn’t work like on your site.here is my testing link:

    manifesttest.atspace.com

    If you can tell me why this is so.

    Thanks,
    Gerome

    Gerome, February 3rd, 2010
  100. @Gerome – See GeoNomads comment for a useful tool to let you know if you are sending the manfiest through as the right mime type. Running your one returns text/plain you need to setup on your server to feed all .manifest files as text/cache-manifest.

    The Css Ninja, February 3rd, 2010
  101. Hi Ninja, i tried manifest to cache background image, the page is still reloaded and the page is refreshed. do you know how to fix this? thanks! (i wanted to cache the background image to avoid reloading the image when the site is online)

    atom, February 14th, 2010
  102. by the way, i’ve tried ‘offline’ of the page, it works fine. Just when it is online, i see the background image is always being refreshed.

    Thanks,
    Ray.

    atom, February 14th, 2010
  103. Someone heard about improvements to make video caching possible with HTML5?

    NicoVB, February 15th, 2010
  104. @Atom – Is it actually requesting and downloading the image again? It could be reloading it from the cache and that could appear to look like it’s downloading from the server again.

    The Css Ninja, February 15th, 2010
  105. WWWHHHYYYYY cant i get this to work lol,
    ok so i suck at this but heres what i wanna do ,
    i wanna use iWebKit on an app, no internet at all, just iWebKit and the app…i created a manifest file but in xcode, do i point to the manifest file or just to the index.html but make sure i have the manifest file in there?

    z3r01, March 2nd, 2010
  106. @z3r01 – Xcode should take care of the referencing and creating of the manifest file so all you need to so is load the index.html page. If you look at the source there should be an attribute on the html tag i.e. <html manifest=”…” >

    The Css Ninja, March 2nd, 2010
  107. Great stuff! The demo is working and I created my own example from your info and that’s also working.

    But, none of them work for me if I close the page in Safari and re-open, then I get a no connection alert from the browser and a blank page. Is this supposed to work like that? I don’t see the point if the user is not allowed to close the page in Safari..

    Btw, using a 3GS with everything up-to-date.

    Hansn, March 3rd, 2010
  108. @Hansn – Hmm, I just tried it myself using my demo (I’m on an updated 3GS too), put it into airplane mode went back in to safari and reloaded the page and went to the second page without a problem. I also closed the window, opened a new one and typed in the address again and it loaded without issue all while still in airplane mode.

    I did however get the connection error on the second page when clicking the “back to home” link, but I think that was because it was trying to load /index.html instead of just / and the fact that it isn’t added in the manifest. Looks like the cache gets confused when you try to link to the same file by using different names.

    The Css Ninja, March 4th, 2010
  109. Cheers for the testing! What I’m trying to do is to cache a web app that’s added to the home screen. This shouldn’t be any different from opening a page in Safari (right?). But what do you get if you:

    Open your demo, add it to the home screen, open safari, close the page, close safari and click the web app icon?

    That’s my problem here, I always get a connection error, but maybe it has something to do with trying to open / vs /index.html as you said.

    Hansn, March 4th, 2010
  110. Did a test and renamed your index.html to index1.html, created a new index.html with an onload redirect to index1.html just to have the home screen icon pointing to a file and not /. But I still get the same connection error. It feels like Safari is dumping the cache when the app is closed without having the page in it.. weird!

    Hansn, March 4th, 2010
  111. @Hansn – So I followed your steps of loading it in safari, putting it on the home screen etc. All works fine for me, loads the pages without issue and all the cached assets appear.

    Have you changed the demo at all, or is it the exact same as my hosted demo?

    The Css Ninja, March 5th, 2010
  112. It pretty much the same but I get the same thing when I run your demo, long step by step with your demo:

    1. Empty Safari cache, just to get a clean start…
    2. Open Safari
    3. Browse to Ninja demo, not loading the second page
    4. Add to home screen
    5. Open Safari
    6. Close demo tab
    7. Close Safari
    8. Enable Airplane Mode
    9. Run Ninja Demo with homescreen icon
    10. Safari is opening a new tab, saying “Turn off Airplane Mode…”
    11. Then saying “Cannot Open Page…”
    12. Blank boring page showing :(

    Something is very fishy here if you get the demo when doing this..

    Hansn, March 5th, 2010
  113. I can’t re-create your problem. I followed your steps exactly and it all worked fine for me? Have you got anyone else with an iPhone to try this demo out and follow your steps?

    The Css Ninja, March 8th, 2010
  114. Thanks, I’ll check with some people and post the result.

    Hansn, March 8th, 2010
  115. we just had a similar problem with one of our apps. turns out a complete shutdown of the iphone and a restart did the trick. the cache is caching again properly.

    pete, March 8th, 2010

Trackbacks

  1. [...] research into offline capabilities.  HTML 5 offers some exciting new capabilities in this area. Here’s a great article on how to take advantage of offline access to content in HTML [...]

    Going Offline with HTML5 | PointAbout: Mobilize Your Brand, July 22nd, 2009
  2. [...] und lässt sich sowohl dynamisch als auch von Hand erstellen. Richtig gute Anleitungen zum Thema gibt es bei thecssninja und auf [...]

    iFUN.de/iPhone :: Alles zum iPhone − Offline, mit Icon und im Vollbild: Tipps für den Entwurf eigener Web-Applikationen, December 30th, 2009
  3. [...] This article has gotten me started. [...]

    Caching content « fredsherbet.com, January 29th, 2010

Leave a comment