SlideShare une entreprise Scribd logo
1  sur  69
Télécharger pour lire hors ligne
Offline applications
Jérôme Van Der Linden - 28/10/2016
jeromevdl @jeromevdl
Jérôme
Van Der
Linden
@OCTOSuisse @OCTOTechnology
AT AW AD
AnyWhere ?
AnyWhere ?
AnyWhere ?
Offline applications
5 questions to ask before creating an offline application
Question #1
What can I do offline ?
READ
CREATE
UPDATE
UPDATE, SURE ?
DELETE
Question #2
How much
data is it and
where can i store it ?
Few kilobytes…
Few
megabytes
Hundred of
megabytes
(maybe few giga)
Several gigabytes (or many more) ?
Storage Solutions
Application Cache
<html manifest="/cache.manifest">
...
</html>
CACHE MANIFEST
# Explicitly cached
CACHE:
/favicon.ico
page.html
stylesheet.css
images/logo.png
scripts/main.js
http://cdn.example.com/scripts/main.js
# Resources that require the user to be online.
NETWORK:
*
# static.html will be served if main.py is inaccessible
# offline.jpg will be served in place of all images in images/large/
FALLBACK:
/main.py /static.html
images/large/ images/offline.jpg
cache.manifest
index.html
http://www.html5rocks.com/en/tutorials/appcache/beginner/
http://alistapart.com/article/application-cache-is-a-douchebag
Application Cache
<html manifest="/cache.manifest">
...
</html>
CACHE MANIFEST
# Explicitly cached
CACHE:
/favicon.ico
page.html
stylesheet.css
images/logo.png
scripts/main.js
http://cdn.example.com/scripts/main.js
# Resources that require the user to be online.
NETWORK:
*
# static.html will be served if main.py is inaccessible
# offline.jpg will be served in place of all images in images/large/
FALLBACK:
/main.py /static.html
images/large/ images/offline.jpg
cache.manifest
index.html
http://www.html5rocks.com/en/tutorials/appcache/beginner/
http://alistapart.com/article/application-cache-is-a-douchebag
Service Workers (Cache API)
this.addEventListener('install', function(event) {
event.waitUntil(
caches.open('v1').then(function(cache) {
return cache.addAll([
'/sw-test/',
'/sw-test/index.html',
'/sw-test/style.css',
'/sw-test/app.js',
'/sw-test/star-wars-logo.jpg',
'/sw-test/gallery/',
'/sw-test/gallery/myLittleVader.jpg'
]);
})
);
});
2. Installation of Service Worker
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register(‘/sw.js')
.then(function(registration) {
// Registration was successful
}).catch(function(err) {
// registration failed :(
});
}
1. Registration of Service Worker
self.addEventListener('fetch', function(event) {
event.respondWith(
caches.match(event.request)
.then(function(response) {
// Cache hit - return response
if (response) {
return response;
}
var fetchRequest = event.request.clone();
return fetch(fetchRequest).then(
function(response) {
if (!response || response.status !== 200) {
return response;
}
var responseToCache = response.clone();
caches.open('v1').then(function(cache) {
cache.put(event.request, responseToCache);
});
return response;
}
);
})
);
});
3 . Fetch and Cache requests
Service Workers (Cache API)
44+40+
https://jakearchibald.github.io/isserviceworkerready/
27+
Future of upcoming web development ?
Web storage (local / session)
if (('localStorage' in window) && window['localStorage'] !== null) {
localStorage.setItem(key, value);
}
if (key in localStorage) {
var value = localStorage.getItem(key);
}
1. Store data
2. Retrieve data
if (key in localStorage) {
localStorage.removeItem(key);
}
localStorage.clear();
3. Remove data / clear
Web SQL
var db = openDatabase('mydb', '1.0', 'Test DB', 2 * 1024 * 1024);
var msg;
db.transaction(function (tx) {
tx.executeSql('CREATE TABLE IF NOT EXISTS LOGS (id unique, log)');
tx.executeSql('INSERT INTO LOGS (id, log) VALUES (1, "foobar")');
tx.executeSql('INSERT INTO LOGS (id, log) VALUES (2, "logmsg")');
msg = '<p>Log message created and row inserted.</p>';
document.querySelector('#status').innerHTML = msg;
});
db.transaction(function (tx) {
tx.executeSql('SELECT * FROM LOGS', [], function (tx, results) {
var len = results.rows.length, i;
msg = "<p>Found rows: " + len + "</p>";
document.querySelector('#status').innerHTML += msg;
for (i = 0; i < len; i++) {
msg = "<p><b>" + results.rows.item(i).log + "</b></p>";
document.querySelector('#status').innerHTML += msg;
}
}, null);
});
var db = openDatabase('mydb', '1.0', 'Test DB', 2 * 1024 * 1024);
var msg;
db.transaction(function (tx) {
tx.executeSql('CREATE TABLE IF NOT EXISTS LOGS (id unique, log)');
tx.executeSql('INSERT INTO LOGS (id, log) VALUES (1, "foobar")');
tx.executeSql('INSERT INTO LOGS (id, log) VALUES (2, "logmsg")');
msg = '<p>Log message created and row inserted.</p>';
document.querySelector('#status').innerHTML = msg;
});
db.transaction(function (tx) {
tx.executeSql('SELECT * FROM LOGS', [], function (tx, results) {
var len = results.rows.length, i;
msg = "<p>Found rows: " + len + "</p>";
document.querySelector('#status').innerHTML += msg;
for (i = 0; i < len; i++) {
msg = "<p><b>" + results.rows.item(i).log + "</b></p>";
document.querySelector('#status').innerHTML += msg;
}
}, null);
});
Web SQL
function onInitFs(fs) {
fs.root.getFile('log.txt', {}, function(fileEntry) {
// Get a File object representing the file,
// then use FileReader to read its contents.
fileEntry.file(function(file) {
var reader = new FileReader();
reader.onloadend = function(e) {
var txtArea = document.createElement('textarea');
txtArea.value = this.result;
document.body.appendChild(txtArea);
};
reader.readAsText(file);
}, errorHandler);
}, errorHandler);
}
window.requestFileSystem(window.TEMPORARY, 1024*1024, onInitFs, errorHandler);
FileSystem API
function onInitFs(fs) {
fs.root.getFile('log.txt', {}, function(fileEntry) {
// Get a File object representing the file,
// then use FileReader to read its contents.
fileEntry.file(function(file) {
var reader = new FileReader();
reader.onloadend = function(e) {
var txtArea = document.createElement('textarea');
txtArea.value = this.result;
document.body.appendChild(txtArea);
};
reader.readAsText(file);
}, errorHandler);
}, errorHandler);
}
window.requestFileSystem(window.TEMPORARY, 1024*1024, onInitFs, errorHandler);
FileSystem API
IndexedDB
var db;
function openDb() {
var req = indexedDB.open(DB_NAME, DB_VERSION);
req.onsuccess = function (evt) {
db = this.result;
};
req.onerror = function (evt) {
console.error("openDb:", evt.target.errorCode);
};
req.onupgradeneeded = function (evt) {
var store = evt.currentTarget.result.createObjectStore(
DB_STORE_NAME, { keyPath: 'id', autoIncrement: true });
store.createIndex('title', 'title', { unique: false });
store.createIndex('isbn', 'isbn', { unique: true });
};
}
1. Open Database
IndexedDB
var tx = db.transaction(DB_STORE_NAME, 'readwrite');
var store = tx.objectStore(DB_STORE_NAME);
var obj = { isbn: ‘0062316095’, title: ‘Sapiens: A Brief History of Humankind’, year: 2015 };
var req;
try {
req = store.add(obj);
} catch (e) {
// ...
}
req.onsuccess = function (evt) {
console.log("Insertion in DB successful");
// ...
};
req.onerror = function() {
console.error("Insert error", this.error);
// ...
};
2. Insert data
IndexedDB
var var tx = db.transaction(DB_STORE_NAME, 'readonly');
var store = tx.objectStore(DB_STORE_NAME);
var req = store.openCursor();
req.onsuccess = function (evt) {
var cursor = evt.target.result;
if (cursor) {
alert(cursor.value.title);
cursor.continue();
}
};
3. Retrieve data (cursor)
var var tx = db.transaction(DB_STORE_NAME, 'readonly');
var store = tx.objectStore(DB_STORE_NAME);
var req = store.get(42);
req.onsuccess = function (evt) {
var object = evt.target.result;
alert(object.title);
};
3. Retrieve data (one item)
IndexedDB
var var tx = db.transaction(DB_STORE_NAME, 'readonly');
var store = tx.objectStore(DB_STORE_NAME);
var index = store.index(‘title’);
var req = index.get(‘Sapiens: A Brief History of Humankind’);
req.onsuccess = function (evt) {
var result = evt.target.result;
if (result) {
// ...
}
};
3. Retrieve data (index)
IndexedDB wrappers
• db.js
• joqular
• TaffyDB
• localForage
• IDBWrapper
• YDN
IndexedDB
16+24+ 15+ 10+
8+ 4.4+
Google Gears
HTML 5 Storage Limitations
Quotas
50 %
33 %
20
%
20
%
Free disk space
Space browser can use
Space application (domain) can use
Quotas
Users
https://storage.spec.whatwg.org/
https://developers.google.com/web/updates/2016/06/persistent-storage
if (navigator.storage && navigator.storage.persist)
navigator.storage.persist().then(granted => {
if (granted)
alert("Storage will not be cleared except by explicit user action");
else
alert("Storage may be cleared by the UA under storage pressure.");
});
if (navigator.storage && navigator.storage.persist)
navigator.storage.persisted().then(persistent=>{
if (persistent)
console.log("Storage will not be cleared except by explicit user action");
else
console.log("Storage may be cleared by the UA under storage pressure.");
});
Persistent storage
55+
Question #3
How to handle offline-online
synchronization ?
CONFLICTS
Basic Resolution : based on timestamp
« Last version win »
Optimistic lock
Source : Patterns of Enterprise Application Architecture - Martin Fowler
System transaction
boundaries
Business transaction
boundaries
Pessimistic lock
Source : Patterns of Enterprise Application Architecture - Martin Fowler
System transaction
boundaries
Business transaction
boundaries
Theory is when you
know everything but
nothing works.
Practice is when
everything works but no
one knows why.
In our lab, theory and
practice are combined:
nothing works and no
one knows why!
kinto.js
var db = new Kinto();
var todos = db.collection(‘todos’);
todos.create({
title: ‘buy some bread’),
finished : false
})
.then(function(res){…})
.catch(function(err){…})
todos.list().then(function(res) {
renderTodos(res.data);
})
.catch(function(err) {…});
todos.update(todo)
.then(function(res) {…})
.catch(function(err) {…});
Create, Read, Update, Delete
using IndexedDB
todos.delete(todo.id)
.then(function(res) {…})
.catch(function(err) {…});
var syncOptions = {
remote: "https://host/kintoapi",
headers: {Authorization: …}
};
todos.sync(syncOptions)
.then(function(res){…})
.catch(function(err){…})
Synchronize with remote
kinto.js
var syncOptions = {
remote: "https://host/kintoapi",
headers: {Authorization: …}
};
todos.sync(syncOptions)
.then(function(res){…})
.catch(function(err){…})
{
"ok": true,
"lastModified": 1434617181458,
"errors": [],
"created": [], // created locally
"updated": [], // updated locally
"deleted": [], // deleted locally
"published": [ // published remotely
{
"last_modified": 1434617181458,
"done": false,
"id": "7ca54d89-479a-4201-8494",
"title": "buy some bread",
"_status": "synced"
}
],
"conflicts": [],
"skipped": []
}
{
"ok": true,
"lastModified": 1434617181458,
"errors": [],
"created": [], // created locally
"updated": [], // updated locally
"deleted": [], // deleted locally
"published": [], // published remotely
"conflicts": [
{
"type": "incoming", // or outgoing
"local": {
"last_modified": 1434619634577,
"done": true,
"id": "7ca54d89-479a-4201-8494",
"title": "buy some bread",
"_status": "updated"
},
"remote": {
"last_modified": 1434619745465,
"done": false,
"id": "7ca54d89-479a-4201-8494",
"title": "buy some bread and wine"
}
}
],
"skipped": []
}
OK Conflicts
kinto.js
{
"ok": true,
"lastModified": 1434617181458,
"errors": [],
"created": [], // created locally
"updated": [], // updated locally
"deleted": [], // deleted locally
"published": [], // published remotely
"conflicts": [
{
"type": "incoming", // or outgoing
"local": {
"last_modified": 1434619634577,
"done": true,
"id": "7ca54d89-479a-4201-8494",
"title": "buy some bread",
"_status": "updated"
},
"remote": {
"last_modified": 1434619745465,
"done": false,
"id": "7ca54d89-479a-4201-8494",
"title": "buy some bread and wine"
}
}
],
"skipped": []
}
Conflicts
todos.sync(syncOptions)
.then(function(res){
if (res.conflicts.length) {
return handleConflicts(res.conflicts);
}
})
.catch(function(err){…});
function handleConflicts(conflicts) {
return Promise.all(conflicts.map(function(conflict) {
return todos.resolve(conflict, conflict.remote);
}))
.then(function() {
todos.sync(syncOptions);
});
}
Choose your way to solve the conflict:
• Choose remote or local version
• Choose according last_modified
• Pick the good fields
(need to provide 3-way-merge screen)
var db = new PouchDB(‘todos’);
db.post({ // can use ‘put’ with an _id
title: ‘buy some bread’),
finished : false
})
.then(function(res){…})
.catch(function(err){…})
db.get(‘mysuperid’).then(function(todo) {
// return an object with auto
// generated ‘_rev’ field
// update the full doc (with _rev)
todo.finished = true;
db.put(todo);
// remove the full doc (with _rev)
db.remove(todo);
})
.catch(function(err) {…});
Create, Read, Update, Delete
using IndexedDB
var localDB = new PouchDB(‘todos’);
// Remote CouchDB
var remoteDB
= new PouchDB(‘http://host/todos’);
localDB.replicate.to(remoteDB);
localDB.replicate.from(remoteDB);
// or
localDB.sync(remoteDB, {
live: true,
retry: true
}).on('change', function (change) {
// something changed!
}).on('paused', function (info) {
// replication was paused,
// usually because of a lost connection
}).on('active', function (info) {
// replication was resumed
}).on('error', function (err) {
// unhandled error (shouldn't happen)
});
Synchronize with remote
var myDoc = {
_id: 'someid',
_rev: '1-somerev'
};
db.put(myDoc).then(function () {
// success
}).catch(function (err) {
if (err.name === 'conflict') {
// conflict! Handle it!
} else {
// some other error
}
});
Immediate conflict : error 409
_rev: ‘1-revabc’ _rev: ‘1-revabc’
_rev: ‘2-revcde’ _rev: ‘2-revjkl’
_rev: ‘1-revabc’
_rev: ‘2-revjkl’
_rev: ‘2-revcde’
db.get('someid', {conflicts: true})
.then(function (doc) {
// do something with the object
}).catch(function (err) {
// handle any errors
});
{
"_id": "someid",
"_rev": "2-revjkl",
"_conflicts": ["2-revcde"]
}
==>
Eventual conflict
==> remove
the bad one,
merge, …
it’s up to you
Question #4
How to communicate with users ?
Inform the user …
Save Save locally
Send Send when online
… or not
Outbox (1)Send
Do no display errors !
Do not load indefinitelyyyyyyyyyy
Do not display
an empty screen
Handling conflicts
Question #5
Do I really need offline ?
(2001) (2009) (2020)
« You are not on a f*cking plane and if
you are, it doesn’t matter »
- David Heinemeier Hansson (2007)
https://signalvnoise.com/posts/347-youre-not-on-a-fucking-plane-and-if-you-are-it-doesnt-matter
ATAWAD
Unfortunately
NOT
AnyWhere !
User Experience
matters !
Thank you
Bibliography
• http://diveintohtml5.info/offline.html
• https://github.com/pazguille/offline-first
• https://jakearchibald.com/2014/offline-cookbook/
• https://github.com/offlinefirst/research/blob/master/links.md
• http://www.html5rocks.com/en/tutorials/offline/whats-offline/
• http://offlinefirst.org/
• http://fr.slideshare.net/MarcelKalveram/offline-first-the-painless-way
• https://developer.mozilla.org/en-US/Apps/Fundamentals/Offline
• https://uxdesign.cc/offline-93c2f8396124#.97njk8o5m
• https://www.ibm.com/developerworks/community/blogs/worklight/entry/
offline_patterns?lang=en
• http://apress.jensimmons.com/v5/pro-html5-programming/ch12.html
• http://alistapart.com/article/offline-first
• http://alistapart.com/article/application-cache-is-a-douchebag
• https://logbook.hanno.co/offline-first-matters-developers-know/
• https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API/
Using_Service_Workers
• https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API
• https://developer.chrome.com/apps/offline_storage
• http://martinfowler.com/eaaCatalog/index.html
• http://offlinestat.es/
• http://caniuse.com/
Jake Archibald

Contenu connexe

Tendances

Advanced Hibernate Notes
Advanced Hibernate NotesAdvanced Hibernate Notes
Advanced Hibernate Notes
Kaniska Mandal
 
Indexing & Query Optimization
Indexing & Query OptimizationIndexing & Query Optimization
Indexing & Query Optimization
MongoDB
 
Java Persistence Frameworks for MongoDB
Java Persistence Frameworks for MongoDBJava Persistence Frameworks for MongoDB
Java Persistence Frameworks for MongoDB
MongoDB
 
Symfony Day 2010 Doctrine MongoDB ODM
Symfony Day 2010 Doctrine MongoDB ODMSymfony Day 2010 Doctrine MongoDB ODM
Symfony Day 2010 Doctrine MongoDB ODM
Jonathan Wage
 
Indexing and Query Optimization
Indexing and Query OptimizationIndexing and Query Optimization
Indexing and Query Optimization
MongoDB
 

Tendances (20)

Advanced Hibernate Notes
Advanced Hibernate NotesAdvanced Hibernate Notes
Advanced Hibernate Notes
 
Data access 2.0? Please welcome: Spring Data!
Data access 2.0? Please welcome: Spring Data!Data access 2.0? Please welcome: Spring Data!
Data access 2.0? Please welcome: Spring Data!
 
Indexing & Query Optimization
Indexing & Query OptimizationIndexing & Query Optimization
Indexing & Query Optimization
 
Symfony2 from the Trenches
Symfony2 from the TrenchesSymfony2 from the Trenches
Symfony2 from the Trenches
 
Java Persistence Frameworks for MongoDB
Java Persistence Frameworks for MongoDBJava Persistence Frameworks for MongoDB
Java Persistence Frameworks for MongoDB
 
Symfony Day 2010 Doctrine MongoDB ODM
Symfony Day 2010 Doctrine MongoDB ODMSymfony Day 2010 Doctrine MongoDB ODM
Symfony Day 2010 Doctrine MongoDB ODM
 
Doctrine MongoDB Object Document Mapper
Doctrine MongoDB Object Document MapperDoctrine MongoDB Object Document Mapper
Doctrine MongoDB Object Document Mapper
 
Sequelize
SequelizeSequelize
Sequelize
 
Ajax chap 4
Ajax chap 4Ajax chap 4
Ajax chap 4
 
Indexing and Query Optimization
Indexing and Query OptimizationIndexing and Query Optimization
Indexing and Query Optimization
 
MongoDB + Java - Everything you need to know
MongoDB + Java - Everything you need to know MongoDB + Java - Everything you need to know
MongoDB + Java - Everything you need to know
 
Mongo db for C# Developers
Mongo db for C# DevelopersMongo db for C# Developers
Mongo db for C# Developers
 
MongoDB Performance Tuning
MongoDB Performance TuningMongoDB Performance Tuning
MongoDB Performance Tuning
 
Persistences
PersistencesPersistences
Persistences
 
MySQL flexible schema and JSON for Internet of Things
MySQL flexible schema and JSON for Internet of ThingsMySQL flexible schema and JSON for Internet of Things
MySQL flexible schema and JSON for Internet of Things
 
Ajax chap 5
Ajax chap 5Ajax chap 5
Ajax chap 5
 
Mongo db for c# developers
Mongo db for c# developersMongo db for c# developers
Mongo db for c# developers
 
Saving Data
Saving DataSaving Data
Saving Data
 
Android Data Persistence
Android Data PersistenceAndroid Data Persistence
Android Data Persistence
 
greenDAO
greenDAOgreenDAO
greenDAO
 

En vedette

324.ayuda a las personas de la tercera edad
324.ayuda a las personas de la tercera edad324.ayuda a las personas de la tercera edad
324.ayuda a las personas de la tercera edad
dec-admin
 
Bert Savoie Resume
Bert Savoie ResumeBert Savoie Resume
Bert Savoie Resume
Bert Savoie
 
Escuela amado nervo
Escuela amado nervoEscuela amado nervo
Escuela amado nervo
dec-admin
 
96. alimentacion sustentable
96. alimentacion sustentable96. alimentacion sustentable
96. alimentacion sustentable
dec-admin
 
220.todos contra el bullyng
220.todos contra el bullyng220.todos contra el bullyng
220.todos contra el bullyng
dec-admin
 
163.regio activo
163.regio activo163.regio activo
163.regio activo
dec-admin
 

En vedette (18)

Home project sc
Home project scHome project sc
Home project sc
 
324.ayuda a las personas de la tercera edad
324.ayuda a las personas de la tercera edad324.ayuda a las personas de la tercera edad
324.ayuda a las personas de la tercera edad
 
Bert Savoie Resume
Bert Savoie ResumeBert Savoie Resume
Bert Savoie Resume
 
Escuela amado nervo
Escuela amado nervoEscuela amado nervo
Escuela amado nervo
 
Garrtech investment has the answer to hollywood real estate
Garrtech investment has the answer to hollywood real estateGarrtech investment has the answer to hollywood real estate
Garrtech investment has the answer to hollywood real estate
 
96. alimentacion sustentable
96. alimentacion sustentable96. alimentacion sustentable
96. alimentacion sustentable
 
Just one ... Myles Sullivan's paintings
Just one ... Myles Sullivan's paintingsJust one ... Myles Sullivan's paintings
Just one ... Myles Sullivan's paintings
 
Volume 24.2
Volume 24.2Volume 24.2
Volume 24.2
 
The housing market.irkutsk.02.11.2016
The housing market.irkutsk.02.11.2016The housing market.irkutsk.02.11.2016
The housing market.irkutsk.02.11.2016
 
Learn to Drift
Learn to DriftLearn to Drift
Learn to Drift
 
Habilidades del Pensamiento en las TIC
Habilidades del Pensamiento en las TICHabilidades del Pensamiento en las TIC
Habilidades del Pensamiento en las TIC
 
220.todos contra el bullyng
220.todos contra el bullyng220.todos contra el bullyng
220.todos contra el bullyng
 
Atias Parasitología Médica Cap 3 - El Hospedero
Atias Parasitología Médica Cap 3 - El HospederoAtias Parasitología Médica Cap 3 - El Hospedero
Atias Parasitología Médica Cap 3 - El Hospedero
 
Creencias religiosas en los jóvenes y niños
Creencias religiosas en los jóvenes y niñosCreencias religiosas en los jóvenes y niños
Creencias religiosas en los jóvenes y niños
 
Bases de una educación para la paz.
Bases de una educación para la paz.Bases de una educación para la paz.
Bases de una educación para la paz.
 
Aire Libre
Aire LibreAire Libre
Aire Libre
 
Presentation NWA BIZ Con Event
Presentation NWA BIZ Con EventPresentation NWA BIZ Con Event
Presentation NWA BIZ Con Event
 
163.regio activo
163.regio activo163.regio activo
163.regio activo
 

Similaire à Softshake - Offline applications

The Magic Revealed: Four Real-World Examples of Using the Client Object Model...
The Magic Revealed: Four Real-World Examples of Using the Client Object Model...The Magic Revealed: Four Real-World Examples of Using the Client Object Model...
The Magic Revealed: Four Real-World Examples of Using the Client Object Model...
SPTechCon
 
Taking Web Apps Offline
Taking Web Apps OfflineTaking Web Apps Offline
Taking Web Apps Offline
Pedro Morais
 
Developing your first application using FI-WARE
Developing your first application using FI-WAREDeveloping your first application using FI-WARE
Developing your first application using FI-WARE
Fermin Galan
 
Boost Your Environment With XMLDB - UKOUG 2008 - Marco Gralike
Boost Your Environment With XMLDB - UKOUG 2008 - Marco GralikeBoost Your Environment With XMLDB - UKOUG 2008 - Marco Gralike
Boost Your Environment With XMLDB - UKOUG 2008 - Marco Gralike
Marco Gralike
 
Intro to HTML5 Web Storage
Intro to HTML5 Web StorageIntro to HTML5 Web Storage
Intro to HTML5 Web Storage
dylanks
 
Jquery dojo slides
Jquery dojo slidesJquery dojo slides
Jquery dojo slides
helenmga
 

Similaire à Softshake - Offline applications (20)

Local data storage for mobile apps
Local data storage for mobile appsLocal data storage for mobile apps
Local data storage for mobile apps
 
Local Storage
Local StorageLocal Storage
Local Storage
 
React Native Course - Data Storage . pdf
React Native Course - Data Storage . pdfReact Native Course - Data Storage . pdf
React Native Course - Data Storage . pdf
 
The Magic Revealed: Four Real-World Examples of Using the Client Object Model...
The Magic Revealed: Four Real-World Examples of Using the Client Object Model...The Magic Revealed: Four Real-World Examples of Using the Client Object Model...
The Magic Revealed: Four Real-World Examples of Using the Client Object Model...
 
Android Data Storagefinal
Android Data StoragefinalAndroid Data Storagefinal
Android Data Storagefinal
 
Taking Web Apps Offline
Taking Web Apps OfflineTaking Web Apps Offline
Taking Web Apps Offline
 
Local storage in Web apps
Local storage in Web appsLocal storage in Web apps
Local storage in Web apps
 
Developing your first application using FI-WARE
Developing your first application using FI-WAREDeveloping your first application using FI-WARE
Developing your first application using FI-WARE
 
Boost Your Environment With XMLDB - UKOUG 2008 - Marco Gralike
Boost Your Environment With XMLDB - UKOUG 2008 - Marco GralikeBoost Your Environment With XMLDB - UKOUG 2008 - Marco Gralike
Boost Your Environment With XMLDB - UKOUG 2008 - Marco Gralike
 
Fun Teaching MongoDB New Tricks
Fun Teaching MongoDB New TricksFun Teaching MongoDB New Tricks
Fun Teaching MongoDB New Tricks
 
Intro to HTML5 Web Storage
Intro to HTML5 Web StorageIntro to HTML5 Web Storage
Intro to HTML5 Web Storage
 
Tornadoweb
TornadowebTornadoweb
Tornadoweb
 
Hidden pearls for High-Performance-Persistence
Hidden pearls for High-Performance-PersistenceHidden pearls for High-Performance-Persistence
Hidden pearls for High-Performance-Persistence
 
Jquery dojo slides
Jquery dojo slidesJquery dojo slides
Jquery dojo slides
 
Global objects in Node.pdf
Global objects in Node.pdfGlobal objects in Node.pdf
Global objects in Node.pdf
 
Cloudbase.io MoSync Reload Course
Cloudbase.io MoSync Reload CourseCloudbase.io MoSync Reload Course
Cloudbase.io MoSync Reload Course
 
Saving Time And Effort With QuickBase Api - Sergio Haro
Saving Time And Effort With QuickBase Api - Sergio HaroSaving Time And Effort With QuickBase Api - Sergio Haro
Saving Time And Effort With QuickBase Api - Sergio Haro
 
[Srijan Wednesday Webinars] Ruling Drupal 8 with #d8rules
[Srijan Wednesday Webinars] Ruling Drupal 8 with #d8rules[Srijan Wednesday Webinars] Ruling Drupal 8 with #d8rules
[Srijan Wednesday Webinars] Ruling Drupal 8 with #d8rules
 
Paris js extensions
Paris js extensionsParis js extensions
Paris js extensions
 
Devoxx08 - Nuxeo Core, JCR 2, CMIS
Devoxx08 - Nuxeo Core, JCR 2, CMIS Devoxx08 - Nuxeo Core, JCR 2, CMIS
Devoxx08 - Nuxeo Core, JCR 2, CMIS
 

Plus de jeromevdl

Devoxx France 2013 : Musclez vos apps android avec les outils du monde java
Devoxx France 2013 : Musclez vos apps android avec les outils du monde javaDevoxx France 2013 : Musclez vos apps android avec les outils du monde java
Devoxx France 2013 : Musclez vos apps android avec les outils du monde java
jeromevdl
 

Plus de jeromevdl (13)

Message-Driven Architecture on AWS
Message-Driven Architecture on AWSMessage-Driven Architecture on AWS
Message-Driven Architecture on AWS
 
Do more with less code in serverless
Do more with less code in serverlessDo more with less code in serverless
Do more with less code in serverless
 
Do more with less code in a serverless world
Do more with less code in a serverless worldDo more with less code in a serverless world
Do more with less code in a serverless world
 
DevopsDays Geneva 2020 - Compliance & Governance as Code
DevopsDays Geneva 2020 - Compliance & Governance as CodeDevopsDays Geneva 2020 - Compliance & Governance as Code
DevopsDays Geneva 2020 - Compliance & Governance as Code
 
Softshake 2017 - Développer un chatbot Alexa
Softshake 2017 - Développer un chatbot AlexaSoftshake 2017 - Développer un chatbot Alexa
Softshake 2017 - Développer un chatbot Alexa
 
Chatbots buzzword ou nouvel eldorado
Chatbots   buzzword ou nouvel eldoradoChatbots   buzzword ou nouvel eldorado
Chatbots buzzword ou nouvel eldorado
 
Management projet vs management produit
Management projet vs management produitManagement projet vs management produit
Management projet vs management produit
 
My Android is not an iPhone like any others (Mdevcon 2014)
My Android is not an iPhone like any others (Mdevcon 2014)My Android is not an iPhone like any others (Mdevcon 2014)
My Android is not an iPhone like any others (Mdevcon 2014)
 
DroidconUK 2013 : Beef up android apps with java tools
DroidconUK 2013 : Beef up android apps with java toolsDroidconUK 2013 : Beef up android apps with java tools
DroidconUK 2013 : Beef up android apps with java tools
 
Droidcon Paris 2013 - Musclez vos applications Android avec les outils du mon...
Droidcon Paris 2013 - Musclez vos applications Android avec les outils du mon...Droidcon Paris 2013 - Musclez vos applications Android avec les outils du mon...
Droidcon Paris 2013 - Musclez vos applications Android avec les outils du mon...
 
Devoxx France 2013 : Musclez vos apps android avec les outils du monde java
Devoxx France 2013 : Musclez vos apps android avec les outils du monde javaDevoxx France 2013 : Musclez vos apps android avec les outils du monde java
Devoxx France 2013 : Musclez vos apps android avec les outils du monde java
 
Jug Lausanne Android Janvier2013
Jug Lausanne Android Janvier2013Jug Lausanne Android Janvier2013
Jug Lausanne Android Janvier2013
 
Metroide
MetroideMetroide
Metroide
 

Dernier

AKTU Computer Networks notes --- Unit 3.pdf
AKTU Computer Networks notes ---  Unit 3.pdfAKTU Computer Networks notes ---  Unit 3.pdf
AKTU Computer Networks notes --- Unit 3.pdf
ankushspencer015
 
FULL ENJOY Call Girls In Mahipalpur Delhi Contact Us 8377877756
FULL ENJOY Call Girls In Mahipalpur Delhi Contact Us 8377877756FULL ENJOY Call Girls In Mahipalpur Delhi Contact Us 8377877756
FULL ENJOY Call Girls In Mahipalpur Delhi Contact Us 8377877756
dollysharma2066
 
Call for Papers - African Journal of Biological Sciences, E-ISSN: 2663-2187, ...
Call for Papers - African Journal of Biological Sciences, E-ISSN: 2663-2187, ...Call for Papers - African Journal of Biological Sciences, E-ISSN: 2663-2187, ...
Call for Papers - African Journal of Biological Sciences, E-ISSN: 2663-2187, ...
Christo Ananth
 
Call Now ≽ 9953056974 ≼🔝 Call Girls In New Ashok Nagar ≼🔝 Delhi door step de...
Call Now ≽ 9953056974 ≼🔝 Call Girls In New Ashok Nagar  ≼🔝 Delhi door step de...Call Now ≽ 9953056974 ≼🔝 Call Girls In New Ashok Nagar  ≼🔝 Delhi door step de...
Call Now ≽ 9953056974 ≼🔝 Call Girls In New Ashok Nagar ≼🔝 Delhi door step de...
9953056974 Low Rate Call Girls In Saket, Delhi NCR
 
VIP Call Girls Palanpur 7001035870 Whatsapp Number, 24/07 Booking
VIP Call Girls Palanpur 7001035870 Whatsapp Number, 24/07 BookingVIP Call Girls Palanpur 7001035870 Whatsapp Number, 24/07 Booking
VIP Call Girls Palanpur 7001035870 Whatsapp Number, 24/07 Booking
dharasingh5698
 
Call Girls in Ramesh Nagar Delhi 💯 Call Us 🔝9953056974 🔝 Escort Service
Call Girls in Ramesh Nagar Delhi 💯 Call Us 🔝9953056974 🔝 Escort ServiceCall Girls in Ramesh Nagar Delhi 💯 Call Us 🔝9953056974 🔝 Escort Service
Call Girls in Ramesh Nagar Delhi 💯 Call Us 🔝9953056974 🔝 Escort Service
9953056974 Low Rate Call Girls In Saket, Delhi NCR
 

Dernier (20)

Booking open Available Pune Call Girls Pargaon 6297143586 Call Hot Indian Gi...
Booking open Available Pune Call Girls Pargaon  6297143586 Call Hot Indian Gi...Booking open Available Pune Call Girls Pargaon  6297143586 Call Hot Indian Gi...
Booking open Available Pune Call Girls Pargaon 6297143586 Call Hot Indian Gi...
 
Call Girls Walvekar Nagar Call Me 7737669865 Budget Friendly No Advance Booking
Call Girls Walvekar Nagar Call Me 7737669865 Budget Friendly No Advance BookingCall Girls Walvekar Nagar Call Me 7737669865 Budget Friendly No Advance Booking
Call Girls Walvekar Nagar Call Me 7737669865 Budget Friendly No Advance Booking
 
PVC VS. FIBERGLASS (FRP) GRAVITY SEWER - UNI BELL
PVC VS. FIBERGLASS (FRP) GRAVITY SEWER - UNI BELLPVC VS. FIBERGLASS (FRP) GRAVITY SEWER - UNI BELL
PVC VS. FIBERGLASS (FRP) GRAVITY SEWER - UNI BELL
 
Thermal Engineering Unit - I & II . ppt
Thermal Engineering  Unit - I & II . pptThermal Engineering  Unit - I & II . ppt
Thermal Engineering Unit - I & II . ppt
 
AKTU Computer Networks notes --- Unit 3.pdf
AKTU Computer Networks notes ---  Unit 3.pdfAKTU Computer Networks notes ---  Unit 3.pdf
AKTU Computer Networks notes --- Unit 3.pdf
 
FULL ENJOY Call Girls In Mahipalpur Delhi Contact Us 8377877756
FULL ENJOY Call Girls In Mahipalpur Delhi Contact Us 8377877756FULL ENJOY Call Girls In Mahipalpur Delhi Contact Us 8377877756
FULL ENJOY Call Girls In Mahipalpur Delhi Contact Us 8377877756
 
Roadmap to Membership of RICS - Pathways and Routes
Roadmap to Membership of RICS - Pathways and RoutesRoadmap to Membership of RICS - Pathways and Routes
Roadmap to Membership of RICS - Pathways and Routes
 
Intze Overhead Water Tank Design by Working Stress - IS Method.pdf
Intze Overhead Water Tank  Design by Working Stress - IS Method.pdfIntze Overhead Water Tank  Design by Working Stress - IS Method.pdf
Intze Overhead Water Tank Design by Working Stress - IS Method.pdf
 
Top Rated Pune Call Girls Budhwar Peth ⟟ 6297143586 ⟟ Call Me For Genuine Se...
Top Rated  Pune Call Girls Budhwar Peth ⟟ 6297143586 ⟟ Call Me For Genuine Se...Top Rated  Pune Call Girls Budhwar Peth ⟟ 6297143586 ⟟ Call Me For Genuine Se...
Top Rated Pune Call Girls Budhwar Peth ⟟ 6297143586 ⟟ Call Me For Genuine Se...
 
Unleashing the Power of the SORA AI lastest leap
Unleashing the Power of the SORA AI lastest leapUnleashing the Power of the SORA AI lastest leap
Unleashing the Power of the SORA AI lastest leap
 
Booking open Available Pune Call Girls Koregaon Park 6297143586 Call Hot Ind...
Booking open Available Pune Call Girls Koregaon Park  6297143586 Call Hot Ind...Booking open Available Pune Call Girls Koregaon Park  6297143586 Call Hot Ind...
Booking open Available Pune Call Girls Koregaon Park 6297143586 Call Hot Ind...
 
CCS335 _ Neural Networks and Deep Learning Laboratory_Lab Complete Record
CCS335 _ Neural Networks and Deep Learning Laboratory_Lab Complete RecordCCS335 _ Neural Networks and Deep Learning Laboratory_Lab Complete Record
CCS335 _ Neural Networks and Deep Learning Laboratory_Lab Complete Record
 
Call for Papers - African Journal of Biological Sciences, E-ISSN: 2663-2187, ...
Call for Papers - African Journal of Biological Sciences, E-ISSN: 2663-2187, ...Call for Papers - African Journal of Biological Sciences, E-ISSN: 2663-2187, ...
Call for Papers - African Journal of Biological Sciences, E-ISSN: 2663-2187, ...
 
Call Now ≽ 9953056974 ≼🔝 Call Girls In New Ashok Nagar ≼🔝 Delhi door step de...
Call Now ≽ 9953056974 ≼🔝 Call Girls In New Ashok Nagar  ≼🔝 Delhi door step de...Call Now ≽ 9953056974 ≼🔝 Call Girls In New Ashok Nagar  ≼🔝 Delhi door step de...
Call Now ≽ 9953056974 ≼🔝 Call Girls In New Ashok Nagar ≼🔝 Delhi door step de...
 
Thermal Engineering -unit - III & IV.ppt
Thermal Engineering -unit - III & IV.pptThermal Engineering -unit - III & IV.ppt
Thermal Engineering -unit - III & IV.ppt
 
Java Programming :Event Handling(Types of Events)
Java Programming :Event Handling(Types of Events)Java Programming :Event Handling(Types of Events)
Java Programming :Event Handling(Types of Events)
 
VIP Call Girls Palanpur 7001035870 Whatsapp Number, 24/07 Booking
VIP Call Girls Palanpur 7001035870 Whatsapp Number, 24/07 BookingVIP Call Girls Palanpur 7001035870 Whatsapp Number, 24/07 Booking
VIP Call Girls Palanpur 7001035870 Whatsapp Number, 24/07 Booking
 
Call Girls in Ramesh Nagar Delhi 💯 Call Us 🔝9953056974 🔝 Escort Service
Call Girls in Ramesh Nagar Delhi 💯 Call Us 🔝9953056974 🔝 Escort ServiceCall Girls in Ramesh Nagar Delhi 💯 Call Us 🔝9953056974 🔝 Escort Service
Call Girls in Ramesh Nagar Delhi 💯 Call Us 🔝9953056974 🔝 Escort Service
 
Double Revolving field theory-how the rotor develops torque
Double Revolving field theory-how the rotor develops torqueDouble Revolving field theory-how the rotor develops torque
Double Revolving field theory-how the rotor develops torque
 
(INDIRA) Call Girl Bhosari Call Now 8617697112 Bhosari Escorts 24x7
(INDIRA) Call Girl Bhosari Call Now 8617697112 Bhosari Escorts 24x7(INDIRA) Call Girl Bhosari Call Now 8617697112 Bhosari Escorts 24x7
(INDIRA) Call Girl Bhosari Call Now 8617697112 Bhosari Escorts 24x7
 

Softshake - Offline applications

  • 1. Offline applications Jérôme Van Der Linden - 28/10/2016
  • 7. Offline applications 5 questions to ask before creating an offline application
  • 8. Question #1 What can I do offline ?
  • 14. Question #2 How much data is it and where can i store it ?
  • 17. Several gigabytes (or many more) ?
  • 19. Application Cache <html manifest="/cache.manifest"> ... </html> CACHE MANIFEST # Explicitly cached CACHE: /favicon.ico page.html stylesheet.css images/logo.png scripts/main.js http://cdn.example.com/scripts/main.js # Resources that require the user to be online. NETWORK: * # static.html will be served if main.py is inaccessible # offline.jpg will be served in place of all images in images/large/ FALLBACK: /main.py /static.html images/large/ images/offline.jpg cache.manifest index.html http://www.html5rocks.com/en/tutorials/appcache/beginner/ http://alistapart.com/article/application-cache-is-a-douchebag
  • 20. Application Cache <html manifest="/cache.manifest"> ... </html> CACHE MANIFEST # Explicitly cached CACHE: /favicon.ico page.html stylesheet.css images/logo.png scripts/main.js http://cdn.example.com/scripts/main.js # Resources that require the user to be online. NETWORK: * # static.html will be served if main.py is inaccessible # offline.jpg will be served in place of all images in images/large/ FALLBACK: /main.py /static.html images/large/ images/offline.jpg cache.manifest index.html http://www.html5rocks.com/en/tutorials/appcache/beginner/ http://alistapart.com/article/application-cache-is-a-douchebag
  • 21. Service Workers (Cache API) this.addEventListener('install', function(event) { event.waitUntil( caches.open('v1').then(function(cache) { return cache.addAll([ '/sw-test/', '/sw-test/index.html', '/sw-test/style.css', '/sw-test/app.js', '/sw-test/star-wars-logo.jpg', '/sw-test/gallery/', '/sw-test/gallery/myLittleVader.jpg' ]); }) ); }); 2. Installation of Service Worker if ('serviceWorker' in navigator) { navigator.serviceWorker.register(‘/sw.js') .then(function(registration) { // Registration was successful }).catch(function(err) { // registration failed :( }); } 1. Registration of Service Worker self.addEventListener('fetch', function(event) { event.respondWith( caches.match(event.request) .then(function(response) { // Cache hit - return response if (response) { return response; } var fetchRequest = event.request.clone(); return fetch(fetchRequest).then( function(response) { if (!response || response.status !== 200) { return response; } var responseToCache = response.clone(); caches.open('v1').then(function(cache) { cache.put(event.request, responseToCache); }); return response; } ); }) ); }); 3 . Fetch and Cache requests
  • 22. Service Workers (Cache API) 44+40+ https://jakearchibald.github.io/isserviceworkerready/ 27+
  • 23. Future of upcoming web development ?
  • 24. Web storage (local / session) if (('localStorage' in window) && window['localStorage'] !== null) { localStorage.setItem(key, value); } if (key in localStorage) { var value = localStorage.getItem(key); } 1. Store data 2. Retrieve data if (key in localStorage) { localStorage.removeItem(key); } localStorage.clear(); 3. Remove data / clear
  • 25. Web SQL var db = openDatabase('mydb', '1.0', 'Test DB', 2 * 1024 * 1024); var msg; db.transaction(function (tx) { tx.executeSql('CREATE TABLE IF NOT EXISTS LOGS (id unique, log)'); tx.executeSql('INSERT INTO LOGS (id, log) VALUES (1, "foobar")'); tx.executeSql('INSERT INTO LOGS (id, log) VALUES (2, "logmsg")'); msg = '<p>Log message created and row inserted.</p>'; document.querySelector('#status').innerHTML = msg; }); db.transaction(function (tx) { tx.executeSql('SELECT * FROM LOGS', [], function (tx, results) { var len = results.rows.length, i; msg = "<p>Found rows: " + len + "</p>"; document.querySelector('#status').innerHTML += msg; for (i = 0; i < len; i++) { msg = "<p><b>" + results.rows.item(i).log + "</b></p>"; document.querySelector('#status').innerHTML += msg; } }, null); });
  • 26. var db = openDatabase('mydb', '1.0', 'Test DB', 2 * 1024 * 1024); var msg; db.transaction(function (tx) { tx.executeSql('CREATE TABLE IF NOT EXISTS LOGS (id unique, log)'); tx.executeSql('INSERT INTO LOGS (id, log) VALUES (1, "foobar")'); tx.executeSql('INSERT INTO LOGS (id, log) VALUES (2, "logmsg")'); msg = '<p>Log message created and row inserted.</p>'; document.querySelector('#status').innerHTML = msg; }); db.transaction(function (tx) { tx.executeSql('SELECT * FROM LOGS', [], function (tx, results) { var len = results.rows.length, i; msg = "<p>Found rows: " + len + "</p>"; document.querySelector('#status').innerHTML += msg; for (i = 0; i < len; i++) { msg = "<p><b>" + results.rows.item(i).log + "</b></p>"; document.querySelector('#status').innerHTML += msg; } }, null); }); Web SQL
  • 27. function onInitFs(fs) { fs.root.getFile('log.txt', {}, function(fileEntry) { // Get a File object representing the file, // then use FileReader to read its contents. fileEntry.file(function(file) { var reader = new FileReader(); reader.onloadend = function(e) { var txtArea = document.createElement('textarea'); txtArea.value = this.result; document.body.appendChild(txtArea); }; reader.readAsText(file); }, errorHandler); }, errorHandler); } window.requestFileSystem(window.TEMPORARY, 1024*1024, onInitFs, errorHandler); FileSystem API
  • 28. function onInitFs(fs) { fs.root.getFile('log.txt', {}, function(fileEntry) { // Get a File object representing the file, // then use FileReader to read its contents. fileEntry.file(function(file) { var reader = new FileReader(); reader.onloadend = function(e) { var txtArea = document.createElement('textarea'); txtArea.value = this.result; document.body.appendChild(txtArea); }; reader.readAsText(file); }, errorHandler); }, errorHandler); } window.requestFileSystem(window.TEMPORARY, 1024*1024, onInitFs, errorHandler); FileSystem API
  • 29. IndexedDB var db; function openDb() { var req = indexedDB.open(DB_NAME, DB_VERSION); req.onsuccess = function (evt) { db = this.result; }; req.onerror = function (evt) { console.error("openDb:", evt.target.errorCode); }; req.onupgradeneeded = function (evt) { var store = evt.currentTarget.result.createObjectStore( DB_STORE_NAME, { keyPath: 'id', autoIncrement: true }); store.createIndex('title', 'title', { unique: false }); store.createIndex('isbn', 'isbn', { unique: true }); }; } 1. Open Database
  • 30. IndexedDB var tx = db.transaction(DB_STORE_NAME, 'readwrite'); var store = tx.objectStore(DB_STORE_NAME); var obj = { isbn: ‘0062316095’, title: ‘Sapiens: A Brief History of Humankind’, year: 2015 }; var req; try { req = store.add(obj); } catch (e) { // ... } req.onsuccess = function (evt) { console.log("Insertion in DB successful"); // ... }; req.onerror = function() { console.error("Insert error", this.error); // ... }; 2. Insert data
  • 31. IndexedDB var var tx = db.transaction(DB_STORE_NAME, 'readonly'); var store = tx.objectStore(DB_STORE_NAME); var req = store.openCursor(); req.onsuccess = function (evt) { var cursor = evt.target.result; if (cursor) { alert(cursor.value.title); cursor.continue(); } }; 3. Retrieve data (cursor) var var tx = db.transaction(DB_STORE_NAME, 'readonly'); var store = tx.objectStore(DB_STORE_NAME); var req = store.get(42); req.onsuccess = function (evt) { var object = evt.target.result; alert(object.title); }; 3. Retrieve data (one item)
  • 32. IndexedDB var var tx = db.transaction(DB_STORE_NAME, 'readonly'); var store = tx.objectStore(DB_STORE_NAME); var index = store.index(‘title’); var req = index.get(‘Sapiens: A Brief History of Humankind’); req.onsuccess = function (evt) { var result = evt.target.result; if (result) { // ... } }; 3. Retrieve data (index)
  • 33. IndexedDB wrappers • db.js • joqular • TaffyDB • localForage • IDBWrapper • YDN
  • 36. HTML 5 Storage Limitations
  • 37. Quotas 50 % 33 % 20 % 20 % Free disk space Space browser can use Space application (domain) can use
  • 39. Users
  • 40. https://storage.spec.whatwg.org/ https://developers.google.com/web/updates/2016/06/persistent-storage if (navigator.storage && navigator.storage.persist) navigator.storage.persist().then(granted => { if (granted) alert("Storage will not be cleared except by explicit user action"); else alert("Storage may be cleared by the UA under storage pressure."); }); if (navigator.storage && navigator.storage.persist) navigator.storage.persisted().then(persistent=>{ if (persistent) console.log("Storage will not be cleared except by explicit user action"); else console.log("Storage may be cleared by the UA under storage pressure."); }); Persistent storage 55+
  • 41. Question #3 How to handle offline-online synchronization ?
  • 43. Basic Resolution : based on timestamp « Last version win »
  • 44. Optimistic lock Source : Patterns of Enterprise Application Architecture - Martin Fowler System transaction boundaries Business transaction boundaries
  • 45. Pessimistic lock Source : Patterns of Enterprise Application Architecture - Martin Fowler System transaction boundaries Business transaction boundaries
  • 46. Theory is when you know everything but nothing works. Practice is when everything works but no one knows why. In our lab, theory and practice are combined: nothing works and no one knows why!
  • 47.
  • 48. kinto.js var db = new Kinto(); var todos = db.collection(‘todos’); todos.create({ title: ‘buy some bread’), finished : false }) .then(function(res){…}) .catch(function(err){…}) todos.list().then(function(res) { renderTodos(res.data); }) .catch(function(err) {…}); todos.update(todo) .then(function(res) {…}) .catch(function(err) {…}); Create, Read, Update, Delete using IndexedDB todos.delete(todo.id) .then(function(res) {…}) .catch(function(err) {…}); var syncOptions = { remote: "https://host/kintoapi", headers: {Authorization: …} }; todos.sync(syncOptions) .then(function(res){…}) .catch(function(err){…}) Synchronize with remote
  • 49. kinto.js var syncOptions = { remote: "https://host/kintoapi", headers: {Authorization: …} }; todos.sync(syncOptions) .then(function(res){…}) .catch(function(err){…}) { "ok": true, "lastModified": 1434617181458, "errors": [], "created": [], // created locally "updated": [], // updated locally "deleted": [], // deleted locally "published": [ // published remotely { "last_modified": 1434617181458, "done": false, "id": "7ca54d89-479a-4201-8494", "title": "buy some bread", "_status": "synced" } ], "conflicts": [], "skipped": [] } { "ok": true, "lastModified": 1434617181458, "errors": [], "created": [], // created locally "updated": [], // updated locally "deleted": [], // deleted locally "published": [], // published remotely "conflicts": [ { "type": "incoming", // or outgoing "local": { "last_modified": 1434619634577, "done": true, "id": "7ca54d89-479a-4201-8494", "title": "buy some bread", "_status": "updated" }, "remote": { "last_modified": 1434619745465, "done": false, "id": "7ca54d89-479a-4201-8494", "title": "buy some bread and wine" } } ], "skipped": [] } OK Conflicts
  • 50. kinto.js { "ok": true, "lastModified": 1434617181458, "errors": [], "created": [], // created locally "updated": [], // updated locally "deleted": [], // deleted locally "published": [], // published remotely "conflicts": [ { "type": "incoming", // or outgoing "local": { "last_modified": 1434619634577, "done": true, "id": "7ca54d89-479a-4201-8494", "title": "buy some bread", "_status": "updated" }, "remote": { "last_modified": 1434619745465, "done": false, "id": "7ca54d89-479a-4201-8494", "title": "buy some bread and wine" } } ], "skipped": [] } Conflicts todos.sync(syncOptions) .then(function(res){ if (res.conflicts.length) { return handleConflicts(res.conflicts); } }) .catch(function(err){…}); function handleConflicts(conflicts) { return Promise.all(conflicts.map(function(conflict) { return todos.resolve(conflict, conflict.remote); })) .then(function() { todos.sync(syncOptions); }); } Choose your way to solve the conflict: • Choose remote or local version • Choose according last_modified • Pick the good fields (need to provide 3-way-merge screen)
  • 51. var db = new PouchDB(‘todos’); db.post({ // can use ‘put’ with an _id title: ‘buy some bread’), finished : false }) .then(function(res){…}) .catch(function(err){…}) db.get(‘mysuperid’).then(function(todo) { // return an object with auto // generated ‘_rev’ field // update the full doc (with _rev) todo.finished = true; db.put(todo); // remove the full doc (with _rev) db.remove(todo); }) .catch(function(err) {…}); Create, Read, Update, Delete using IndexedDB var localDB = new PouchDB(‘todos’); // Remote CouchDB var remoteDB = new PouchDB(‘http://host/todos’); localDB.replicate.to(remoteDB); localDB.replicate.from(remoteDB); // or localDB.sync(remoteDB, { live: true, retry: true }).on('change', function (change) { // something changed! }).on('paused', function (info) { // replication was paused, // usually because of a lost connection }).on('active', function (info) { // replication was resumed }).on('error', function (err) { // unhandled error (shouldn't happen) }); Synchronize with remote
  • 52. var myDoc = { _id: 'someid', _rev: '1-somerev' }; db.put(myDoc).then(function () { // success }).catch(function (err) { if (err.name === 'conflict') { // conflict! Handle it! } else { // some other error } }); Immediate conflict : error 409 _rev: ‘1-revabc’ _rev: ‘1-revabc’ _rev: ‘2-revcde’ _rev: ‘2-revjkl’ _rev: ‘1-revabc’ _rev: ‘2-revjkl’ _rev: ‘2-revcde’ db.get('someid', {conflicts: true}) .then(function (doc) { // do something with the object }).catch(function (err) { // handle any errors }); { "_id": "someid", "_rev": "2-revjkl", "_conflicts": ["2-revcde"] } ==> Eventual conflict ==> remove the bad one, merge, … it’s up to you
  • 53.
  • 54. Question #4 How to communicate with users ?
  • 55. Inform the user … Save Save locally Send Send when online
  • 56. … or not Outbox (1)Send
  • 57. Do no display errors !
  • 58. Do not load indefinitelyyyyyyyyyy
  • 59. Do not display an empty screen
  • 60.
  • 62. Question #5 Do I really need offline ?
  • 64.
  • 65. « You are not on a f*cking plane and if you are, it doesn’t matter » - David Heinemeier Hansson (2007) https://signalvnoise.com/posts/347-youre-not-on-a-fucking-plane-and-if-you-are-it-doesnt-matter
  • 69. Bibliography • http://diveintohtml5.info/offline.html • https://github.com/pazguille/offline-first • https://jakearchibald.com/2014/offline-cookbook/ • https://github.com/offlinefirst/research/blob/master/links.md • http://www.html5rocks.com/en/tutorials/offline/whats-offline/ • http://offlinefirst.org/ • http://fr.slideshare.net/MarcelKalveram/offline-first-the-painless-way • https://developer.mozilla.org/en-US/Apps/Fundamentals/Offline • https://uxdesign.cc/offline-93c2f8396124#.97njk8o5m • https://www.ibm.com/developerworks/community/blogs/worklight/entry/ offline_patterns?lang=en • http://apress.jensimmons.com/v5/pro-html5-programming/ch12.html • http://alistapart.com/article/offline-first • http://alistapart.com/article/application-cache-is-a-douchebag • https://logbook.hanno.co/offline-first-matters-developers-know/ • https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API/ Using_Service_Workers • https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API • https://developer.chrome.com/apps/offline_storage • http://martinfowler.com/eaaCatalog/index.html • http://offlinestat.es/ • http://caniuse.com/ Jake Archibald