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

DevIT; Thessaloniki; Progressive Web Apps: Futu...

DevIT; Thessaloniki; Progressive Web Apps: Future of web development

Slides, from the presentation performed on the DevIT conference in Thessaloniki.

Containing overview of what PWAs are, how to start working with them using ngsw (Angular solution) and/or workbox. Refers also to tools like pwabuilder.com and serwiceworke.rs

Maciej Treder

June 03, 2019
Tweet

More Decks by Maciej Treder

Other Decks in Technology

Transcript

  1. @maciejtreder • Kraków, Poland • Over 10 years of experience

    in IT, • Senior Software Development Engineer at Akamai Technologies, • Articles author • Open Source contributor (founder of @ng-toolkit)
  2. • Cambridge, Massachusetts • Content Delivery Network • Over 240

    00 servers deployed in more then 120 countries • Serves 15%-30% of the Internet traffic
  3. Single Page App • Everything on one page • Content

    loaded asynchronously (AJAX calls) • No ‘reload’ • Share-able links (# - strategy, later on HTML5 history API) Native-like experience
  4. Site is served over HTTPS Pages are responsive on tablets

    & mobile devices Site works cross-browser Page transitions don't feel like they block on the network Each page has a URL First load fast even on 3G All app URLs load while offline Metadata provided for Add to Home screen PWA checklist
  5. ServiceWorker Lifecycle let tick = 0; setInterval(() => console.log(tick++), 1000);

    0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 if ('serviceWorker' in navigator) { window.addEventListener('load', () => { navigator.serviceWorker.register('/service-worker.js'); }); } service-worker.js index.html , 11, 12, 13, 14, 15, 16, 17, 18, 19, 20
  6. ngsw-config.json • App information • What to cache • How

    to cache { “index”: “/index.html”, “appData”: { “name”: “app name”, “description”: “app description”}, “ ”: [{}], “ “: [{}] } assetGroups dataGroups
  7. assetGroups • external & internal parts of application { “assetGroups”:

    [{ “name”: “images_and_fonts”, “installMode”: “lazy”, “updateMode”: “prefetch”, “resources”: { “files”: [“/assets/img/**”], “urls”: [“https://fonts.googleapis.com/**"] } }] }
  8. ngsw-config.json • What to cache • How updates should be

    performed { “index”: “/index.html”, “appData”: { “name”: “app name”, “description”: “app description”}, “ ”: [{}], “ “: [{}] } assetGroups dataGroups
  9. dataGroups • API calls { “dataGroups”: [{ “name”: “api_calls”, “urls”:

    [“https://my-app.com/api/**”], “cacheConfig”: { “maxSize”: 10, “maxAge”: “1d”, “timeout”: “4s”, “strategy”: “freshness” } }] }
  10. Workbox importScripts('https://storage.googleapis.com/workbox-cdn/releases/4.3.1/workbox-sw.js'); if (workbox) { console.log(`Yay! Workbox is loaded `);

    } else { console.log(`Boo! Workbox didn't load `); } <script type="text/javascript"> if ('serviceWorker' in navigator) { window.addEventListener('load', () => { navigator.serviceWorker.register('/service-worker.js'); }); } </script> service-worker.js index.html
  11. Workbox-CLI module.exports = { "globDirectory": "www/", "globPatterns": [ "**/*.{html,js}" ],

    "swDest": "www/service-worker.js", "swSrc": "sw-dev.js" }; workbox-config.js (created by workbox wizard) > workbox injectManifest Using configuration from /Users/mtreder/something/workbox-config.js. The service worker was written to www/service-worker.js 2 files will be precached, totalling 808 B.
  12. Caching workbox.precaching.precacheAndRoute([ { "url": "index.html", "revision": "40f4848158e6e82701c4e9904c981449" }, { "url":

    "script.js", "revision": "85655e0fbf9a9093747877ee5270618e" } ]); workbox.precaching.precacheAndRoute([]); workbox injectManifest
  13. Use PushManager API Use importScripts section in your service-worker dev

    version importScripts: ['push-notifications.abcd1234.js'] https://developer.mozilla.org/en-US/docs/Web/API/Push_API
  14. import { SwPush } from '@angular/service-worker'; export class PushComponent implements

    OnInit { constructor(private swPush: SwPush) {} public ngOnInit(): void { this.swPush.requestSubscription({serverPublicKey: ‘key_obtained_by_push_companion'}) .then((pushSubscription: ) => { // save key on the server }); } } Register client PushSubscription
  15. interface PushSubscription { readonly expirationTime: number | null; readonly options:

    PushSubscriptionOptions; getKey(name: PushEncryptionKeyName): ArrayBuffer | null; toJSON(): PushSubscriptionJSON; unsubscribe(): Promise<boolean>; } PushSubscription readonly endpoint: string;
  16. Engage Your Customers { title: “my notification”, content: “content” }

    https://push-service.com/ subscription_endpoint https://push-service.com/ subscription_endpoint your server push service
  17. Background synchronization const bgSyncPlugin = new workbox.backgroundSync.Plugin('myQueueName', { maxRetentionTime: 24

    * 60 // Retry for max of 24 Hours }); workbox.routing.registerRoute( /\/api\/.*\/*.json/, workbox.strategies.networkOnly({ plugins: [bgSyncPlugin] }), 'POST' );
  18. manifest.json { "name": "Akamai Affinity", "short_name": "Affinity", "theme_color": "#f08b00", "background_color":

    "#363636", "display": "standalone", "scope": "/", "start_url": "/", "icons": [ { "src": "/assets/images/icons/favicon-32.png", "sizes": "32x32", "type": "image/png" } ] }
  19. Server-Side Rendering • Problem: 
 Lack of the service-worker (navigator)

    in the NodeJS environment • Solutions: • Provide mock of ServiceWorkerModule in the server entry module • ng add @ng-toolkit/pwa
  20. “Non-updatable” application • Problem: 
 Once installed app is “never”

    updated • Solutions: • Implement update mechanism using SwUpdate service • Use workbox-window SwUpdate
  21. SwUpdate • @angular/service-worker • isEnabled • available - emits an

    event when new version of an app is available • checkForUpdate() - indicates check for update & updates application
  22. SwUpdate public ngOnInit(): void { if (!isPlatformBrowser(this.platformId)) { return; }

    if (this.swUpdate.isEnabled) { this.swUpdate.available.subscribe((evt) => { setTimeout(() => {this.windowRef.nativeWindow.location.reload(true); }, 1); }); this.swUpdate.checkForUpdate().then(() => { // noop }).catch((err) => { console.error('error when checking for update', err); }); } }
  23. workbox-window import { Workbox } from 'workbox-window'; if ('serviceWorker' in

    navigator) { const wb = new Workbox('service-worker.js'); wb.addEventListener('installed', event => { if (event.isUpdate) { if (confirm(`New content is available!. Click OK to refresh`)) { window.location.reload(); } } }); }
  24. Safari Push • Apple developer membership required • Works only

    on Safari Desktop (lack of iOS support) • Written tutorial: https://apple.co/2A80fB4 • Video tutorial: https://apple.co/2S11wkJ
  25. Add to desktop (iOS) <meta name="apple-mobile-web-app-capable" content="yes"> <meta name="apple-mobile-web-app-status-bar-style" content="black">

    <meta name="apple-mobile-web-app-title" content="Angular PWA”> <link rel="apple-touch-icon" type="image/png" sizes="192x192" href="assets/icons/favicon-192.png"> <link rel="apple-touch-icon" type="image/png" sizes="144x144" href="assets/icons/favicon-144.png"> <link rel="apple-touch-icon" type="image/png" sizes="96x96" href="assets/icons/favicon-96.png"> <link rel="apple-touch-icon" type="image/png" sizes="72x72" href="assets/icons/favicon-72.png"> <link rel="apple-touch-icon" type="image/png" sizes="48x48" href="assets/icons/favicon-48.png"> <link rel="apple-touch-icon" type="image/png" sizes="32x32" href="assets/icons/favicon-32.png">
  26. PWACompat • https://github.com/GoogleChromeLabs/pwacompat • Analyze manifest.json • Brings necessary html

    tags ‘on the fly’ (like splash screen for mobile safari or pinned sites features for IE/Edge) <link rel="manifest" href="manifest.webmanifest" /> <script async src="https://cdn.jsdelivr.net/npm/[email protected]/pwacompat.min.js" integrity="sha384-uONtBTCBzHKF84F6XvyC8S0gL8HTkAPeCyBNvfLfsqHh+Kd6s/kaS4BdmNQ5ktp1" crossorigin="anonymous"></script>
  27. • 43% increase in session • 100% increase in engagement

    • 2.5 sec loading time • 9x increase in carts recovered
  28. • 65% increase in pages per session • 75% increase

    in tweets sent • 20% decrease in bounce rate
  29. • 103% increase of active users • 296% increase in

    session length • 406% increase in number of pins • 44% increase in ad revenue
  30. • Cut load time from 11.91 to 4.69 seconds •

    90% smaller than native Android app