$30 off During Our Annual Pro Sale. View Details »

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

Marco Cedaro

March 11, 2016
Tweet

More Decks by Marco Cedaro

Other Decks in Programming

Transcript

  1. The
    UnCloud

    View Slide

  2. Basically
    UNCLOUD?

    View Slide

  3. Once upon a
    time…
    1980s

    View Slide

  4. …fast forward
    2010s

    View Slide

  5. Something
    happened
    1980s 2010s

    View Slide

  6. TL;DR:
    2007

    View Slide

  7. @cedmax
    Front end

    developer since
    able to grow a
    beard.
    90s/00s

    View Slide

  8. Basically
    postAPPSERA

    View Slide

  9. Challenges
    Privacy
    Security
    Design & Ux
    Context
    APIs access

    View Slide

  10. The web?
    2004

    View Slide

  11. The web…

    View Slide

  12. …was different

    View Slide

  13. Adapt
    2008/9

    View Slide

  14. Adapt
    2008/9

    View Slide

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

    Ethan Marcotte

    http://alistapart.com/article/responsive-web-design
    2010

    View Slide

  16. Flexibility
    2008/9

    View Slide

  17. Catch up

    View Slide

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

    Ethan Marcotte

    http://alistapart.com/article/responsive-web-design

    View Slide

  19. Challenges
    Privacy
    Security
    Design & Ux
    Context
    APIs access

    View Slide

  20. Network
    perception

    View Slide

  21. Internet

    View Slide

  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

    View Slide

  23. offline is a
    big deal

    View Slide

  24. Application
    Cache
    2010

    View Slide



  25. # 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

    View Slide

  26. View Slide

  27. Biggest 

    problem
    Cache First Only

    &

    The Cache only updates if 

    the manifest itself changes

    View Slide



  28. View Slide

  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

    View Slide

  30. View Slide

  31. Basically
    current
    state

    View Slide

  32. you might have
    heard of

    View Slide

  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/

    View Slide

  34. Pretty
    powerful

    View Slide

  35. but with great
    powers…

    View Slide

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

    can access IndexDB API [1]
    serviceworker.js

    View Slide

  37. View Slide

  38. self.addEventListener('install', function(event) {
    //[…]
    });
    self.addEventListener('activate', function(event) {
    //[…]
    });
    self.addEventListener('fetch', function(event) {
    //[…]
    });

    View Slide

  39. a working
    example

    View Slide

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

    View Slide

  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());
    });

    View Slide

  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());
    });

    View Slide

  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());
    });

    View Slide

  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());
    });

    View Slide

  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());
    });

    View Slide

  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/

    View Slide

  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())
    });

    View Slide

  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())
    });

    View Slide

  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())
    });

    View Slide

  50. // serviceworker.js
    self.addEventListener('fetch', function(event) {
    //Where the magic lives
    });

    View Slide

  51. cache first

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  57. network first

    View Slide

  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');
    })
    })
    );
    });

    View Slide

  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');
    })
    })
    );
    });

    View Slide

  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');
    })
    })
    );
    });

    View Slide

  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');
    })
    })
    );
    });

    View Slide

  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');
    })
    })
    );
    });

    View Slide

  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');
    })
    })
    );
    });

    View Slide

  64. not a GET?

    View Slide

  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;
    }
    });

    View Slide

  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;
    }
    });

    View Slide

  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;
    }
    });

    View Slide

  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;
    }
    });

    View Slide

  69. wrapping up

    View Slide

  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);
    }
    });

    View Slide

  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);
    }
    });

    View Slide

  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);
    }
    });

    View Slide

  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);
    }
    });

    View Slide

  74. service workers
    give you control

    View Slide

  75. https://serviceworke.rs/
    The Service Worker Cookbook is a
    collection of working, practical examples of
    using service workers in modern web apps.

    View Slide

  76. Basically
    WHAT NOW?

    View Slide

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

    View Slide

  78. this is sorted

    View Slide

  79. but is it?

    View Slide

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

    View Slide

  81. Progressive
    enhancement

    View Slide

  82. We’ll fix that

    View Slide

  83. a takeaway note

    View Slide

  84. Does this make
    any sense?

    View Slide

  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

    View Slide

  86. And yet the
    web persists.

    View Slide

  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
    [email protected]
    http://cedmax.com
    @cedmax
    any question?

    View Slide