43
Taking web apps oine [email protected] @pedromorais morais on app.net GDG Portugal DevFest March ’13

Taking Web Apps Offline

Embed Size (px)

Citation preview

Takingweb appsoffline

[email protected]@pedromorais

morais on app.netGDG Portugal DevFestMarch ’13

Offline webapps - why?

no network connection requiredfaster startupkeep data local

The Road to Offline

user data

assets

1. user data

localStorage*and sessionStorage

localstorage

Simple API

window.localStorage[‘hello’] = ‘world’;

localStorage

Can store a JSON string

window.localStorage[‘helloJSON’] =

JSON.stringify({‘key1’: ‘world’,

‘key2’: ‘Lisbon’});

http://caniuse.com/indexeddb92.25%

localStorage limitationsStorage Limits

Spec - 5 MbFirefox - can adjusted

Chrome - 2600k chars (5 mb in UTF-16)

IE - 5000k chars

localStorage limitations

Sync API

window.localStorage[‘sloooow’] = ‘imagine a 1 MB string here’;

sql storage

sql storagework stopped November ’10

sqlite was used in all implementations(webkit, opera)

mozilla & microsoft: “not gonna happen”

http://caniuse.com/sql-storage48.32%

sql storage

http://nparashuram.com/IndexedDBShim/

indexed db polyfill over websql

indexed db

indexed dbAsync API

var request = indexedDB.doStuff();

request.onerror = function(event) {

...

};

request.onsuccess = function(event) {

...

};

indexed db

Opening a database

var request = indexedDB.open("MyTestDatabase");

request.onerror = ...

var db;

request.onsuccess = function(event) {

db = request.result;

};

creating the schema

var request = indexedDB.open("MyTestDatabase", 5);

...

request.onupgradeneeded = function(event) {

var db = event.target.result;

...

};

creating the schema# student: cardNumber, name, email, ...

request.onupgradeneeded = function(event) {

var db = event.target.result;

var studentsStore = db.createObjectStore("students",

{ keyPath: "cardNumber" });

studentsStore.createIndex("nameIndex", "name",

{ unique: false });

studentsStore.createIndex("emailIndex", "email",

{ unique: true });

};

adding datavar transaction

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

transaction.oncomplete = function(event) {

  alert("All done!");

};

...

// transactions go away when you

// return to the event loop

// without making a request

adding data...

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

aStudent = { ‘studentCard’: ‘44124’,

‘name’: ‘Pedro Morais’,

‘email’: ‘[email protected]'}

var request = objectStore.add(aStudent);

request.onsuccess = function(event) {

  // event.target.result == aStuddent.studentCard

};

updating data...

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

aStudent = { ‘studentCard’: ‘44124’,

‘name’: ‘Pedro Morais with updated name’,

‘email’: ‘[email protected]'}

var request = objectStore.put(aStudent);

request.onsuccess = function(event) {

  // event.target.result == aStuddent.studentCard

};

deleting data

...

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

var request = objectStore.delete(‘44124’);

request.onsuccess = function(event) {

// deleted

};

getting data

var transaction = db.transaction(["students"]);

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

var request = objectStore.get(‘44124’);

request.onsuccess = function(event) {

console.log(“Name is “, request.result.name);

};

using a cursor....

var request = objectStore.openCursor();

request.onsuccess = function(event) {

var c = event.target.result;

  if (c) {

    console.log

("Student " + c + " is named " + c.value.name);

    cursor.continue();

  } else {

    console.log("Done!");

  }

};

using an index

...

var index = objectStore.index("name");

index.get("Pedro Morais").onsuccess = function(event) {

  console.log("Email=" + event.target.result.email);

};

// if name is not unique

// you get the first entry that matches

using an index + cursor...

var index = objectStore.index("name");

var request

= index.openCursor(IDBKeyRange.only("Pedro Morais"));

request.onsuccess = function(event) {

  var c = event.target.result;

  if (c) {

    // c.key is a name, like "Pedro Morais",

// c.value is the whole object.

    console.log("Name: " + cursor.key +

“Email: " + cursor.value.email);

    cursor.continue();

  }

};

cursor key ranges

IDBKeyRange.only("Pedro Morais")

IDBKeyRange.lowerBound("Pedro")

IDBKeyRange.lowerBound("Pedro", true) // don’t inc Pedro

IDBKeyRange.upperBound("Pedro", true) // don’t inc Pedro

IDBKeyRange.bound("Pedro", "Vanda", false, true);

// include Pedro, don’t include Vanda

http://caniuse.com/indexeddb46.93%

2. assets

offline web apps

http://caniuse.com/offline-apps67.43%

offline web apps<!DOCTYPE html>

<html manifest=”cache.appcache”>

...

cache.appcache must be served astext/cache-manifest

cache manifestCACHE MANIFEST# this is a comment

css/styles.cssjs/scripts.jsimages/logo.png

NETWORK:*

cache with fallbackCACHE MANIFEST# this is a comment

css/styles.cssjs/scripts.jsimages/logo.png

FALLBACK:/ /offline.html

NETWORK:*

network accessnot using appcache

• user navigates to http://test.com/app.html

• browser check if file “app.html” is in cache

• browser check if it has not expired

• browser checks validity of file using etags (optional)

• browser renders the cached or downloaded app.html

network accessusing appcache

• user navigates to http://test.com/app.html

• browser renders app.html from appcache

• in the background:

• browser checks if manifest has changed

• if it has, browser downloads all files in the manifest (expires, etags still apply)

• user only gets new version of app.html the next time!

cache manifest versionsCACHE MANIFEST# version 50

css/styles.cssjs/scripts.jsimages/logo.png

NETWORK:*

cache manifest versionsCACHE MANIFEST

css/styles.css?b3c4dejs/scripts.js?adf341images/logo.png?ef3451

NETWORK:*

+ far future expires

cache manifest versionsCACHE MANIFEST

b3c4de/css/styles.cssadf341/js/scripts.jsef3451/images/logo.png

NETWORK:*

+ far future expires

conclusion

Available today

localStorageIndexed DB (+ polyfill)

AppCache

Take your web apps offline

Thanks!

[email protected]@pedromorais

morais on app.netGDG Portugal DevFestMarch ’13