SlideShare a Scribd company logo
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

More Related Content

What's hot

Advanced Hibernate Notes
Advanced Hibernate NotesAdvanced Hibernate Notes
Advanced Hibernate Notes
Kaniska Mandal
 
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!
Oliver Gierke
 
Indexing & Query Optimization
Indexing & Query OptimizationIndexing & Query Optimization
Indexing & Query Optimization
MongoDB
 
Symfony2 from the Trenches
Symfony2 from the TrenchesSymfony2 from the Trenches
Symfony2 from the Trenches
Jonathan Wage
 
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
 
Doctrine MongoDB Object Document Mapper
Doctrine MongoDB Object Document MapperDoctrine MongoDB Object Document Mapper
Doctrine MongoDB Object Document Mapper
Jonathan Wage
 
Sequelize
SequelizeSequelize
Sequelize
Tarek Raihan
 
Ajax chap 4
Ajax chap 4Ajax chap 4
Ajax chap 4
Mukesh Tekwani
 
Indexing and Query Optimization
Indexing and Query OptimizationIndexing and Query Optimization
Indexing and Query Optimization
MongoDB
 
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
Norberto Leite
 
Mongo db for C# Developers
Mongo db for C# DevelopersMongo db for C# Developers
Mongo db for C# Developers
Simon Elliston Ball
 
MongoDB Performance Tuning
MongoDB Performance TuningMongoDB Performance Tuning
MongoDB Performance Tuning
Puneet Behl
 
Persistences
PersistencesPersistences
Persistences
Training Guide
 
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
Alexander Rubin
 
Ajax chap 5
Ajax chap 5Ajax chap 5
Ajax chap 5
Mukesh Tekwani
 
Mongo db for c# developers
Mongo db for c# developersMongo db for c# developers
Mongo db for c# developers
Simon Elliston Ball
 
Saving Data
Saving DataSaving Data
Saving Data
SV.CO
 
Android Data Persistence
Android Data PersistenceAndroid Data Persistence
Android Data Persistence
Romain Rochegude
 
greenDAO
greenDAOgreenDAO
greenDAO
Mu Chun Wang
 

What's hot (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
 

Viewers also liked

Home project sc
Home project scHome project sc
Home project sc
sarah Colsenet
 
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
 
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
Brandon Jones
 
96. alimentacion sustentable
96. alimentacion sustentable96. alimentacion sustentable
96. alimentacion sustentable
dec-admin
 
Just one ... Myles Sullivan's paintings
Just one ... Myles Sullivan's paintingsJust one ... Myles Sullivan's paintings
Just one ... Myles Sullivan's paintings
Makala (D)
 
Volume 24.2
Volume 24.2Volume 24.2
Volume 24.2
aderm
 
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
drift101
 
Habilidades del Pensamiento en las TIC
Habilidades del Pensamiento en las TICHabilidades del Pensamiento en las TIC
Habilidades del Pensamiento en las TIC
Liliana Angel
 
220.todos contra el bullyng
220.todos contra el bullyng220.todos contra el bullyng
220.todos contra el bullyng
dec-admin
 
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
neljirsh
 
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
Ronald'o Cardenas
 
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.
felipe0410
 
Aire Libre
Aire LibreAire Libre
Presentation NWA BIZ Con Event
Presentation NWA BIZ Con EventPresentation NWA BIZ Con Event
Presentation NWA BIZ Con Event
Tom Gazaway
 
163.regio activo
163.regio activo163.regio activo
163.regio activo
dec-admin
 

