Creating an application using Amazon's Mobile App Distribution Program is a great way for developers who have hosted web apps to create downloadable apps available on the Amazon Appstore. Web app developers benefit from the increased discoverability that the store provides when users are searching for new apps, as well as being reminded to use the web app by the icon on their device's home screen afterwards. In addition, all of a user’s linked devices that use the Amazon Appstore will have the icon waiting in their Cloud list of apps as well.
And because web apps are hosted online, developers have increased flexibility to re-use existing assets and make changes or fixes to the app quickly and easily, without having to re-create a new app that has to be re-installed by the end user. But what happens if the user wants to use the app offline? Obviously, if the app relies on live server-side content - such as streaming videos or airline reservations - then obviously it's not going to work. But if the web app is self-contained and doesn't need to talk to a server for its core functionality - like most games - then being able to take the app offline is something that should be an option for users.
Happily, enabling your web app to be used offline can be done using HTML5's built-in Application Cache with only a few small changes to your code. Below I'll outline the steps you need to take to create a basic offline web app. It's surprisingly easy to set up, but beware! Application Cache has a well deserved reputation for being difficult for developers to get a handle on.
The first thing you need to do is create an HTML5 manifest text file with a list of every asset file your web app requires - HTML, JavaScript, CSS, images, icons, fonts, etc. The manifest file can have any name you choose, but we'll use app.appcache to be clear and to avoid overlap with other types of manifest files.
Here's the content of a basic manifest file:
CACHE MANIFEST # Version 1.0 CACHE: main.js main.css logo.png NETWORK: *
- The first line needs to be CACHE MANIFEST.
- The second line in this example is just a comment, but is useful to make changes to your web app by simply incrementing the version number. Note: Only changes to the manifest file will invalidate the cache.
- The third line begins the CACHE: section where you list out the asset files used by your web app, either relative to the location of the manifest file, an absolute path or complete URL. Note: DO NOT list app.appcache in your manifest.
- The NETWORK: section has a wildcard which permits the browser to download files that are not listed in the CACHE: section. Note: Without the NETWORK: section, the browser will ONLY re-request files listed in the CACHE: section after the initial page load.
You need to also make sure your web server serves the correct MIME type for the manifest file. For Apache, it looks like this:
AddType text/cache-manifest .appcache
You also need to makes sure the manifest file is not being cached on the server. If the HTTP Cache-Control header for the manifest file doesn't update, or a 304 Not Modified is return, then the web engine won't be able to see if the manifest file has been changed or not, which is the only way to invalidate the offline cache.
You then need to add an attribute to the tag of every HTML page you serve pointing at the manifest file, like this:
<html manifest="app.appcache">
Finally, you need to make sure your app updates itself if the manifest changes - the easiest way to do this is to add this bit of JavaScript to your main HTML:
<script> window.applicationCache.addEventListener('updateready', function(e){ window.location.reload(); }); </script>
Your web app should now be offline enabled! If you have Python installed, you can test this by setting up a local server to see what's happening both on the server and in the browser.
beattier@amazon.com:~/html5demos/offline$ python -m SimpleHTTPServer Serving HTTP on 0.0.0.0 port 8000 ... 1.0.0.127 - - [21/Jan/2014 13:42:52] "GET / HTTP/1.1" 200 - 1.0.0.127 - - [21/Jan/2014 13:42:52] "GET /app.appcache HTTP/1.1" 200 - 1.0.0.127 - - [21/Jan/2014 13:42:52] "GET /main.css HTTP/1.1" 200 - 1.0.0.127 - - [21/Jan/2014 13:42:52] "GET /main.js HTTP/1.1" 200 - 1.0.0.127 - - [21/Jan/2014 13:42:52] "GET /logo.png HTTP/1.1" 200 - ...
If you request the page again, you'll see that *only* the manifest is requested.
1.0.0.127 - - [21/Jan/2014 13:43:12] "GET /app.appcache HTTP/1.1" 304 -
By modifying the manifest file and reloading, you'll see that all the files listed will be re-downloaded again.
You can also connect the Amazon Web App Tester to see the client side of the process as well by using Remote Debugging. (See our previous overview of setting up the Tester here.) In the screenshot above, I've connected to a Kindle Fire HDX and loaded a demo offline web app stored on Github. By looking at the Resources tab and drilling down into the Application Cache folder, I can see the assets that are cached locally, and a log of the Application Cache events.
This is just a basic way to setup an offline web app. There are more options that you can add to your manifest file, more events you can track in JavaScript and more functionality you can use to make your web app's offline experience much more seamless to the end user. Check out the links below for more information.
Conceptually, it's important to understand that once you've enabled a manifest, your web app is now offline first and forever. Let's repeat that for clarity: OFFLINE FIRST AND FOREVER.
OFFLINE FIRST means:
- Your web app's files will then always be loaded from the offline cache first, and then a request will be made to the server for the manifest file to see if there have been any updates.
- The browser will not automatically refresh if the manifest has changed. It will in fact download the files from the server, but it will wait until the next time the page is requested to use them. This is why the script in step 4 above to detect a manifest change and immediately refresh the page is important.
FOREVER means:
- The only thing that can invalidate the offline cache and trigger a re-download of files is a change in the contents of the manifest file - not just the timestamp.
- There is no programmatic way to invalidate the offline cache from the browser. Even changing or deleting the manifest attribute in the tag will not invalidate the cache.
- Until the browser requests a manifest and receives a 404 or 410 from the server, it will continue to consider the web app as being offline and use the last downloaded version, rather than updating from the server.
The info above should be able to get you started with offline web apps. Once you've added in the manifest, your web app will be available offline the next time your users load up your app. Fair warning: This can be a tricky feature to implement - especially if you misconfigure or forget some of the steps above. Getting the browser to let go of the manifest and refresh your code can be incredibly frustrating. I think the upside is worth it though, as enabling your web app to be used anywhere a native app can be used is incredibly satisfying.
To get more information about Application Cache, I encourage you to check out these great articles which dive into the topic in even more detail.
· http://diveintohtml5.info/offline.html
· http://www.html5rocks.com/en/tutorials/appcache/beginner/
· http://www.html5rocks.com/en/mobile/workingoffthegrid/
· http://alistapart.com/article/application-cache-is-a-douchebag
In future posts, I'll expand on offline apps by looking at topics such as offline data storage and efficient caching strategies.
-Russ (@RussB)