Working Offline with HTML5 web apps



with tips and trips for Firefox OS

Hi! I'm Francisco Jordano



  @mepartoconmigo  
arcturus

The story ...





No more cable!

The story ...





Lots of new devices

The story ...


and all of them running stunning browsers!

The story ...


... but this is happening quite often

The London Underground problem



We have WiFi connection in each station

The London Underground problem




but we lose it when the train leaves the station

Try to avoid this



For this



What are we going to talk about?



80% things you apply to the web
20% optimisations and Firefox OS specific

What are we going to talk about?


We can tackle this problem with:


CONNECTIVITY DETECTION

and

CLIENT SIDE STORAGE

Are we online yet?




Losing connection happens more often than we expect

Are we online yet?


Check it!

//navigator.online;
if (navigator.onLine) { alert('online'); } else { alert('offline'); }
Supported in almost all the browsers.

Are we online yet?


Listen for changes in the connection

window.addEventListener('offline',    function onOffline() {  // We are offline});
window.addEventListener('online',    function onOnline() {  // We got the connection back});

Are we online yet?



DEMO

Are we online yet?



Unfortunately we have some problems:

Doesn't give information about why we are offline

What Firefox OS is doing?

Better messaging

What Firefox OS is doing?


Example of online/offline detection

Store all the things!




We can save our relevant content on the client side 
to play later with it

Let's remember how many ways of storing locally we have

A really old friend


  • We can save up to 4KB
  • Modified locally and server side
  • Have expiration date
  • They are damn fast

but
  • Can be modified server side
  • We send them with each request

The new kid on the block


LocalStorage

  • Seriously, is all around, supported in all browsers
  • Simple interface key/value
  • Different default limits by browser (2.5, 5 and 10 MB)
  • Doesn't has expiration

 localStorage.myKey = myValue;
... but ...

  • Cannot be accessed by workers
  • Funny implementation: http://www.filldisk.com/
  • Can store just strings
  • But the most important, it's SYNCHRONOUS!

IndexedDB to the rescue!


  • It is asynchronous!
  • It's versioned!
  • It's transactional!
  • Support indexes for faster searches!
  • Offers bigger space (5 to 50 MB) and can be surpassed if user agrees


  • ... but ...
  • Well supported, but still missing some browsers :(
  • Low level API
  • 'Complicated' learning curve

IndexedDB example


Opening a connection with the database

var myDB;
var request = window.indexedDB.open("MyBookShop", 1);

request.onsuccess = function onSuccess(event) {
    myDB = event.target.result;
};request.onerror = function onError(event) {    alert('Sorry, cannot open the DB');};

IndexedDB example


We do have versioning! \o/

// Once opened the connectionrequest.onupgradeneeded = function onUpgradedNeeded(event) {
    var db = event.target.result;
    var oldVersion = event.oldVersion;
    
    // Apply any schema upgrade if needed
    createSchema(db);
};

We can modify the schema just within onupgradeneeded

IndexedDB example

We create object stores and indexes for searches

function createSchema(db) {
    // { 'isbn' : <String> (primary key),    //   'title' : <String>,
    //   'date' : <Date>,
    //   'author' : <String>,
    //   'cover': <Blob>
    // }

    var objectStore = db.createObjectStore('books',         { keyPath: 'isbn' });
// Search by title objectStore.createIndex('title', 'title'); // Search by author objectStore.createIndex('author', 'author'); }

IndexedDB example


We have transactions!


var transaction = myDB.transaction(['books'], 'readwrite');transaction.onerror = function() { // Handle error };transaction.oncomplete = function() { // Handle success };
var objectStore = transaction.objectStore('books');objectStore.add(book);

And we save objects!

IndexedDB example


And we can use the indexes to perform searches

var objectStore = myDB.transaction('books').objectStore('books');
var index = objectStore.index('author');
index.openCursor().onsuccess = function(event) {
    var cursor = event.target.result;
    if (!cursor) {/* We finished */}
    // Process each object in cursor.value
    cursor.continue();
};

We can use a streaming approach to process data!
Long live to cursors!

WoW that was dense!


Yeah, a lot of stuff there isn't? Some people have some code to help us.

even the guys from Firefox OS created their own utility:

Getting ready to work offline


Now we can save user's data locally.

Are we ready for that?

HTML5 AppCache


manifest.appcache
CACHE MANIFEST
# version 1.0

index.html
style.css
image.png

# Use from network if available
NETWORK:
*

# Fallback content
FALLBACK:
/ offline.html
Server this file with mime type: text/cache-manifest

HTML5 AppCache

Use it in your html

<html manifest="manifest.appcache">
   <h1>YAY!</h1></html>

And/or listen to events:
  • checking
  • noupdate
  • downloading
  • progress
  • cached
  • updateready
  • obsolete
  • error

... but it not the panacea


Unfortunately has it downsides


But hey! Let's try to fix it


or evolve it

AppCache and Firefox OS


If you are using appcache and have a Firefox OS app,
define it into the application manifest

{
  "name": "My App",
  "description": "My elevator pitch goes here",
  "launch_path": "/",  "appcache_path": "manifest.appcache",  "icons": {
    "128": "/img/icon-128.png"
  },
  "developer": {
    "name": "Your name or organization",
    "url": "http://your-homepage-here.org"
  },
  "default_locale": "en"
}

... and improve user experience

Web Apps with AppCache will download content before use

Firefox OS Packaged Apps


Another way of solving the offline problem is just downloading the whole content!



Packaged, on a simple format that we all know like ZIP

Firefox OS Packaged Apps


Simply add the following extra field to your application manifest

{
  "name": "My App",
  "description": "My elevator pitch goes here",
  "launch_path": "/",  "package_path": "/application.zip",   "size": 1000000,  "icons": {
    "128": "/img/icon-128.png"
  },
  "developer": {
    "name": "Your name or organization",
    "url": "http://your-homepage-here.org"
  },
  "default_locale": "en"
}

Extra information for Packaged Apps



Questions?




@mepartoconmigo

Thanks a lot!!


Don't miss the chance to know more about Firefox OS!

Ask us!

Working Offline

By Francisco Jordano