Viewers also liked (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
 

Similar to Softshake - Offline applications

Local data storage for mobile apps
Local data storage for mobile appsLocal data storage for mobile apps
Local data storage for mobile apps
Ivano Malavolta
 
Local Storage
Local StorageLocal Storage
Local Storage
Ivano Malavolta
 
React Native Course - Data Storage . pdf
React Native Course - Data Storage . pdfReact Native Course - Data Storage . pdf
React Native Course - Data Storage . pdf
AlvianZachryFaturrah
 
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
 
Android Data Storagefinal
Android Data StoragefinalAndroid Data Storagefinal
Android Data Storagefinal
Nakka Srilakshmi
 
Taking Web Apps Offline
Taking Web Apps OfflineTaking Web Apps Offline
Taking Web Apps Offline
Pedro Morais
 
Local storage in Web apps
Local storage in Web appsLocal storage in Web apps
Local storage in Web apps
Ivano Malavolta
 
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
 
Fun Teaching MongoDB New Tricks
Fun Teaching MongoDB New TricksFun Teaching MongoDB New Tricks
Fun Teaching MongoDB New Tricks
MongoDB
 
Intro to HTML5 Web Storage
Intro to HTML5 Web StorageIntro to HTML5 Web Storage
Intro to HTML5 Web Storage
dylanks
 
Tornadoweb
TornadowebTornadoweb
Tornadoweb
Osman Yuksel
 
Hidden pearls for High-Performance-Persistence
Hidden pearls for High-Performance-PersistenceHidden pearls for High-Performance-Persistence
Hidden pearls for High-Performance-Persistence
Sven Ruppert
 
Jquery dojo slides
Jquery dojo slidesJquery dojo slides
Jquery dojo slides
helenmga
 
Global objects in Node.pdf
Global objects in Node.pdfGlobal objects in Node.pdf
Global objects in Node.pdf
SudhanshiBakre1
 
Cloudbase.io MoSync Reload Course
Cloudbase.io MoSync Reload CourseCloudbase.io MoSync Reload Course
Cloudbase.io MoSync Reload Course
cloudbase.io
 
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
QuickBase, Inc.
 
[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
Srijan Technologies
 
Paris js extensions
Paris js extensionsParis js extensions
Paris js extensions
erwanl
 
Devoxx08 - Nuxeo Core, JCR 2, CMIS
Devoxx08 - Nuxeo Core, JCR 2, CMIS Devoxx08 - Nuxeo Core, JCR 2, CMIS
Devoxx08 - Nuxeo Core, JCR 2, CMIS
Nuxeo
 

Similar to 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
 

More from jeromevdl

Message-Driven Architecture on AWS
Message-Driven Architecture on AWSMessage-Driven Architecture on AWS
Message-Driven Architecture on AWS
jeromevdl
 
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
jeromevdl
 
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
jeromevdl
 
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
jeromevdl
 
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
jeromevdl
 
Chatbots buzzword ou nouvel eldorado
Chatbots   buzzword ou nouvel eldoradoChatbots   buzzword ou nouvel eldorado
Chatbots buzzword ou nouvel eldorado
jeromevdl
 
Management projet vs management produit
Management projet vs management produitManagement projet vs management produit
Management projet vs management produit
jeromevdl
 
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)
jeromevdl
 
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
jeromevdl
 
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...
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
 
Jug Lausanne Android Janvier2013
Jug Lausanne Android Janvier2013Jug Lausanne Android Janvier2013
Jug Lausanne Android Janvier2013
jeromevdl
 
Metroide
MetroideMetroide
Metroide
jeromevdl
 

More from 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
 

Recently uploaded

DEEP LEARNING FOR SMART GRID INTRUSION DETECTION: A HYBRID CNN-LSTM-BASED MODEL
DEEP LEARNING FOR SMART GRID INTRUSION DETECTION: A HYBRID CNN-LSTM-BASED MODELDEEP LEARNING FOR SMART GRID INTRUSION DETECTION: A HYBRID CNN-LSTM-BASED MODEL
DEEP LEARNING FOR SMART GRID INTRUSION DETECTION: A HYBRID CNN-LSTM-BASED MODEL
ijaia
 
2008 BUILDING CONSTRUCTION Illustrated - Ching Chapter 02 The Building.pdf
2008 BUILDING CONSTRUCTION Illustrated - Ching Chapter 02 The Building.pdf2008 BUILDING CONSTRUCTION Illustrated - Ching Chapter 02 The Building.pdf
2008 BUILDING CONSTRUCTION Illustrated - Ching Chapter 02 The Building.pdf
Yasser Mahgoub
 
Null Bangalore | Pentesters Approach to AWS IAM
Null Bangalore | Pentesters Approach to AWS IAMNull Bangalore | Pentesters Approach to AWS IAM
Null Bangalore | Pentesters Approach to AWS IAM
Divyanshu
 
Electric vehicle and photovoltaic advanced roles in enhancing the financial p...
Electric vehicle and photovoltaic advanced roles in enhancing the financial p...Electric vehicle and photovoltaic advanced roles in enhancing the financial p...
Electric vehicle and photovoltaic advanced roles in enhancing the financial p...
IJECEIAES
 
一比一原版(CalArts毕业证)加利福尼亚艺术学院毕业证如何办理
一比一原版(CalArts毕业证)加利福尼亚艺术学院毕业证如何办理一比一原版(CalArts毕业证)加利福尼亚艺术学院毕业证如何办理
一比一原版(CalArts毕业证)加利福尼亚艺术学院毕业证如何办理
ecqow
 
Design and optimization of ion propulsion drone
Design and optimization of ion propulsion droneDesign and optimization of ion propulsion drone
Design and optimization of ion propulsion drone
bjmsejournal
 
Software Engineering and Project Management - Software Testing + Agile Method...
Software Engineering and Project Management - Software Testing + Agile Method...Software Engineering and Project Management - Software Testing + Agile Method...
Software Engineering and Project Management - Software Testing + Agile Method...
Prakhyath Rai
 
Prediction of Electrical Energy Efficiency Using Information on Consumer's Ac...
Prediction of Electrical Energy Efficiency Using Information on Consumer's Ac...Prediction of Electrical Energy Efficiency Using Information on Consumer's Ac...
Prediction of Electrical Energy Efficiency Using Information on Consumer's Ac...
PriyankaKilaniya
 
2008 BUILDING CONSTRUCTION Illustrated - Ching Chapter 08 Doors and Windows.pdf
2008 BUILDING CONSTRUCTION Illustrated - Ching Chapter 08 Doors and Windows.pdf2008 BUILDING CONSTRUCTION Illustrated - Ching Chapter 08 Doors and Windows.pdf
2008 BUILDING CONSTRUCTION Illustrated - Ching Chapter 08 Doors and Windows.pdf
Yasser Mahgoub
 
Engineering Standards Wiring methods.pdf
Engineering Standards Wiring methods.pdfEngineering Standards Wiring methods.pdf
Engineering Standards Wiring methods.pdf
edwin408357
 
Computational Engineering IITH Presentation
Computational Engineering IITH PresentationComputational Engineering IITH Presentation
Computational Engineering IITH Presentation
co23btech11018
 
Properties Railway Sleepers and Test.pptx
Properties Railway Sleepers and Test.pptxProperties Railway Sleepers and Test.pptx
Properties Railway Sleepers and Test.pptx
MDSABBIROJJAMANPAYEL
 
Optimizing Gradle Builds - Gradle DPE Tour Berlin 2024
Optimizing Gradle Builds - Gradle DPE Tour Berlin 2024Optimizing Gradle Builds - Gradle DPE Tour Berlin 2024
Optimizing Gradle Builds - Gradle DPE Tour Berlin 2024
Sinan KOZAK
 
4. Mosca vol I -Fisica-Tipler-5ta-Edicion-Vol-1.pdf
4. Mosca vol I -Fisica-Tipler-5ta-Edicion-Vol-1.pdf4. Mosca vol I -Fisica-Tipler-5ta-Edicion-Vol-1.pdf
4. Mosca vol I -Fisica-Tipler-5ta-Edicion-Vol-1.pdf
Gino153088
 
morris_worm_intro_and_source_code_analysis_.pdf
morris_worm_intro_and_source_code_analysis_.pdfmorris_worm_intro_and_source_code_analysis_.pdf
morris_worm_intro_and_source_code_analysis_.pdf
ycwu0509
 
Gas agency management system project report.pdf
Gas agency management system project report.pdfGas agency management system project report.pdf
Gas agency management system project report.pdf
Kamal Acharya
 
CEC 352 - SATELLITE COMMUNICATION UNIT 1
CEC 352 - SATELLITE COMMUNICATION UNIT 1CEC 352 - SATELLITE COMMUNICATION UNIT 1
CEC 352 - SATELLITE COMMUNICATION UNIT 1
PKavitha10
 
Mechanical Engineering on AAI Summer Training Report-003.pdf
Mechanical Engineering on AAI Summer Training Report-003.pdfMechanical Engineering on AAI Summer Training Report-003.pdf
Mechanical Engineering on AAI Summer Training Report-003.pdf
21UME003TUSHARDEB
 
学校原版美国波士顿大学毕业证学历学位证书原版一模一样
学校原版美国波士顿大学毕业证学历学位证书原版一模一样学校原版美国波士顿大学毕业证学历学位证书原版一模一样
学校原版美国波士顿大学毕业证学历学位证书原版一模一样
171ticu
 
Embedded machine learning-based road conditions and driving behavior monitoring
Embedded machine learning-based road conditions and driving behavior monitoringEmbedded machine learning-based road conditions and driving behavior monitoring
Embedded machine learning-based road conditions and driving behavior monitoring
IJECEIAES
 

Recently uploaded (20)

DEEP LEARNING FOR SMART GRID INTRUSION DETECTION: A HYBRID CNN-LSTM-BASED MODEL
DEEP LEARNING FOR SMART GRID INTRUSION DETECTION: A HYBRID CNN-LSTM-BASED MODELDEEP LEARNING FOR SMART GRID INTRUSION DETECTION: A HYBRID CNN-LSTM-BASED MODEL
DEEP LEARNING FOR SMART GRID INTRUSION DETECTION: A HYBRID CNN-LSTM-BASED MODEL
 
2008 BUILDING CONSTRUCTION Illustrated - Ching Chapter 02 The Building.pdf
2008 BUILDING CONSTRUCTION Illustrated - Ching Chapter 02 The Building.pdf2008 BUILDING CONSTRUCTION Illustrated - Ching Chapter 02 The Building.pdf
2008 BUILDING CONSTRUCTION Illustrated - Ching Chapter 02 The Building.pdf
 
Null Bangalore | Pentesters Approach to AWS IAM
Null Bangalore | Pentesters Approach to AWS IAMNull Bangalore | Pentesters Approach to AWS IAM
Null Bangalore | Pentesters Approach to AWS IAM
 
Electric vehicle and photovoltaic advanced roles in enhancing the financial p...
Electric vehicle and photovoltaic advanced roles in enhancing the financial p...Electric vehicle and photovoltaic advanced roles in enhancing the financial p...
Electric vehicle and photovoltaic advanced roles in enhancing the financial p...
 
一比一原版(CalArts毕业证)加利福尼亚艺术学院毕业证如何办理
一比一原版(CalArts毕业证)加利福尼亚艺术学院毕业证如何办理一比一原版(CalArts毕业证)加利福尼亚艺术学院毕业证如何办理
一比一原版(CalArts毕业证)加利福尼亚艺术学院毕业证如何办理
 
Design and optimization of ion propulsion drone
Design and optimization of ion propulsion droneDesign and optimization of ion propulsion drone
Design and optimization of ion propulsion drone
 
Software Engineering and Project Management - Software Testing + Agile Method...
Software Engineering and Project Management - Software Testing + Agile Method...Software Engineering and Project Management - Software Testing + Agile Method...
Software Engineering and Project Management - Software Testing + Agile Method...
 
Prediction of Electrical Energy Efficiency Using Information on Consumer's Ac...
Prediction of Electrical Energy Efficiency Using Information on Consumer's Ac...Prediction of Electrical Energy Efficiency Using Information on Consumer's Ac...
Prediction of Electrical Energy Efficiency Using Information on Consumer's Ac...
 
2008 BUILDING CONSTRUCTION Illustrated - Ching Chapter 08 Doors and Windows.pdf
2008 BUILDING CONSTRUCTION Illustrated - Ching Chapter 08 Doors and Windows.pdf2008 BUILDING CONSTRUCTION Illustrated - Ching Chapter 08 Doors and Windows.pdf
2008 BUILDING CONSTRUCTION Illustrated - Ching Chapter 08 Doors and Windows.pdf
 
Engineering Standards Wiring methods.pdf
Engineering Standards Wiring methods.pdfEngineering Standards Wiring methods.pdf
Engineering Standards Wiring methods.pdf
 
Computational Engineering IITH Presentation
Computational Engineering IITH PresentationComputational Engineering IITH Presentation
Computational Engineering IITH Presentation
 
Properties Railway Sleepers and Test.pptx
Properties Railway Sleepers and Test.pptxProperties Railway Sleepers and Test.pptx
Properties Railway Sleepers and Test.pptx
 
Optimizing Gradle Builds - Gradle DPE Tour Berlin 2024
Optimizing Gradle Builds - Gradle DPE Tour Berlin 2024Optimizing Gradle Builds - Gradle DPE Tour Berlin 2024
Optimizing Gradle Builds - Gradle DPE Tour Berlin 2024
 
4. Mosca vol I -Fisica-Tipler-5ta-Edicion-Vol-1.pdf
4. Mosca vol I -Fisica-Tipler-5ta-Edicion-Vol-1.pdf4. Mosca vol I -Fisica-Tipler-5ta-Edicion-Vol-1.pdf
4. Mosca vol I -Fisica-Tipler-5ta-Edicion-Vol-1.pdf
 
morris_worm_intro_and_source_code_analysis_.pdf
morris_worm_intro_and_source_code_analysis_.pdfmorris_worm_intro_and_source_code_analysis_.pdf
morris_worm_intro_and_source_code_analysis_.pdf
 
Gas agency management system project report.pdf
Gas agency management system project report.pdfGas agency management system project report.pdf
Gas agency management system project report.pdf
 
CEC 352 - SATELLITE COMMUNICATION UNIT 1
CEC 352 - SATELLITE COMMUNICATION UNIT 1CEC 352 - SATELLITE COMMUNICATION UNIT 1
CEC 352 - SATELLITE COMMUNICATION UNIT 1
 
Mechanical Engineering on AAI Summer Training Report-003.pdf
Mechanical Engineering on AAI Summer Training Report-003.pdfMechanical Engineering on AAI Summer Training Report-003.pdf
Mechanical Engineering on AAI Summer Training Report-003.pdf
 
学校原版美国波士顿大学毕业证学历学位证书原版一模一样
学校原版美国波士顿大学毕业证学历学位证书原版一模一样学校原版美国波士顿大学毕业证学历学位证书原版一模一样
学校原版美国波士顿大学毕业证学历学位证书原版一模一样
 
Embedded machine learning-based road conditions and driving behavior monitoring
Embedded machine learning-based road conditions and driving behavior monitoringEmbedded machine learning-based road conditions and driving behavior monitoring
Embedded machine learning-based road conditions and driving behavior monitoring
 

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