Upgrade to Pro — share decks privately, control downloads, hide ads and more …

At Your Service

At Your Service

Lets face it. There are more devices out there than you can support with dedicated native apps. And except for very specific cases, most of what you’ll want to do with your app is available through web API’s. And yes, this includes offline support. Whether it’s a loss of cellular connectivity or lousy WiFi at a conference, there will be times when a site can and should be available when the network is not. During this presentation we’ll take a look at Service Workers, which can be used for store data in the client browser and how you can leverage them.

Presented at the following conferences:
- 2016 RWD Summit
- 2016 ConvergeSE
- 2016 HighEdWeb Michigan
- 2016 HighEdWeb National

Erik Runyon

October 17, 2017
Tweet

More Decks by Erik Runyon

Other Decks in Programming

Transcript

  1. At Your Service The Next Generation of Offline Websites Erik

    Runyon @erunyon #AIM2 2016.highedweb.org
  2. What it is… Service workers act as proxy servers between

    web applications, the browser, and network. They enable the creation of offline experiences by intercepting network requests and taking appropriate action based on whether the network is available and updated assets reside on the server. https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API
  3. Chrome Android (Chrome) Firefox Opera Safari/iOS Edge IE 40+ 46+

    44+ 27+ None* None** None Current Support * Under Consideration ** In Development http://caniuse.com/#feat=serviceworkers = 61% = 55% = 55% Global U.S.A. Notre Dame
  4. WebKit 5-year plan “People think they want it, some of

    them actually do want it. We should probably do it.” https://trac.webkit.org/wiki/FiveYearPlanFall2015
  5. A Service Worker is a stand-alone javascript file that is

    initialized from elsewhere in the site and has control over its current scope.
  6. // site.js function loadImage(){ var rand = (Math.floor(Math.random() * 200)

    + 200); var img = document.createElement("img"); img.src = 'https://placekitten.com/' + rand + '/300'; document.getElementById('images').appendChild(img); } https://demos.erikrunyon.com/sw-img-override/
  7. 4. Start intercepting requests! self.addEventListener('fetch', event => { let request

    = event.request; if(request.headers.get('Accept').indexOf('text/html') !== -1){ event.respondWith( fetch(request) ); return; } if(request.headers.get('Accept').indexOf('image') !== -1){ let rand = (Math.floor(Math.random() * 200) + 200); event.respondWith( fetch( 'https://www.placecage.com/' + rand + ‘/300', {mode: 'no-cors'} ) ); } }); https://demos.erikrunyon.com/sw-img-override/
  8. 'use strict'; self.addEventListener('install', event => { self.skipWaiting(); }); self.addEventListener('activate', event

    => { self.clients.claim(); }); self.addEventListener('fetch', event => { let request = event.request; if(request.headers.get('Accept').indexOf('text/html') !== -1){ event.respondWith( fetch(request) ); return; } if(request.headers.get('Accept').indexOf('image') !== -1){ let rand = (Math.floor(Math.random() * 200) + 200); event.respondWith( fetch( 'https://www.placecage.com/' + rand + ‘/300', {mode: 'no-cors'} ) ); } });
  9. <picture> <!-- JPEG Images --> <source media="(min-width: 1024px)" srcset="./images/brooklyn.jpg, ./images/brooklyn-2x.jpg

    2x, ./images/brooklyn-3x.jpg 3x" type="image/jpeg"> <source media="(min-width: 320px)" srcset="./images/brooklyn-small.jpg, ./images/brooklyn-small-2x.jpg 2x, ./images/brooklyn-small-3x.jpg 3x" type="image/jpeg"> <!-- WebP Images --> <source media="(min-width: 1024px)" srcset="./images/brooklyn.webp, ./images/brooklyn-2x.webp 2x, ./images/brooklyn-3x.webp 3x" type="image/webp"> <source media="(min-width: 320px)" srcset="./images/brooklyn-small.webp, ./images/brooklyn-small-2x.webp 2x, ./images/brooklyn-small-3x.webp 3x" type="image/webp"> <!-- The fallback image --> <img src="./images/brooklyn.jpg" alt="Brooklyn Bridge - New York"> </picture> Cleaning up <picture>
  10. A Popular Workflow 1. Store theme, important pages, and offline

    page in cache 2. Cache additional pages and images as the user browses 3. On repeat visit, theme files default to the cache 4. Pages default to the network 5. Account for missing pages and images while offline https://erikrunyon.com/sw.js
  11. 2. Installing self.addEventListener('install', function(event) { event.waitUntil( caches.open(my_cache_version).then(function(cache) { return cache.addAll([

    '/css/images/leaves-right.jpg', '/css/images/icons-social.png', '/css/site.css', '/js/site.js', '/', '/offline/' ]); }).then( () => self.skipWaiting() ) ); }); https://erikrunyon.com/sw.js
  12. 3. Activating self.addEventListener('activate', event => { event.waitUntil( caches.keys() .then( keys

    => { return Promise.all(keys .filter(key => key.indexOf(my_cache_version) !== 0) .map(key => caches.delete(key)) ); }); ).then( () => self.clients.claim() ); }); https://erikrunyon.com/sw.js
  13. 4. Start intercepting requests! // Images (return SVG when offline)

    if (request.headers.get('Accept').indexOf('image') !== -1) { return new Response('<svg role="img" …</svg>', {headers: {'Content-Type': 'image/svg+xml'}} ); } // HTML if (request.headers.get('Accept').indexOf('text/html') !== -1) { // Do something } https://erikrunyon.com/sw.js
  14. Terminated when not in use, and restarted when it's next

    needed (can’t persist variables).
  15. Dynamically Update Cache Version https://erikrunyon.com/2016/03/service-worker-version-using-jekyll/ 'use strict'; const version =

    ‘{{ site.time | date: ‘%Y%m%d%H%M%S' }}::'; const staticCacheName = version + 'static'; const pagesCacheName = version + 'pages'; const imagesCacheName = version + 'images';
  16. Send data to a Service Worker using PostMessage // In

    the site javascript file window.addEventListener('load', function () { if(navigator.serviceWorker.controller) { navigator.serviceWorker.controller.postMessage({ 'myRandomStuff': { width:document.body.clientWidth, height:document.body.clientHeight } }); } }); // In the Service Worker self.addEventListener('message', event => { console.log(event.data.myRandomStuff); }); https://developer.mozilla.org/en-US/docs/Web/API/Client/postMessage
  17. Service Workers in the Wild • lyza.com* • adactio.com* •

    ponyfoo.com* • smashingmagazine.com* • medium.com • modernizr.com • serviceworke.rs • theguardian.com • platform-status.mozilla.org chrome://serviceworker-internals/
  18. What else will Service Workers do? 1. Handling push notifications

    2. Background data synchronization 3. Responding to resource requests from other origins 4. Entering a geo-fence 5. Reacting to a particular time & date https://w3c.github.io/ServiceWorker/
  19. Progressive Web Applications take advantage of new technologies to bring

    the best of mobile sites and native applications to users. They're reliable, fast, and engaging. https://developers.google.com/web/progressive-web-apps/ “
  20. Web App Manifest https://w3c.github.io/manifest/ This specification defines a JSON-based manifest

    file that provides developers with a centralized place to put metadata associated with a web application. This metadata includes, but is not limited to, the web application's name, links to icons, as well as the preferred URL to open when a user launches the web application.
  21. Installable Web Apps • is associated with a manifest with

    at least a name member and a suitable icon. • is served over a secure network connection. • has a sensible content security policy. • is able to responsibly adapt to display on a variety of screen sizes, catering for both mobile and desktop. • is able to function without a network connection. • is repeatedly used by the end-user over some extended period of time. • has been explicitly marked by the user as one that they value and trust (e.g., by bookmarking or "starring" it). https://w3c.github.io/manifest/
  22. Google’s pre-reqs • Must have a web app manifest file

    • The manifest must have a `short_name`, a name for display in the banner • A start URL (e.g. / or index.html) which must be loadable • At least an `144x144` PNG icon • Icon declarations should include a mime type of image/png • Should have a service worker registered on the site • Served over HTTPS (service worker requires HTTPS for security) • The user has visited your site at least twice, with at least five minutes between visits https://developers.google.com/web/updates/2015/03/increasing-engagement-with-app-install-banners-in-chrome-for-android
  23. • fullscreen: take over the whole screen. • standalone: opens

    the app with a status bar. • minimal-ui: like on iOS, the app is fullscreen, but certain actions can cause the navigation bar and back/forward buttons to reappear. • browser: opens your app with normal browser toolbars and buttons. “display” options
  24. Erik Runyon erikrunyon.com @erunyon thank you Credits Offline phone graphic:

    @tpacket Hunny-Do Design: @zastrow Browser logos: https://github.com/alrra/browser-logos Photos: Lego worker: https://www.flickr.com/photos/stavos52093/ Postage Stamp Show: https://www.flickr.com/photos/vegaskent/3265121626/ Terminator: Dan Shearn - http://mungolovescandy.blogspot.com/