Offline First @caolan Unlike the always-wired machines of the - - PowerPoint PPT Presentation

offline first
SMART_READER_LITE
LIVE PREVIEW

Offline First @caolan Unlike the always-wired machines of the - - PowerPoint PPT Presentation

Offline First @caolan Unlike the always-wired machines of the past, computers are now truly personal, and people move through online and offline seamlessly our apps should do the same More often than not, the mobile experience for a


slide-1
SLIDE 1

Offline First

@caolan

slide-2
SLIDE 2
slide-3
SLIDE 3
slide-4
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
SLIDE 5

…our apps should do the same

slide-6
SLIDE 6
slide-7
SLIDE 7
slide-8
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.”

  • Luke Wroblewski (2009)
slide-9
SLIDE 9
  • 1. Mobile is exploding
slide-10
SLIDE 10
  • 1. Mobile is exploding
  • 2. Mobile forces you to focus
slide-11
SLIDE 11
  • 1. Mobile is exploding
  • 2. Mobile forces you to focus
  • 3. Mobile extends your capabilities
slide-12
SLIDE 12
slide-13
SLIDE 13
slide-14
SLIDE 14
slide-15
SLIDE 15
slide-16
SLIDE 16

> Offline First Meetup #1, Berlin

slide-17
SLIDE 17

“When travelling, I take screenshots of important messages”

slide-18
SLIDE 18

“before the release, you turn on flight mode on and check if the app crashes…”

slide-19
SLIDE 19

“If it doesn’t, you consider the app 'offline-ready' ...this is not enough”

slide-20
SLIDE 20

Offline is not an error

slide-21
SLIDE 21

It's a legitimate use-case that isn't going away soon

slide-22
SLIDE 22

T E C H N O L O G Y

slide-23
SLIDE 23
  • 1. Delivering the application
  • 2. Detecting connectivity
  • 3. Storing data
  • 4. Syncing data
slide-24
SLIDE 24
  • 1. Delivering the application
  • 2. Detecting connectivity
  • 3. Storing data
  • 4. Syncing data
slide-25
SLIDE 25

<html manifest=”example.appcache”> ... </html>

slide-26
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
SLIDE 27
  • 1. The Application Cache will only update if the

contents of the manifest file have changed

slide-28
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
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
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
SLIDE 31

Service Worker

slide-32
SLIDE 32

<html> <head> <script> navigator.serviceWorker.register("worker.js"); </script> </head> ... </html>

slide-33
SLIDE 33

// worker.js this.addEventListener("fetch", function (e) { if (e.request.url == “/data.json”) { e.respondWith( new Response({statusCode: 200, body: …}) ); } });

slide-34
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
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
SLIDE 36

HTTP + Cache Browser Page

slide-37
SLIDE 37

HTTP + Cache Browser Page AppCache Declarative only, no direct programmatic access

slide-38
SLIDE 38

HTTP + Cache Browser Page Sits between your page and the browser's network stack Service Worker

slide-39
SLIDE 39

HTTP + Cache Browser Page It can intercept, modify and respond to network requests Service Worker

slide-40
SLIDE 40

HTTP + Cache Browser Page Cache Programmatic access to a set of durable caches Service Worker

slide-41
SLIDE 41

(Diagram totally stolen from @phuunet)

slide-42
SLIDE 42
  • 1. Delivering the application
  • 2. Detecting connectivity
  • 3. Storing data
  • 4. Syncing data
slide-43
SLIDE 43

if (navigator.onLine) { alert('online'); }

slide-44
SLIDE 44

window.addEventListener("offline", ...); window.addEventListener("online", ...);

slide-45
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
SLIDE 46

In Firefox and Internet Explorer, switching the browser to offline mode sends a false value. All other conditions return true.

slide-47
SLIDE 47

