Offline JS

@agup

Can JS apps work offline?

Demo

esri.github.io/offline-editor-js/demo/

Why offline JS?

Loss of connection is common in mobile apps!!

Consumer

Commercial

Government

Why offline JS?

Allow users to keep working, seamlessly

 

Avoid application crashes or errors

 

Gracefully fail or degrade

Why offline JS?

 

Slow or intermittent cellular internet

 

Slow or intermittent WiFi

 

Avoid HTTP Timeouts

Why offline JS?

Why offline JS?

You don't have to be remote!

 

Can lose internet at any time.

 

Offline JS is caching of:

Data

 

HTTP requests

 

HTML/CSS/JS/images

Does browser storage work offline?

Does Geolocation work offline?

Intermittent offline

Full offline

&

Most common.

 

Offline for a short period of time

 

Does not require browser restart

 

Fail gracefully, graceful degradation

Intermittent Offline

Online

Offline

Online

Intermittent Offline

Bad, bad, bad

HTTP Request

Good, good, good

HTTP Request

Fully Offline

Offline for an extended time

 

Expect full functionality 

 

Requires offline browser restart

 

Online

Offline

Online

Fully Offline

Restart

Browser

APIs...?

Native JavaScript

    localStorage

    IndexedDB

    Application Cache

    Service Workers 

 

3rd party storage libraries

localStorage

Simple to use

 

Limited to 5 MBs

 

Store only Strings

 

Persistent between browser restarts

localStorage

// Window.localStorage

// Set an item -- Must be a String
localStorage.pageNumber = "2";

// Get an item
var pageNumber = localStorage.pageNumber;

// Remove an item
localStorage.removeItem("pageNumber");

IndexedDB

More complex than localStorage

 

50 - 100MB + (on mobile devices)

 

Stores Objects, Strings and more

 

github.com/axemclion/IndexedDBShim

IndexedDB

var transaction = db.transaction(["myObjectStore"], "readwrite");

transaction.oncomplete = function (event) {
    dbOnCompleteHandler(event);
};

transaction.onerror = function (event) {
    alert("There was a problem writing to the database");
};

var objectStore = transaction.objectStore("myObjectStore");

// Write object to database
objectStore.put(object);

Application Cache

A.K.A. Cache Manifest, AppCache

 

Stores .html, .js, .css and images

 

Files available online and offline

 

Temperamental 

 

Application Cache

Use with localStorage/IndexedDB!

 

Browser automatically recognizes the local copy

Application Cache

<html manifest="example.appcache">
  ...
</html>
CACHE MANIFEST
# Time: Thu Mar 26 2015 09:45:14 GMT-0600 (MDT)
# Home Page
http://www.example.com/index.html
http://www.example.com/main.js
http://www.example.com/css/main.css
http://www.example.com/images/header.png
/example.appcache

Application Cache

Typical console log...

Service Workers

Very limited support (Chrome)

 

Requires HTTPS

 

Acts as a network proxy

 

More complex than AppCache

3rd Party Storage

PouchDB, localForage

 

They wrap localStorage, IndexedDB and/or WebSQL

 

Require an addt'l library

 

Offer addt'l features

Detecting offline?

Offline.js (3 kb)

 

Set HTTP.timeout

Capture HTTP request failure 

 

Caveats:​

- 404's and connection issues

- Use Hybrid detection when available

XHR validate online

function validateOnline(callback) {

    var req = new XMLHttpRequest();
    req.timeout = 15000; //ms
    req.open("GET", "http://someurl.com/test.png?" + 
        (Math.floor(Math.random() * 1000000000)), true);

    req.onload = function() {
        if( req.status === 200 && req.responseText !== "") {
            req = null;
            callback(true);
        }
        else {
            req = null;
            callback(false);
        }
    };

    req.ontimeout = function() { callback( false ); };

    req.onerror = function() { callback(false); };

    req.send(null);
}

Offline.js - simple

function buttonClickHandler() {

    Offline.check();

    // Fail gracefully if offline
    if(Offline.state === "down") {
        alert("Sorry, app is currently offline");
    }
    else {
        // Do something
    }
}

Offline.js - advanced

function submitFormClickHandler() {

    Offline.check();

    var newAddress = street + "," + city + "," + zip;

    // Store changes locally
    if(Offline.state === "down") {
        localStorage.newAddress = localStorage.newAddress + 
            "|@|" + newAddress;
    }
    else {
        sendDataToServer( newAddress );
    }
}

Offline.on("up", function() {
    sendDataToServer( localStorage.newAddress, function() { 
        localStorage.newAddress = null; 
    });
});

Caveat Emptor

Very difficult to determine why HTTP requests fail.

What about hybrid?

PhoneGap/Cordova, Titanium, etc

 

Use the same JS coding patterns!

 

Cordova check network status:

cordova-plugin-network-information

Check network (Cordova)

function checkConnection() {
    var networkState = navigator.connection.type;

    var states = {};
    states[Connection.UNKNOWN]  = 'Unknown connection';
    states[Connection.ETHERNET] = 'Ethernet connection';
    states[Connection.WIFI]     = 'WiFi connection';
    states[Connection.CELL_2G]  = 'Cell 2G connection';
    states[Connection.CELL_3G]  = 'Cell 3G connection';
    states[Connection.CELL_4G]  = 'Cell 4G connection';
    states[Connection.CELL]     = 'Cell generic connection';
    states[Connection.NONE]     = 'No network connection';

    return states[networkState];
}

var internetStatus = checkConnection();

How permanent is storage?

Browser crash or device restart = okay

 

User clears browser cache = not good

 

Full device reset = not good

Best practices

Always:

   Listen for offline conditions

   Protect all HTTP requests

   Set HTTP timeout

   Cache resources when feasible

Best practices

Use as little memory as possible!

Upload 14MB zip

 

Add 1.5MBs to IndexedDB

___________________

 

Cached 196MB 

===============

Andy Gup

agup@esri.com
@agup
andygup.net

Bonus slide

How much browser storage?

Made with Slides.com