First, Some Politics
I wanted to take a little while before I explored Part 4. This is probably going to be one of the more controversial technologies I used in this demo: Application Cache. It's controversial because Application Cache is a Douchebag. I can confirm: even just making a demo based on Application Cache had him aimlessly spilling many a beer on my nice new shirts.
The reality is that this is an ideal application of AppCache; none of Jake Archibald's Gotchas really apply here:
"Gotcha #1: Files always come from the ApplicationCache, even if you’re online"
Because we've designed this app to be entirely static (e.g.: depends on coupling only with static services that can be updated in batch pushes), a stale application in an online state isn't necessarily a bad thing.
However, if you're designing an application that depends on some tighter-coupled relationships with dynamic services while online, Application Cache may not be for you.
(A) You shouldn't be tightly coupling your services so much that an update breaks stale frontends, and
(B) You shouldn't be AppCaching a highly dynamic page that's making lots of service calls.
If you want to make a highly dynamic application do work offline, you're going to have to wait for ServiceWorkers to be ready. Right now, it has experimental support in Firefox and Chrome, and it's on Apple's "Five Year Plan", whatever that means.
"Gotcha #2: The ApplicationCache only updates if the content of the manifest itself has changed"
This is one of those situations where good developer tools come in very useful. Manually incrementing a versioning comment in the manifest every time you want to refresh the page is really painful. Ideally, you'd follow a couple of best practices:
Don't have a manifest in development. Or if you have a manifest, maybe it's best to do name it something like
cache.manifest.template. Everyone already sets their development browser to blow away the cache on every reload- you don't need another, more difficult-to-manage cache getting in the way.
In staging/production, couple your manifest to source control. The easiest way to do this would be to take some concatenation of timestamp, tag name, commit hash- whatever, and have that become part of the AppCache manifest's comments.
Now, The Demo
There are two steps to making the compass work offline:
<html lang=en-US manifest='cache.manifest'>
That's where our AppCache is, and our AppCache says this:
CACHE MANIFEST # rev <revision number> FALLBACK: / /index.html CACHE: /index.html /index.js /index.css
Line by Line:
CACHE MANIFEST is required at the beginning of every AppCache.
# rev <revision number> - This is a comment, but changing the comment changes the file, which forces a new pull of the cached data when it's loaded. Note Jake Archibald's Gotcha #3, that this will only actually happen when the Cache-Control settings say it should.
FALLBACK: - This starts the section where we essentially define route fallbacks. Because we don't have a connection to a server, we don't necessarily get to make assumptions such as
/ -> /index.html. We really don't get to make more complex assumptions such as
/images/foo.png -> /images/offline/bar.png.
CACHE: - These are the routes that we want to cache. Note that this is not just "that we want to be available offline", all of these will be cached whether you're offline or not.
There are other fields like
NETWORK:, but due to problems which may or may not be related to the upcoming phase-out of AppCache, it's not extremely useful to talk about that here. If you came here from a google search hoping to learn more about this, I'm so, so sorry, but hopefully you can learn more at MDN and its link section.
One thing that kind of threw me for a loop was migrating this file to http://scottmmjackson.github.io/compass - you can't package the AppCache with the app. It has to be aware of its own route:
CACHE MANIFEST # rev 8 FALLBACK: /compass/ /compass/index.html CACHE: /compass/index.html /compass/index.js /compass/index.css
Unfortunately, this is a compelling reason not to have an AppCache at all, but to have a template that gets processed by a backend service which fills in the relative paths with domain-absolute paths. However, this introduces truly funny behavior when the template service breaks and delivers a bad (or worse, incomplete) Application Cache
In summary, I can't wait for ServiceWorkers. I really can't wait for the Geofencing API.