var appcache = window.applicationCache; appcache.addEventListener("error", function (e) { // probably offline });

slide-48
SLIDE 48

xhr.status === 0 xhr.readyState === 0 xhr.addEventListener('error', onDown, false); xhr.addEventListener('timeout', onDown, false);

slide-49
SLIDE 49
  • 1. Delivering the application
  • 2. Detecting connectivity
  • 3. Storing data
  • 4. Syncing data
slide-50
SLIDE 50

LocalStorage

slide-51
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-52
SLIDE 52
  • 1. It's dead simple
slide-53
SLIDE 53
  • 1. It's dead simple
  • 2. It's well supported by browsers
slide-54
SLIDE 54
slide-55
SLIDE 55
  • 1. It's synchronous (blocks UI)
slide-56
SLIDE 56
  • 1. It's synchronous (blocks UI)
  • 2. Only strings, no Blobs
slide-57
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
SLIDE 58

IndexedDB

slide-59
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-60
SLIDE 60
  • 1. Asynchronous
slide-61
SLIDE 61
  • 1. Asynchronous
  • 2. Transactions
slide-62
SLIDE 62
  • 1. Asynchronous
  • 2. Transactions
  • 3. No need to serialize/deserialize
slide-63
SLIDE 63
  • 1. Asynchronous
  • 2. Transactions
  • 3. No need to serialize/deserialize
  • 4. Indexes
slide-64
SLIDE 64
  • 1. Asynchronous
  • 2. Transactions
  • 3. No need to serialize/deserialize
  • 4. Indexes
  • 5. Higher storage limits

(browser usually asks >50mb)

slide-65
SLIDE 65
  • 1. More complicated API
slide-66
SLIDE 66
  • 1. More complicated API
  • 2. Supported by fewer browsers
slide-67
SLIDE 67
slide-68
SLIDE 68

Wrappers

slide-69
SLIDE 69
slide-70
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
SLIDE 71
  • 1. Delivering the application
  • 2. Detecting connectivity
  • 3. Storing data
  • 4. Syncing data
slide-72
SLIDE 72
slide-73
SLIDE 73
slide-74
SLIDE 74

Offline by default

slide-75
SLIDE 75

App hoodie.store localStorage

Hoodie Sync

slide-76
SLIDE 76

App hoodie.store CouchDB Plugins (Node.js) localStorage Sync REST

Hoodie Sync

slide-77
SLIDE 77

Database per-user

slide-78
SLIDE 78

A. B.

slide-79
SLIDE 79

Shared data

A. B.

slide-80
SLIDE 80

Shared data

A. B.

slide-81
SLIDE 81

Shared data

A. B.

slide-82
SLIDE 82

Sync is hard (use existing protocols where possible)

slide-83
SLIDE 83
slide-84
SLIDE 84
slide-85
SLIDE 85

You need to think about...

slide-86
SLIDE 86

Queuing of tasks & events (resumable sync)

slide-87
SLIDE 87

Identity (sandboxing & access control)

slide-88
SLIDE 88

Conflicts (this affects your data model!)

slide-89
SLIDE 89

D E S I G N

slide-90
SLIDE 90

Launching should feel natural

slide-91
SLIDE 91
slide-92
SLIDE 92

...what, really?

slide-93
SLIDE 93
slide-94
SLIDE 94
slide-95
SLIDE 95

Offline should not be an after-thought

slide-96
SLIDE 96
slide-97
SLIDE 97
slide-98
SLIDE 98
slide-99
SLIDE 99

Offline content should be trust-worthy

slide-100
SLIDE 100
slide-101
SLIDE 101

The spinner is a lie

slide-102
SLIDE 102

I shouldn't have to plan ahead

slide-103
SLIDE 103
slide-104
SLIDE 104

Oh ...and docs should be on the device, not just a link to your website!

slide-105
SLIDE 105

I M P A C T

slide-106
SLIDE 106

Mobile is huge, offline first ensures great mobile experiences

slide-107
SLIDE 107

It gives users control

slide-108
SLIDE 108
slide-109
SLIDE 109

It's about trust

slide-110
SLIDE 110

Forces you to consider the relationship between your users and their data

slide-111
SLIDE 111

You don't need to deliver all data all the time, just the right data at the right moment

slide-112
SLIDE 112

It's the final hurdle in performance

slide-113
SLIDE 113
slide-114
SLIDE 114

Offline-first means zero latency UX. We live in the age of experiences, this is the #1 priority - @janl

slide-115
SLIDE 115

It protects from service interruptions. Users may not even notice if your server is down.

slide-116
SLIDE 116

Scalability – perhaps you don't even need a backend?

slide-117
SLIDE 117
slide-118
SLIDE 118

“Offline First” is an ongoing discussion...

slide-119
SLIDE 119

How do we create a modern design language for offline?

slide-120
SLIDE 120

What does offline first mean for business models?

slide-121
SLIDE 121

How can we make offline first development easier?

slide-122
SLIDE 122

Let's talk!

slide-123
SLIDE 123
slide-124
SLIDE 124

Thanks!

@caolan