SLIDE 1 Offline First
@caolan
SLIDE 2
SLIDE 3
SLIDE 4 Unlike the always-wired machines of the past, computers are now truly personal, and people move through
- nline and offline seamlessly
SLIDE 5
…our apps should do the same
SLIDE 6
SLIDE 7
SLIDE 8 “More often than not, the mobile experience for a Web application or site is designed and built after the PC version is complete. Here's three reasons why Web applications should be designed for mobile first instead.”
SLIDE 10
- 1. Mobile is exploding
- 2. Mobile forces you to focus
SLIDE 11
- 1. Mobile is exploding
- 2. Mobile forces you to focus
- 3. Mobile extends your capabilities
SLIDE 12
SLIDE 13
SLIDE 14
SLIDE 15
SLIDE 16 > Offline First Meetup #1, Berlin
SLIDE 17
“When travelling, I take screenshots of important messages”
SLIDE 18
“before the release, you turn on flight mode on and check if the app crashes…”
SLIDE 19
“If it doesn’t, you consider the app 'offline-ready' ...this is not enough”
SLIDE 20
Offline is not an error
SLIDE 21
It's a legitimate use-case that isn't going away soon
SLIDE 22
T E C H N O L O G Y
SLIDE 23
- 1. Delivering the application
- 2. Detecting connectivity
- 3. Storing data
- 4. Syncing data
SLIDE 24
- 1. Delivering the application
- 2. Detecting connectivity
- 3. Storing data
- 4. Syncing data
SLIDE 25 <html manifest=”example.appcache”> ... </html>
SLIDE 26 CACHE MANIFEST # 2010-06-18:v2 # Explicitly cached 'master entries'. CACHE: /favicon.ico index.html stylesheet.css images/logo.png 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/ # offline.html will be served in place of all other .html files FALLBACK: /main.py /static.html images/large/ images/offline.jpg
SLIDE 27
- 1. The Application Cache will only update if the
contents of the manifest file have changed
SLIDE 28
- 1. The Application Cache will only update if the
contents of the manifest file have changed
- 2. It always serves from the cache, even when
- nline (watch out for manifest renames)
SLIDE 29
- 1. The Application Cache will only update if the
contents of the manifest file have changed
- 2. It always serves from the cache, even when
- nline (watch out for manifest renames)
- 3. Non-cached files will not load on a cached
page unless explicitly listed
SLIDE 30
- 1. The Application Cache will only update if the
contents of the manifest file have changed
- 2. It always serves from the cache, even when
- nline (watch out for manifest renames)
- 3. Non-cached files will not load on a cached
page unless explicitly listed
- 4. User sees new content on next visit
(requires double refresh)
SLIDE 31
Service Worker
SLIDE 32 <html> <head> <script> navigator.serviceWorker.register("worker.js"); </script> </head> ... </html>
SLIDE 33 // worker.js this.addEventListener("fetch", function (e) { if (e.request.url == “/data.json”) { e.respondWith( new Response({statusCode: 200, body: …}) ); } });
SLIDE 34 this.addEventListener("install", function (e) { // Create a cache of resources and fetch them. var resources = new Cache( “/app.html”, “/data.json” ); // Wait until all resources are ready. e.waitUntil(resources.ready()); // Set cache so we can use during onfetch caches.set("v1", resources); });
SLIDE 35 this.addEventListener("fetch", function (e) { // No "onfetch" events are dispatched to the // ServiceWorker until it successfully installs. e.respondWith(caches.match(e.request)); });
SLIDE 36 HTTP + Cache Browser Page
SLIDE 37 HTTP + Cache Browser Page AppCache Declarative only, no direct programmatic access
SLIDE 38 HTTP + Cache Browser Page Sits between your page and the browser's network stack Service Worker
SLIDE 39 HTTP + Cache Browser Page It can intercept, modify and respond to network requests Service Worker
SLIDE 40 HTTP + Cache Browser Page Cache Programmatic access to a set of durable caches Service Worker
SLIDE 41 (Diagram totally stolen from @phuunet)
SLIDE 42
- 1. Delivering the application
- 2. Detecting connectivity
- 3. Storing data
- 4. Syncing data
SLIDE 43 if (navigator.onLine) { alert('online'); }
SLIDE 44 window.addEventListener("offline", ...); window.addEventListener("online", ...);
SLIDE 45 In Chrome and Safari, if the Browser is not able to connect to a local area network (LAN)
- r a router, it is offline.
SLIDE 46
In Firefox and Internet Explorer, switching the browser to offline mode sends a false value. All other conditions return true.
SLIDE 47 var appcache = window.applicationCache; appcache.addEventListener("error", function (e) { // probably offline });
SLIDE 48 xhr.status === 0 xhr.readyState === 0 xhr.addEventListener('error', onDown, false); xhr.addEventListener('timeout', onDown, false);
SLIDE 49
- 1. Delivering the application
- 2. Detecting connectivity
- 3. Storing data
- 4. Syncing data
SLIDE 50
LocalStorage
SLIDE 51 // The values we want to store offline. var users = [ {id: 1, fullName: 'Matt'}, {id: 2, fullName: 'Bob'} ]; // Let's save it for the next time we load the app. localStorage.setItem('users', JSON.stringify(users)); // The next time we load the app, we can do: var users = JSON.parse(localStorage.getItem('users'));
SLIDE 53
- 1. It's dead simple
- 2. It's well supported by browsers
SLIDE 54
SLIDE 55
- 1. It's synchronous (blocks UI)
SLIDE 56
- 1. It's synchronous (blocks UI)
- 2. Only strings, no Blobs
SLIDE 57
- 1. It's synchronous (blocks UI)
- 2. Only strings, no Blobs
- 3. No clean way to detect reaching
the storage limit (~5mb)
SLIDE 58
IndexedDB
SLIDE 59 var db; var dbName = "dataspace"; var users = [ {id: 1, fullName: 'Matt'}, {id: 2, fullName: 'Bob'} ]; var request = indexedDB.open(dbName, 2); request.onerror = function (event) { // Handle errors. }; request.onupgradeneeded = function (event) { db = event.target.result; var objectStore = db.createObjectStore("users", { keyPath: "id" });
- bjectStore.createIndex("fullName", "fullName", { unique: false });
- bjectStore.transaction.oncomplete = function (event) {
var userObjectStore = db.transaction("users", "readwrite").objectStore("users"); } }; // Once the database is created, let's add our user to it... var transaction = db.transaction(["users"], "readwrite"); // Do something when all the data is added to the database. transaction.oncomplete = function (event) { console.log("All done!"); }; transaction.onerror = function (event) { // Don't forget to handle errors! }; var objectStore = transaction.objectStore("users"); for (var i in users) { var request = objectStore.add(users[i]); request.onsuccess = function (event) { // Contains our user info. console.log(event.target.result); }; }
SLIDE 61
- 1. Asynchronous
- 2. Transactions
SLIDE 62
- 1. Asynchronous
- 2. Transactions
- 3. No need to serialize/deserialize
SLIDE 63
- 1. Asynchronous
- 2. Transactions
- 3. No need to serialize/deserialize
- 4. Indexes
SLIDE 64
- 1. Asynchronous
- 2. Transactions
- 3. No need to serialize/deserialize
- 4. Indexes
- 5. Higher storage limits
(browser usually asks >50mb)
SLIDE 66
- 1. More complicated API
- 2. Supported by fewer browsers
SLIDE 67
SLIDE 68
Wrappers
SLIDE 69
SLIDE 70 // The values we want to store offline. var users = [ {id: 1, fullName: 'Matt'}, {id: 2, fullName: 'Bob'} ]; // save the values localForage.setItem('users', users, function (result) { console.log(result); });
SLIDE 71
- 1. Delivering the application
- 2. Detecting connectivity
- 3. Storing data
- 4. Syncing data
SLIDE 72
SLIDE 73
SLIDE 74
Offline by default
SLIDE 75 App hoodie.store localStorage
Hoodie Sync
SLIDE 76 App hoodie.store CouchDB Plugins (Node.js) localStorage Sync REST
Hoodie Sync
SLIDE 77
Database per-user
SLIDE 78
A. B.
SLIDE 79 Shared data
A. B.
SLIDE 80 Shared data
A. B.
SLIDE 81 Shared data
A. B.
SLIDE 82
Sync is hard (use existing protocols where possible)
SLIDE 83
SLIDE 84
SLIDE 85
You need to think about...
SLIDE 86
Queuing of tasks & events (resumable sync)
SLIDE 87
Identity (sandboxing & access control)
SLIDE 88
Conflicts (this affects your data model!)
SLIDE 89
D E S I G N
SLIDE 90
Launching should feel natural
SLIDE 91
SLIDE 92
...what, really?
SLIDE 93
SLIDE 94
SLIDE 95
Offline should not be an after-thought
SLIDE 96
SLIDE 97
SLIDE 98
SLIDE 99
Offline content should be trust-worthy
SLIDE 100
SLIDE 101
The spinner is a lie
SLIDE 102
I shouldn't have to plan ahead
SLIDE 103
SLIDE 104
Oh ...and docs should be on the device, not just a link to your website!
SLIDE 105
I M P A C T
SLIDE 106
Mobile is huge, offline first ensures great mobile experiences
SLIDE 107
It gives users control
SLIDE 108
SLIDE 109
It's about trust
SLIDE 110
Forces you to consider the relationship between your users and their data
SLIDE 111
You don't need to deliver all data all the time, just the right data at the right moment
SLIDE 112
It's the final hurdle in performance
SLIDE 113
SLIDE 114
Offline-first means zero latency UX. We live in the age of experiences, this is the #1 priority - @janl
SLIDE 115
It protects from service interruptions. Users may not even notice if your server is down.
SLIDE 116
Scalability – perhaps you don't even need a backend?
SLIDE 117
SLIDE 118
“Offline First” is an ongoing discussion...
SLIDE 119
How do we create a modern design language for offline?
SLIDE 120
What does offline first mean for business models?
SLIDE 121
How can we make offline first development easier?
SLIDE 122
Let's talk!
SLIDE 123
SLIDE 124 Thanks!
@caolan