HTML5 Offline Web Apps: Cache Manifest Gotchas Are Fun

Spent the last few days working on an iOS app, focusing on the iPhone. The goal was to make a simple HTML5 game that can be played offline, in Airplane Mode, etc.

Everything was working great. I got it on my phone & everything. Tested it on a bunch of other phones via willing victims (aka, family & friends). Then suddenly, the offline caching functionality broke. The app would load fine the first time, while online but then would fail to load again online or off.

On initial load Chrome’s console showed this:

Creating Application Cache with manifest http://myurlpath.com/my/app/path/sm2/simplegame2.appcache
Application Cache Checking event
Application Cache Downloading event
Application Cache Progress event (0 of 38) http://myurlpath.com/my/app/path/sm/images/1.png
Application Cache Progress event (1 of 38) http://myurlpath.com/my/app/path/sm/images/10.png
Application Cache Progress event (2 of 38) http://myurlpath.com/my/app/path/sm/images/card.png
Application Cache Progress event (3 of 38) http://myurlpath.com/my/app/path/sm/images/startstop3.gif
Application Cache Progress event (4 of 38) http://myurlpath.com/my/app/path/sm/images/plusminus2.gif
Application Cache Progress event (5 of 38) http://myurlpath.com/my/app/path/sm/images/pic3.png
Application Cache Progress event (6 of 38) http://myurlpath.com/my/app/path/sm/images/plusminus4.gif
Application Cache Progress event (7 of 38) http://myurlpath.com/my/app/path/sm/images/pic8.png
Application Cache Progress event (8 of 38) http://myurlpath.com/my/app/path/sm/images/time.png
Application Cache Progress event (9 of 38) http://myurlpath.com/my/app/path/sm/game.js
Application Cache Progress event (10 of 38) http://myurlpath.com/my/app/path/sm/game.css
Application Cache Progress event (11 of 38) http://myurlpath.com/my/app/path/sm/images/s.png
Application Cache Progress event (12 of 38) http://myurlpath.com/my/app/path/sm/images/8.png
Application Cache Progress event (13 of 38) http://myurlpath.com/my/app/path/sm/images/2.png
Application Cache Progress event (14 of 38) http://myurlpath.com/my/app/path/sm/images/5.png
Application Cache Progress event (15 of 38) http://myurlpath.com/my/app/path/sm/images/simplememory_icon.png
Application Cache Progress event (16 of 38) http://myurlpath.com/my/app/path/sm/images/startstop4.gif
Application Cache Progress event (17 of 38) http://myurlpath.com/my/app/path/sm/images/plusminus3.gif
Application Cache Progress event (18 of 38) http://myurlpath.com/my/app/path/sm/images/pic4.png
Application Cache Progress event (19 of 38) http://myurlpath.com/my/app/path/sm/images/startstop1.gif
Application Cache Progress event (20 of 38) http://myurlpath.com/my/app/path/sm/images/level.png
Application Cache Progress event (21 of 38) http://myurlpath.com/my/app/path/sm/images/found.png
Application Cache Progress event (22 of 38) http://myurlpath.com/my/app/path/sm/images/pic1.png
Application Cache Progress event (23 of 38) http://myurlpath.com/my/app/path/sm/images/blank.gif
Application Cache Progress event (24 of 38) http://myurlpath.com/my/app/path/sm/images/pic6.png
Application Cache Progress event (25 of 38) http://myurlpath.com/my/app/path/sm/images/9.png
Application Cache Progress event (26 of 38) http://myurlpath.com/my/app/path/sm/images/6.png
Application Cache Progress event (27 of 38) http://myurlpath.com/my/app/path/sm/images/simplememory_startup.png
Application Cache Progress event (28 of 38) http://myurlpath.com/my/app/path/sm/images/attempts.png
Application Cache Progress event (29 of 38) http://myurlpath.com/my/app/path/sm/images/3.png
Application Cache Progress event (30 of 38) http://myurlpath.com/my/app/path/sm/images/0.png
Application Cache Progress event (31 of 38) http://myurlpath.com/my/app/path/sm/images/startstop2.gif
Application Cache Progress event (32 of 38) http://myurlpath.com/my/app/path/sm/images/pic5.png
Application Cache Progress event (33 of 38) http://myurlpath.com/my/app/path/sm/images/plusminus1.gif
Application Cache Progress event (34 of 38) http://myurlpath.com/my/app/path/sm/images/pic7.png
Application Cache Progress event (35 of 38) http://myurlpath.com/my/app/path/sm/images/pic2.png
Application Cache Progress event (36 of 38) http://myurlpath.com/my/app/path/sm/images/7.png
Application Cache Progress event (37 of 38) http://myurlpath.com/my/app/path/sm/images/4.png
Application Cache Progress event (38 of 38) 
Application Cache Cached event  

all subsequent attempts to reload the page showed this confusing message in the console:

Document was loaded from Application Cache with manifest http://myurlpath.com/my/app/path/sm2/simplegame2.appcache
Application Cache Checking event
x GET http://myurlpath.com/my/app/path/sm2/game.css 
x GET http://myurlpath.com/my/app/path/sm2/game.js 
x GET http://myurlpath.com/my/app/path/sm2/images/blank.gif 
x GET http://myurlpath.com/my/app/path/sm2/images/card.png 
x GET http://myurlpath.com/my/app/path/sm2/images/startstop1.gif 
x GET http://myurlpath.com/my/app/path/sm2/images/startstop4.gif 
x GET http://myurlpath.com/my/app/path/sm2/images/level.png 
x GET http://myurlpath.com/my/app/path/sm2/images/10.png 
x GET http://myurlpath.com/my/app/path/sm2/images/plusminus3.gif 
x GET http://myurlpath.com/my/app/path/sm2/images/4.png 
x GET http://myurlpath.com/my/app/path/sm2/images/plusminus1.gif 
x GET http://myurlpath.com/my/app/path/sm2/images/time.png 
x GET http://myurlpath.com/my/app/path/sm2/images/s.png 
x GET http://myurlpath.com/my/app/path/sm2/images/attempts.png 
x GET http://myurlpath.com/my/app/path/sm2/images/found.png 
Application Cache NoUpdate event

It was a simple error. I changed my server’s directory but totally forgot to update the paths in my CACHE MANIFEST file to reflect this (duh!), so the app would load once and then fail every time after the Google Chrome’s console showing a failed “GET” request on a bunch of files at the top of my manifest. Specifically, I forgot to change this path: “http://myurlpath.com/my/app/path/sm/game.css” to this “http://myurlpath.com/my/app/path/sm2/game.css”. Manifest-Validator.com did not show me any error (naturally, it wouldn’t be able to detect human stupidity). Manifest files don’t provide anything like compiler warnings, so little things like this, even something like a hidden character for a line break can screw things up easily. A similar error, only involving case sensitivity of manifest files is described in the solution here.