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

Progressive Web Apps in the Real World

Progressive Web Apps in the Real World

This workshop was first presented at Devoxx UK Conference on the 8th June 2016.

http://cfp.devoxx.co.uk/2016/speaker/dean_hume

Presentation Details
----------------------------------

As web developers, we are constantly striving to provide our users with the best possible browsing experience. Regardless of their device, our aim is to provide them with resilient, performant websites that just work. Imagine if you could take this to the next level and provide your users with a super fast website with a similar experience to native applications. This is where Service Workers come in.

They are a promising game changer that provides developers with powerful features such as offline browsing, periodic background syncs, and push notifications to name just a few!

This talk will dive into Progressive apps and showcase some of the features that are available for developers to start using today.

Topics include:
- The basics of Service Workers
- Eliminating third-party single points of failure
- Supercharging your caching
- Implement offline browsing
- Push notifications on the web
- Background Sync
- How to debug your Service Worker and much more!

In this talk, we will run through working a few working examples and explore the ins and outs of Progressive. This talk is aimed at all web developers regardless of skill level - there is something for everyone!

For more information visit http://deanhume.com

Dean Hume

June 08, 2016
Tweet

More Decks by Dean Hume

Other Decks in Technology

Transcript

  1. These apps aren’t packaged and deployed through stores, they’re just

    websites that took all the right vitamins. Alex Russell “
  2. What does this actually mean for our users? ★ More

    engaging ★ Faster ★ Reliable
  3. This Talk 1. Understanding Progressive Web Apps 2. Engage your

    users 3. Speed up your site 4. Improve reliability
  4. Think of your web apps requests as planes taking off.

    ServiceWorker is the air traffic controller that routes the requests. Jeff Posnick “
  5. Look and feel <!-- Chrome, Firefox OS and Opera -->

    <meta name="theme-color" content="#09adec"> <!-- Windows Phone --> <meta name="msapplication-navbutton-color" content=" #09adec"> <!-- iOS Safari --> <meta name="apple-mobile-web-app-capable" content="yes"> <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
  6. { lang: "en", background_color: "#09adec", name: "Building Great Startup Teams",

    short_name: "Building Great Startup Teams", display: "standalone", icons: [ { src: "./images/logo-144.png", sizes: "144x144", type: "image/png" } ], start_url: "./index.html", orientation: "portrait" }
  7. { lang: "en", background_color: "#09adec", name: "Building Great Startup Teams",

    short_name: "Building Great Startup Teams", display: "standalone", icons: [ { src: "./images/logo-144.png", sizes: "144x144", type: "image/png" } ], start_url: "./index.html", orientation: "portrait" }
  8. { lang: "en", background_color: "#09adec", name: "Building Great Startup Teams",

    short_name: "Building Great Startup Teams", display: "standalone", icons: [ { src: "./images/logo-144.png", sizes: "144x144", type: "image/png" } ], start_url: "./index.html", orientation: "portrait" }
  9. if ('serviceWorker' in navigator) { navigator.serviceWorker.register('service-worker.js').then(function (registration) { // Registration

    was successful }).catch(function(err) { // registration failed :( console.log('ServiceWorker registration failed: ', err); }); }
  10. if ('serviceWorker' in navigator) { navigator.serviceWorker.register('service-worker.js').then(function (registration) { // Registration

    was successful registration.pushManager.subscribe({userVisibleOnly: true}) .then(function(subscription){ }); }).catch(function(err) { // registration failed :( console.log('ServiceWorker registration failed: ', err); }); }
  11. if ('serviceWorker' in navigator) { navigator.serviceWorker.register('service-worker.js').then(function (registration) { // Registration

    was successful registration.pushManager.subscribe({userVisibleOnly: true}) .then(function(subscription){ // Send to Server return sendSubscriptionToServer(subscription); }); }).catch(function(err) { // registration failed :( console.log('ServiceWorker registration failed: ', err); }); }
  12. { "name": "Push Demo", "short_name": "Push Demo", "icons": [{ "src":

    "icon.png", "sizes": "192x192", "type": "image/png" }], "start_url": "/index.html", "display": "standalone", "gcm_sender_id": "57874135625436178", "gcm_user_visible_only": true, "permissions": [ "gcm" ]} Manifest
  13. Add to home screen 1. You need a manifest.json file

    2. Your manifest file needs a start URL
  14. Add to home screen 1. You need a manifest.json file

    2. Your manifest file needs a start URL 3. You need a 144x144 PNG icon
  15. Add to home screen 1. You need a manifest.json file

    2. Your manifest file needs a start URL 3. You need a 144x144 PNG icon 4. Your site is using a Service Worker running over HTTPS
  16. Add to home screen 1. You need a manifest.json file

    2. Your manifest file needs a start URL 3. You need a 144x144 PNG icon 4. Your site is using a Service Worker running over HTTPS 5. The user has visited your site at least twice, with at least five minutes between visits.
  17. { lang: "en", background_color: "#09adec", name: "Building Great Startup Teams",

    short_name: "Building Great Startup Teams", display: "standalone", icons: [ { src: "./images/logo-144.png", sizes: "144x144", type: "image/png" } ], start_url: "./index.html", orientation: "portrait" }
  18. var cacheVersion = 1; var currentCache = { assetCache: 'cache'

    + cacheVersion }; this.addEventListener('install', event => { event.waitUntil(caches.open(currentCache.assetCache) .then(function(cache) { }) ); }); Caching
  19. var cacheVersion = 1; var currentCache = { assetCache: 'cache'

    + cacheVersion }; this.addEventListener('install', event => { event.waitUntil(caches.open(currentCache.assetCache) .then(function(cache) { return cache.addAll(['result.min.css', './js/material.min.js']); }) ); }); Caching
  20. var cacheVersion = 1; var currentCache = { assetCache: 'cache'

    + cacheVersion }; this.addEventListener('install', event => { event.waitUntil(caches.open(currentCache.assetCache) .then(function(cache) { return cache.addAll(['result.min.css', './js/material.min.js']); }) ); }); this.addEventListener('fetch', event => { event.respondWith(caches.match(event.request) .then(function (response) { return response || fetch(event.request); }) ); }); Caching
  21. this.addEventListener('fetch', event => { if(event.request.headers.get('save-data')){ if (/\.jpg$|.gif$|.png$/.test(event.request.url)) { // We

    can do something event.respondWith( fetch(‘low-quality-’ + event.request.url, { mode: 'no-cors' }) } } });
  22. this.addEventListener('fetch', event => { if(event.request.headers.get('save-data')){ if (/\.jpg$|.gif$|.png$/.test(event.request.url)) { // We

    can do something event.respondWith( fetch(‘low-quality-’ + event.request.url, { mode: 'no-cors' }) } } });
  23. this.addEventListener('fetch', event => { if(event.request.headers.get('save-data')){ if (/\.jpg$|.gif$|.png$/.test(event.request.url)) { // We

    can do something event.respondWith( fetch(‘low-quality-’ + event.request.url, { mode: 'no-cors' }) } } });
  24. this.addEventListener('fetch', event => { if(event.request.headers.get('save-data')){ if (/\.jpg$|.gif$|.png$/.test(event.request.url)) { // We

    can do something event.respondWith( fetch(‘low-quality-’ + event.request.url, { mode: 'no-cors' }) } } });
  25. // Request a one-off sync: navigator.serviceWorker.ready.then( function(swRegistration) { return swRegistration.sync.register('myFirstSync');

    }); self.addEventListener('sync', function(event) { if (event.tag == 'myFirstSync') { event.waitUntil(expensiveOperation()); } });
  26. // Request a one-off sync: navigator.serviceWorker.ready.then( function(swRegistration) { return swRegistration.sync.register('myFirstSync');

    }); self.addEventListener('sync', function(event) { if (event.tag == 'myFirstSync') { event.waitUntil(expensiveOperation()); } });
  27. this.addEventListener('fetch', event => { // Check if the user navigated

    if (event.request.method === 'GET' && event.request.headers. get('accept').includes('text/html')) { // Respond appropriately } };
  28. this.addEventListener('fetch', event => { // Check if the user navigated

    if (event.request.method === 'GET' && event.request.headers. get('accept').includes('text/html')) { // Respond appropriately event.respondWith( fetch(event.request.url).catch(error => { // Return the offline page return caches.match('the-offline-page'); })); } };
  29. function timeout(delay) { return new Promise(function(resolve, reject) { setTimeout(function(){ resolve(new

    Response('', { status: 408, statusText: 'Request timed out.' })); }, delay); }); }
  30. function timeout(delay) { return new Promise(function(resolve, reject) { setTimeout(function(){ resolve(new

    Response('', { status: 408, statusText: 'Request timed out.' })); }, delay); }); } self.addEventListener('fetch', event => { });
  31. function timeout(delay) { return new Promise(function(resolve, reject) { setTimeout(function(){ resolve(new

    Response('', { status: 408, statusText: 'Request timed out.' })); }, delay); }); } self.addEventListener('fetch', event => { event.respondWith(Promise.race([timeout(6000), fetch (event.request.url)])); });
  32. Summary 1. Understanding Progressive Web Apps 2. Engage your users

    3. Speed up your site 4. Improve reliability
  33. Further reading ❖ Shell Architecture - bit.ly/instant-shell ❖ Getting Started

    - bit.ly/starting-pwas ❖ Service Worker - bit.ly/pwa-swers ❖ Code Labs -bit.ly/code-labs-pwa ❖ Recipes ➢ bit.ly/mozilla-service-workers ➢ bit.ly/chrome-service-workers