Offline Apps with HTML5: A case study in Solitaire

One of my contributions to the newly-launched Sencha Touch mobile framework is the Touch Solitaire game. This is not the first time I have ventured into the dizzying excitement of Solitaire game development; you may remember the wonderful Ext JS Solitaire from 18 months ago. I’m sure you’ll agree that the new version is a small improvement.

Solitaire is a nice example of a fun application that can be written with Sencha Touch. It makes use of the provided Draggables and Droppables, CSS-based animations, the layout manager and the brand new data package. The great thing about a game like this though is that it can be run entirely offline. Obviously this is simple with a native application, but what about a web app? Our goal is not just having the game able to run offline, but to save your game state locally too.

The answer comes in two parts:

Web Storage and the Sencha data package

HTML5 provides a brand new API called Web Storage for storing data locally. You can read all about it on my Web Storage post on Sencha’s blog but the summary is that you can store string data locally in the browser and retrieve it later, even if the browser or the user’s computer had been restarted in the meantime.

The crucial part of the sentence above is that we can only store string data. In the case of a game of Solitaire we need to store data on the elapsed time and number of moves as well as the location and status of each card. This doesn’t sound like the kind of data we want to manually encode into a string, so thankfully the data package comes to the rescue.

The Sencha Touch data package is a complete rewrite of the package that has been so successful in powering Ext JS 3.x. It shares many of the same philosophies and adds the learning we have gained from developing Ext JS 3.x over the past year. One of the new capabilities it offers us is a Local Storage proxy, which automatically marshalls your model data into local storage and transparently restores it when you need it.

Using the new proxy is simple – all we need to do is set up a new Store, specifying the Proxy and the Model that will be saved to it. Models are the spiritual successor to Ext JS 3.x’s Records. Now whenever we add, remove or update model instances in the store they are automatically saved to localStorage for us. Loading the store again is equally easy:

//set the store up
var gameStore = new{
    proxy: new{
        id: 'solitaire-games'
    model: 'Game'

//saves all outstanding modifications, deletions or creations to localStorage

//load our saved games{
    scope: this,
    callback: function(records) {
        //code to load the first record

And just like that we can save and restore games with Web Storage. We can visit our app’s webpage and start a game then come back later and find it automatically restored. But we still can’t play offline, for that we need the application cache.

The HTML5 Application Cache Manifest

The application cache is one of the best features of HTML5. It provides a simple (though sometimes frustrating) way of telling the browser about all of the files your application relies on so that it can download them all ready for offline use. All you have to do is create what’s known as a manifest file which lists all of the files the application needs – the Solitaire manifest looks like this:






We tell the browser about the manifest file by pointing to it in the tag’s manifest atttibute. When the browser finds this file it downloads each of the listed assets so that they are ready for offline consumption. Note that it does not automatically include them on the page, you still need to do that yourself via the usual link and script tags. Here’s a snippet of the Solitaire index.html file:

<!doctype html>
<html manifest="solitaire.manifest">
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8">	

        <link rel="stylesheet" href="resources/css/ext-touch.css" type="text/css">
        <link rel="stylesheet" href="resources/solitaire-notheme.css" type="text/css">
        <link rel="stylesheet" href="resources/themes/wood/wood.css" type="text/css">

        <script type="text/javascript" src="ext-touch-debug.js"></script>
        <script type="text/javascript" src="solitaire-all-debug.js"></script>

Note the manifest file definition in the html element at the top, and the fact that we still include our page resources the normal way. It sounds easy, but without a little setup first it can be a very frustrating experience. Usually your browser will try to cache as many files as possible, including the manifest file itself – we don’t want this. As soon as your browser has a long-term cache of the manifest file it is extremely difficult to update your application – all of the files are already offline and won’t be updated, and the browser won’t even ask the server for an updated manifest file.

Preventing this behaviour turns out to be fairly easy, and the solution in its simplest form comes in the shape of a .htaccess file with contents like the following:

<Files solitaire.manifest> 
    ExpiresActive On 
    ExpiresDefault "access" 

This directs Apache to tell the browser not to cache the manifest file at all, instead requesting the file from the server on every page load. Note that if the device is currently offline it will use the last manifest file it received.

This is half the battle won, but let’s say you change one of your application files and reload – you’ll find nothing happened. This is because when your browser asked the server for the manifest file it actually asked if the file had changed or not. As the manifest itself wasn’t updated, the server responds with a 304 (Not Modified) and your browser keeps the old file.

To make the browser pick up on the change to the application file you need to update the manifest file itself. This is where the mysterious “#rev49” comes in on the manifest example file above. This is a suggestion from the excellent diveintohtml5 article on the subject – whenever you change any application files just bump up the revision number in the manifest file and your browser will know to download the updated files.

One final detail is that your Apache server probably isn’t set up to server manifest files with the correct mime type, so be sure to add the following line to your Apache config and restart the server:

AddType text/cache-manifest .manifest  

Wrapping it up

Offline access is a big deal for mobile apps and Sencha Touch makes them much easier to write. The benefit is not so much that the apps can run without an internet connection (many modern touch devices have a near-permanent connection to the internet already), but that web apps can now be treated as first-class citizens alongside native apps.

The fact that many devices allow your users to save your app to their home screen and load it as though it were native is an important step – you keep all of the advantages of web app deployment while gaining some of the benefits of native apps. As more and more native hardware APIs become available to web apps their importance will only grow.

If you want to check out Solitaire’s offline support for yourself visit the application’s site and save it to your iPad’s home page. Try turning on airplane mode and loading the app and see how it behaves as though it were native. If you don’t have an iPad, you can load the app in up-to-date versions of Chrome or Safari and get a similar experience.

14 Responses to Offline Apps with HTML5: A case study in Solitaire

  1. Wells says:

    What do you know – it works on my brand new Ipad

  2. Timo Paschke says:

    It works really great on the iPad. Sometimes the double-tap event is not properly caught. In addition, it would be nice if you add some effects after winning the game…

    Really great work – this is a perfect demo to show the possibilities of modern web development!!!

  3. Pingback: ExtJS Solitaire : Ed Spencer

  4. Michael barr says:

    I saved the web page to home screen ok but when I switch on airplane mode and try to load the page, I get a “cannot connect to Internet error ” from safari.

    Am I doing something silly???


  5. Yaron Meiner says:

    thanks for the info, wrote a post about it that gathers some more issues we faced during development.

  6. Daniel says:

    Hi, this solitaire app looks great on the ipad. I’m wondering if the source code is available. I can only find the extjs version source code on github.

  7. Ed Spencer says:

    @Daniel it was in early versions of the Sencha Touch SDK. We actually pulled it out of 1.0 because of some API changes that meant we didn’t have time to migrate it (it’s a pretty unusual app for Sencha Touch so had a lot of custom code, and this was in the pre-beta days!).

    It will make a comeback, but I’m not sure when. You can grab the code by viewing the example source, but it’s using a fairly outdated API.

  8. Pingback:

  9. Pingback: cardgame | becknspace

  10. menway says:

    Ed, thank you for the post. It’s very clear for a new HTML5 learner.

    BTW, is it possible to do the same way for an ExtJS 4 project? Besides app-all.js, ext.js, and application resource files, what other files need be included in Cache Manifest (maybe some css files and other necessary ext resources)? Could you please write another guideline for offline ExtJS4 projects?

    Thanks in advance!

  11. Ed Spencer says:

    @menway yes you can apply the same approach to desktop apps using Ext JS, though the older browsers do not support the application cache. You’d need to add a fairly large number of items from the resources folder because there are so many images. It’s something we’ve been kicking about lately as a place to improve.

  12. Hi,

    Great post and example. I was wondering if the the source code available to this version (Touch 2), I found the older version on github?


  13. Santosh Sah says:

    Thanks Ed,

    It is a great post and example. It would be very helpful if you could provide source code for version Sencha Touch 2.2


  14. edspencer says:

    @Santosh sure would, but maybe you can beat me to it 🙂

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: