Will It Work If I'm Offline?
I recently released CreateLists, which is a very nice list manager for keeping track of all your projects and to-dos. It is also my first web app created to be fully functional when used offline.
Having first accessed and used the app, your browser will store the application and your data, so you can access it at any time, even when your device is not connected to the internet. Isn't that something!? I love using the app myself, I hope that you'll like it to.
Needless to say, I wasn't the first to think that making a web app work when the user is offline is a great idea. Here follows, a whirlwind introduction to the concept of "offline first", and how I built CreateLists, my first "offline first" web app.
Thinking offline first.
The team behind the excellent Hoodie project coined the term "offline first", and introduced it to the World in their post Say Hello to Offline First, where they raised the very important point:
We can’t keep building apps with the desktop mindset of permanent, fast connectivity, where a temporary disconnection or slow service is regarded as a problem and communicated as an error.
At the same time, the Hoodie team kick-started the "offline first" movement, aimed at researching good practices for creating software adhering to the principle of "offline first".
Alex Feyerke of the Hoodie team also published the article Designing Offline-First Web Apps in A List Apart Issue 386, wherein he nicely list a range of scenarios where interrupted connectivity can be problematic, and how you in each of these scenarios can deliver a good user experience by adopting the "offline first" perspective.
The pitfalls of going offline.
I actually started my venture into the offline first world by reading Jake Archibald's oddly titled, but very thorough, article "Application Cache is a Douchebag" published in A List Apart Issue 350.
In the article, Jake goes through the great number of pitfalls and gotchas he experienced when building an offline capable version of Lanyrd's mobile site. His main struggles are working with the many different layers of caching featured in modern browsers. When combining these caching mechanisms, they may behave in spurious or unexpected ways.
The article has an associated Github repository, and I found that the localstorage-cache demo is particularly worth studying in detail, as it combines most of the techniques described in the article.
How I went offline.
I built my first "offline first" web app CreateLists on the foundation of Jake's localstorage-cache demo. I've chosen to store static Mustache html templates in the AppCache, and dynamically fill these templates with user and application generated data.
The user interface is built on a slightly styled version of Bootstrap, and I have combined jQuery, jQueryUI and jQuery UI Touch Punch with a pot of CoffeeScript to create the app's functionality.
Being a raving fan of CouchDB I naturally chose to use PouchDB for storing user data in the browser (with IndexedDB/WebSQL in the bottom).
PouchDB (and CouchDB) handles data as versioned JSON documents, which is a very convenient data format to work with in both JavaScript/CoffeeScript and Python.
In CreateLists the user can create and manage pages, lists and list-items. Each of these are represented as separate documents in PouchDB.
To enable sync of user data between devices (a paid feature), I created a Tornado API server for handling stuff like user authentication and authorization of communication between CouchDB (server-side database) and PouchDB (browser database). I use nginx for serving the web app's static assets and as reverse proxy for the API and CouchDB.
Introducing sync, there is a need to resolve conflicts. Conflicts can be introduced when a document has been edited on an offline device, while also being changed on another device.
In CouchDB the conflicting versions of a document are kept, but CouchDB deterministically chooses a winner, and it's up to the client code to later resolve (or ignore) the conflict. I've opted for the conflict resolution model suggested in CouchDB's docs, where conflicts are resolved when documents are fetched.
You can be offline too.
It has been an eye-opening experience to develop a web app that aims at creating a great offline user experience. I got a glimpse of the possibilities awaiting web developers and forward looking companies, for creating novel mobile and "offline first" web experiences, when the current hype of forcing users to use native mobile apps wears off.