The UnCloud - CloudConf 2016

The UnCloud - CloudConf 2016

The cloud changed the way we think about our services.But what happens when there isn't one? Is your web client ready to deal with a down or a loss of connectivity?

This talk will explore reasons, the awkward first steps and the current state of art of offline for web application.

note: Jake Archibald's video clip was extracted from https://www.youtube.com/watch?v=cR-TP6jOSQM

3ca63d4e2f2be0ef47b841e63b564d18?s=128

Marco Cedaro

March 11, 2016
Tweet

Transcript

  1. The UnCloud

  2. Basically UNCLOUD?

  3. Once upon a time… 1980s

  4. …fast forward 2010s

  5. Something happened 1980s 2010s

  6. TL;DR: 2007

  7. @cedmax Front end
 developer since able to grow a beard.

    90s/00s
  8. Basically postAPPSERA

  9. Challenges Privacy Security Design & Ux Context APIs access …

  10. The web? 2004

  11. The web…

  12. …was different

  13. Adapt 2008/9

  14. Adapt 2008/9

  15. “The landscape is shifting, perhaps more quickly than we might

    like.” 
 Ethan Marcotte
 http://alistapart.com/article/responsive-web-design 2010
  16. Flexibility 2008/9

  17. Catch up

  18. “Mobile browsing is expected to outpace desktop-based access within three

    to five years.” 
 Ethan Marcotte
 http://alistapart.com/article/responsive-web-design
  19. Challenges Privacy Security Design & Ux Context APIs access …

  20. Network perception

  21. Internet

  22. “I intend to protect a free and open Internet, extend

    its reach to every classroom, and every community, and help folks build the fastest network.” 
 Barack Obama, President of USA
 https://www.whitehouse.gov/blog/2015/01/20/watch-president-obamas-2015-state-union
  23. offline is a big deal

  24. Application Cache 2010

  25. <!-- index.html --> <html manifest="offline.appcache"> # offline.appcache CACHE MANIFEST assets/v6/script/script.min.js

    assets/v6/style/main.min.css assets/v6/style/font/pro.ttf assets/v6/style/img/sprites.png
  26. None
  27. Biggest 
 problem Cache First Only
 &
 The Cache only

    updates if 
 the manifest itself changes
  28. <!-- index.html --> <html manifest="offline.appcache">

  29. “We all know someone who needs “observing” more than others

    in case they do something really stupid. ApplicationCache is one of those people” 
 Jake Archibald
 http://alistapart.com/article/application-cache-is-a-douchebag 2012
  30. None
  31. Basically current state

  32. you might have heard of

  33. “A service worker is a script that is run by

    your browser in the background, separate from a web page, opening the door to features which don’t need a web page or user interaction.” 
 Matt Gaunt
 http://www.html5rocks.com/en/tutorials/service-worker/introduction/
  34. Pretty powerful

  35. but with great powers…

  36. Can’t access the DOM Can’t hold a state[1] It’s event

    driven 
 can access IndexDB API [1] serviceworker.js
  37. None
  38. self.addEventListener('install', function(event) { //[…] }); self.addEventListener('activate', function(event) { //[…] });

    self.addEventListener('fetch', function(event) { //[…] });
  39. a working example

  40. // script.js if (navigator.serviceWorker) { navigator.serviceWorker.register('/serviceworker.js'); }

  41. // serviceworker.js var staticCacheName = 'cache-v1'; var urlsToCache = [

    '/', '/styles/main.css', '/script/main.js', '/offline' ]; function updateStaticCache() { return caches.open(staticCacheName) .then(function(cache) { return cache.addAll(urlsToCache); }); }; self.addEventListener('install', function (event) { event.waitUntil(updateStaticCache()); });
  42. // serviceworker.js var staticCacheName = 'cache-v1'; var urlsToCache = [

    '/', '/styles/main.css', '/script/main.js', '/offline' ]; function updateStaticCache() { return caches.open(staticCacheName) .then(function(cache) { return cache.addAll(urlsToCache); }); }; self.addEventListener('install', function (event) { event.waitUntil(updateStaticCache()); });
  43. // serviceworker.js var staticCacheName = 'cache-v1'; var urlsToCache = [

    '/', '/styles/main.css', '/script/main.js', '/offline' ]; function updateStaticCache() { return caches.open(staticCacheName) .then(function(cache) { return cache.addAll(urlsToCache); }); }; self.addEventListener('install', function (event) { event.waitUntil(updateStaticCache()); });
  44. // serviceworker.js var staticCacheName = 'cache-v1'; var urlsToCache = [

    '/', '/styles/main.css', '/script/main.js', '/offline' ]; function updateStaticCache() { return caches.open(staticCacheName) .then(function(cache) { return cache.addAll(urlsToCache); }); }; self.addEventListener('install', function (event) { event.waitUntil(updateStaticCache()); });
  45. // serviceworker.js var staticCacheName = 'cache-v1'; var urlsToCache = [

    '/', '/styles/main.css', '/script/main.js', '/offline' ]; function updateStaticCache() { return caches.open(staticCacheName) .then(function(cache) { return cache.addAll(urlsToCache); }); }; self.addEventListener('install', function (event) { event.waitUntil(updateStaticCache()); });
  46. “When the user navigates to your site, the browser tries

    to redownload the script file that defined the service worker in the background.” 
 Matt Gaunt
 http://www.html5rocks.com/en/tutorials/service-worker/introduction/
  47. // serviceworker.js var staticCacheName = ‘cache-v2'; var clearOldCaches = function()

    { return caches.keys().then(function(keys) { return Promise.all( keys.filter(function (key) { return key.indexOf(version) != 0; }).map(function (key) { return caches.delete(key); }) ); }) } self.addEventListener("activate", function(event) { event.waitUntil(clearOldCaches()) });
  48. // serviceworker.js var staticCacheName = ‘cache-v2'; var clearOldCaches = function()

    { return caches.keys().then(function(keys) { return Promise.all( keys.filter(function (key) { return key.indexOf(version) != 0; }).map(function (key) { return caches.delete(key); }) ); }) } self.addEventListener("activate", function(event) { event.waitUntil(clearOldCaches()) });
  49. // serviceworker.js var staticCacheName = ‘cache-v2'; var clearOldCaches = function()

    { return caches.keys().then(function(keys) { return Promise.all( keys.filter(function (key) { return key.indexOf(version) != 0; }).map(function (key) { return caches.delete(key); }) ); }) } self.addEventListener("activate", function(event) { event.waitUntil(clearOldCaches()) });
  50. // serviceworker.js self.addEventListener('fetch', function(event) { //Where the magic lives });

  51. cache first

  52. // serviceworker.js self.addEventListener('fetch', function(event) { event.respondWith( caches.match(request) .then(function (response) {

    return response || fetch(request) .catch(handleFailure) }) ); });
  53. // serviceworker.js self.addEventListener('fetch', function(event) { event.respondWith( caches.match(request) .then(function (response) {

    return response || fetch(request) .catch(handleFailure) }) ); });
  54. // serviceworker.js self.addEventListener('fetch', function(event) { event.respondWith( caches.match(request) .then(function (response) {

    return response || fetch(request) .catch(handleFailure) }) ); });
  55. // serviceworker.js self.addEventListener('fetch', function(event) { event.respondWith( caches.match(request) .then(function (response) {

    return response || fetch(request) .catch(handleFailure) }) ); });
  56. // serviceworker.js self.addEventListener('fetch', function(event) { event.respondWith( caches.match(request) .then(function (response) {

    return response || fetch(request) .catch(handleFailure) }) ); });
  57. network first

  58. // serviceworker.js self.addEventListener('fetch', function(event) { event.respondWith( fetch(request, { credentials: 'include'

    }) .catch(function () { return caches.match(request) .then(function (response) { return response || caches.match('/offline'); }) }) ); });
  59. // serviceworker.js self.addEventListener('fetch', function(event) { event.respondWith( fetch(request, { credentials: 'include'

    }) .catch(function () { return caches.match(request) .then(function (response) { return response || caches.match('/offline'); }) }) ); });
  60. // serviceworker.js self.addEventListener('fetch', function(event) { event.respondWith( fetch(request, { credentials: 'include'

    }) .catch(function () { return caches.match(request) .then(function (response) { return response || caches.match('/offline'); }) }) ); });
  61. // serviceworker.js self.addEventListener('fetch', function(event) { event.respondWith( fetch(request, { credentials: 'include'

    }) .catch(function () { return caches.match(request) .then(function (response) { return response || caches.match('/offline'); }) }) ); });
  62. // serviceworker.js self.addEventListener('fetch', function(event) { event.respondWith( fetch(request, { credentials: 'include'

    }) .catch(function () { return caches.match(request) .then(function (response) { return response || caches.match('/offline'); }) }) ); });
  63. // serviceworker.js self.addEventListener('fetch', function(event) { event.respondWith( fetch(request, { credentials: 'include'

    }) .catch(function () { return caches.match(request) .then(function (response) { return response || caches.match('/offline'); }) }) ); });
  64. not a GET?

  65. // serviceworker.js self.addEventListener('fetch', function(event) { var request = event.request; if

    (request.method !== 'GET') { event.respondWith( fetch(request) .catch(function () { return caches.match('/offline'); }) ); return; } });
  66. // serviceworker.js self.addEventListener('fetch', function(event) { var request = event.request; if

    (request.method !== 'GET') { event.respondWith( fetch(request) .catch(function () { return caches.match('/offline'); }) ); return; } });
  67. // serviceworker.js self.addEventListener('fetch', function(event) { var request = event.request; if

    (request.method !== 'GET') { event.respondWith( fetch(request) .catch(function () { return caches.match('/offline'); }) ); return; } });
  68. // serviceworker.js self.addEventListener('fetch', function(event) { var request = event.request; if

    (request.method !== 'GET') { event.respondWith( fetch(request) .catch(function () { return caches.match('/offline'); }) ); return; } });
  69. wrapping up

  70. // serviceworker.js function isHMTL(request){ return request.headers.get('Accept') .indexOf('text/html') !== -1; }

    self.addEventListener('fetch', function(event) { var request = event.request; if (request.method !== 'GET') { return networkOnly(event); } if (isHTML(request)) { return networkFirst(event); } else { return cacheFirst(event); } });
  71. // serviceworker.js function isHMTL(request){ return request.headers.get('Accept') .indexOf('text/html') !== -1; }

    self.addEventListener('fetch', function(event) { var request = event.request; if (request.method !== 'GET') { return networkOnly(event); } if (isHTML(request)) { return networkFirst(event); } else { return cacheFirst(event); } });
  72. // serviceworker.js function isHMTL(request){ return request.headers.get('Accept') .indexOf('text/html') !== -1; }

    self.addEventListener('fetch', function(event) { var request = event.request; if (request.method !== 'GET') { return networkOnly(event); } if (isHTML(request)) { return networkFirst(event); } else { return cacheFirst(event); } });
  73. // serviceworker.js function isHMTL(request){ return request.headers.get('Accept') .indexOf('text/html') !== -1; }

    self.addEventListener('fetch', function(event) { var request = event.request; if (request.method !== 'GET') { return networkOnly(event); } if (isHTML(request)) { return networkFirst(event); } else { return cacheFirst(event); } });
  74. service workers give you control

  75. https://serviceworke.rs/ The Service Worker Cookbook is a collection of working,

    practical examples of using service workers in modern web apps.
  76. Basically WHAT NOW?

  77. demo time https://pokedex.org 2015/16

  78. this is sorted

  79. but is it?

  80. // script.js if (navigator.serviceWorker) { navigator.serviceWorker.register('/serviceworker.js'); }

  81. Progressive enhancement

  82. We’ll fix that

  83. a takeaway note

  84. Does this make any sense?

  85. “ There will always be some alternative that is technologically

    more advanced than the web. First there were CD- ROMs. Then we had Flash. Now we have native apps.” 
 Jeremy Keith
 https://adactio.com/journal/9016
  86. And yet the web persists.

  87. Responsive Web Design Ethan Marcotte 
 http://alistapart.com/article/responsive-web-design Application Cache is

    A Douchebag Jake Archibald http://alistapart.com/article/application-cache-is-a- douchebag Introduction to Service Worker
 Matt Gaunt http://www.html5rocks.com/en/tutorials/service- worker/introduction/ Pokedex.org Nolan Lawson https://pokedex.org https://github.com/nolanlawson/pokedex.org My First Service Worker Jeremy Keith https://adactio.com/journal/9775 Web! What is it good for? Jeremy Keith https://adactio.com/journal/9016 Resources marco@fromthefront.it http://cedmax.com @cedmax any question?