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

Progressive Web Apps – Mobile has natively come to the Web

Surma
April 23, 2016

Progressive Web Apps – Mobile has natively come to the Web

JSUnconf.eu 2016

Surma

April 23, 2016
Tweet

More Decks by Surma

Other Decks in Technology

Transcript

  1. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"> <HTML> <HEAD> <TITLE>Bach's home

    page</TITLE> <STYLE type="text/css"> h1 { color: red } </STYLE> </HEAD> <BODY> <H1>Bach's home page</H1> <P>Johann Sebastian Bach was a prolific composer. </BODY> </HTML>
  2. Service Workers are to Progressive Web Apps as XMLHttpRequest was

    to Ajax The foundational capability that was a tipping point for innovation
  3. Installable <link rel="apple-touch-icon" href="touch-icon-iphone.png"> // Later on more icon sizes....

    <link rel="apple-touch-icon" sizes="76x76" href="touch-icon-ipad.png"> <link rel="apple-touch-icon" sizes="120x120" href="touch-icon-iphone-retina.png"> <link rel="apple-touch-icon" sizes="152x152" href="touch-icon-ipad-retina.png"> // Nice splash screen ... <link rel="apple-touch-startup-image" href="/startup.png">
  4. Launch like an app (no address bar) <meta name="apple-mobile-web-app-capable" content="yes">

    <meta name="apple-mobile-web-app-status-bar-style" content="black">
  5. Offline available <html manifest="app.cache"> db = openDatabase("Todo", "1", "Todo", 5

    * 1024 * 1024); db.transaction(function(tx) { tx.executeSql("SELECT * FROM CARS)"); }); App Cache Web SQL
  6. Source: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis

    non erat sem Installable web apps in 2 simple steps { "name": "The Airhorner", "short_name": "Airhorner", "icons": [], "start_url": "index.html", "display": "standalone", "theme_color": "", "background_color": "" } 1. Create a manifest (json) <link rel="manifest" href="/manifest.json"> 2. Link it to your page
  7. Source: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis

    non erat sem Flow 1. User clicks “Add to home screen” a. Manifest is downloaded 2. Confirmation prompt shown 3. You are on the homescreen
  8. Source: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis

    non erat sem Flow 1. User clicks “Add to home screen” a. Manifest is downloaded 2. Confirmation prompt shown 3. You are on the homescreen
  9. Source: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis

    non erat sem Flow 1. User clicks “Add to home screen” a. Manifest is downloaded 2. Confirmation prompt shown 3. You are on the homescreen
  10. Source: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis

    non erat sem Flow { "name": "The Air Horner", "short_name": "Air Horner", "icons": [ { "src": "images/Airhorner_192.png", "type": "image/png", "sizes": "192x192" } ], "start_url": "index.html", "display": "standalone", "theme_color": "#2196F3", "background_color": "#2196F3" }
  11. Source: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis

    non erat sem Homescreen { "name": "The Air Horner", "short_name": "Air Horner", "icons": [ { "src": "images/Airhorner_192.png", "type": "image/png", "sizes": "192x192" } ], "start_url": "index.html", "display": "standalone", "theme_color": "#2196F3", "background_color": "#2196F3" }
  12. Source: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis

    non erat sem App switcher { "name": "The Air Horner", "short_name": "Air Horner", "icons": [ { "src": "images/Airhorner_192.png", "type": "image/png", "sizes": "192x192" } ], "start_url": "index.html", "display": "standalone", "theme_color": "#2196F3", "background_color": "#2196F3" }
  13. Source: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis

    non erat sem Splash screen: part 1 { "name": "The Air Horner", "short_name": "Air Horner", "icons": [ { "src": "images/Airhorner_192.png", "type": "image/png", "sizes": "192x192" } ], "start_url": "index.html", "display": "standalone", "theme_color": "#2196F3", "background_color": "#2196F3" }
  14. Source: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis

    non erat sem What to Launch { "name": "The Air Horner", "short_name": "Air Horner", "icons": [ { "src": "images/Airhorner_192.png", "type": "image/png", "sizes": "192x192" } ], "start_url": "index.html?homescreen", // stats.... "display": "standalone", "theme_color": "#2196F3", "background_color": "#2196F3" }
  15. Source: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis

    non erat sem Splash screen: part 2 { "name": "The Air Horner", "short_name": "Air Horner", "icons": [ { "src": "images/Airhorner_192.png", "type": "image/png", "sizes": "192x192" } ], "start_url": "index.html", "display": "standalone", "theme_color": "#2196F3", "background_color": "#2196F3" }
  16. Source: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis

    non erat sem How to launch: standalone { "name": "The Air Horner", "short_name": "Air Horner", "icons": [ { "src": "images/Airhorner_192.png", "type": "image/png", "sizes": "192x192" } ], "start_url": "index.html", "display": "standalone", "theme_color": "#2196F3", "background_color": "#2196F3" }
  17. Source: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis

    non erat sem How to launch: window { "name": "The Air Horner", "short_name": "Air Horner", "icons": [ { "src": "images/Airhorner_192.png", "type": "image/png", "sizes": "192x192" } ], "start_url": "index.html", "display": "standalone", // window "theme_color": "#2196F3", "background_color": "#2196F3" }
  18. Source: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis

    non erat sem Tailor user interface if (window.matchMedia('(display-mode: standalone)').matches) { console.log("Thank you for installing our app!"); } @media all and (display-mode: standalone) { body { background-color: yellow; } }
  19. It’s great, but... Native apps can prompt the user to

    install... Proprietary + Confidential
  20. Prompting the user is a powerful feature Only sites the

    browser thinks the user will want to re-engage with will get the prompt
  21. Offer an offline experience Have a manifest User is engaged

    Follow Jake’s guidance. • Service Worker • HTTPS Define what to launch and how to launch it. 2 “navigations” within at least 5 minutes* * this will change
  22. Defer to a later time var deferredEvent; window.addEventListener("beforeinstallprompt", (e) =>

    { e.preventDefault(); deferredEvent = e; // show install button } ); button.addEventListener("click", (e) => deferredEvent.prompt(); );
  23. Defer to a later time var deferredEvent; window.addEventListener("beforeinstallprompt", (e) =>

    { e.preventDefault(); deferredEvent = e; // show install button } ); button.addEventListener("click", (e) => deferredEvent.prompt(); );
  24. Understand the user’s choice var deferredEvent; window.addEventListener("beforeinstallprompt", (e) => e.prompt().then((r)

    => r.userChoice) .then((c) => { if(c.choice == "installed") { // Success } else { // The user cancelled } }) );
  25. 26% increase in average spend per visit by members arriving

    via a push notification 72% increase in time spent for users visiting via a push notification +50% repeat visits within 3 months
  26. Add Project ID to the Manifest.json { "short_name": "Air Horner",

    "name": "Air Horner", "start_url": "/", "display": "standalone", "icons": [{ "src": "icons/icon-192.png", "sizes": "192x192", "type": "image/png" }], "gcm_sender_id": "123456789012345" }
  27. Add Project ID to the Manifest.json { "short_name": "Air Horner",

    "name": "Air Horner", "start_url": "/", "display": "standalone", "icons": [{ "src": "icons/icon-192.png", "sizes": "192x192", "type": "image/png" }], "gcm_sender_id": "123456789012345" }
  28. Checking for subscriptions if ('serviceWorker' in navigator) { navigator.serviceWorker.register('/service-worker.js') .then(function(reg)

    { reg.pushManager.getSubscription() .then(function(sub) { console.log('Subscription Info', sub); }); }); } else { console.log('Subscription Info', 'Not Supported'); }
  29. Checking for subscriptions if ('serviceWorker' in navigator) { navigator.serviceWorker.register('/service-worker.js') .then(function(reg)

    { reg.pushManager.getSubscription() .then(function(sub) { console.log('Subscription Info', sub); }); }); } else { console.log('Subscription Info', 'Not Supported'); }
  30. Checking for subscriptions if ('serviceWorker' in navigator) { navigator.serviceWorker.register('/service-worker.js') .then(function(reg)

    { reg.pushManager.getSubscription() .then(function(sub) { console.log('Subscription Info', sub); }); }); } else { console.log('Subscription Info', 'Not Supported'); }
  31. Requesting permission and subscribing function subscribe() { navigator.serviceWorker.getRegistration().then(function(reg) { reg.pushManager.subscribe({userVisibleOnly:

    true}) .then(function(sub) { console.log('Update Server with End Point', sub); }).catch(function(error) { console.log('Unable to subscribe user', error); }); }); }
  32. Requesting permission and subscribing function subscribe() { navigator.serviceWorker.getRegistration().then(function(reg) { reg.pushManager.subscribe({userVisibleOnly:

    true}) .then(function(sub) { console.log('Update Server with End Point', sub); }).catch(function(error) { console.log('Unable to subscribe user', error); }); }); }
  33. Requesting permission and subscribing function subscribe() { navigator.serviceWorker.getRegistration().then(function(reg) { reg.pushManager.subscribe({userVisibleOnly:

    true}) .then(function(sub) { console.log('Update Server with End Point', sub); }).catch(function(error) { console.log('Unable to subscribe user', error); }); }); }
  34. Requesting permission and subscribing function subscribe() { navigator.serviceWorker.getRegistration().then(function(reg) { reg.pushManager.subscribe({userVisibleOnly:

    true}) .then(function(sub) { console.log('Update Server with End Point', sub); }).catch(function(error) { console.log('Unable to subscribe user', error); }); }); }
  35. Source: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis

    non erat sem or check out Propel https://github.com/GoogleChrome/propel
  36. example.com // sw.js onfetch = function(e) { if(e.request.url == "app.html")

    { e.respondWith( caches.match(e.request) ); } if(e.request.url == "content.json") { // go to the network for updates, // meanwhile, use cached content fetch(...).then(function(r) { r.asJSON().then(function(json) { e.client.postMessage(json); }); }); } }; GET /app.html HTTP/1.1 HOST example.com ... GET /content.json HTTP/1.1 HOST example.com ... GET /content.json HTTP/1.1 HOST example.com ... HTTP/1.1 200 OK Date: Thu, 19 Feb 2015... ...
  37. Source: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis

    non erat sem or check out sw-toolbox & sw-precache https://github.com/GoogleChrome/sw-toolbox https://github.com/GoogleChrome/sw-precache
  38. Confidential & Proprietary • Reliable: Fast loading, offline and on

    flaky networks • Fast: Smooth animation, scrolling and nav • Engaging and integrated ◦ On the home screen, no URL bar, icons, splash ◦ Re-engaging with push notifications • Consistent experience across browsers (still in progress, though) The Progressive Web App Experience