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

A year with progressive web applications! #DevConMU

A year with progressive web applications! #DevConMU

About a year ago we started using Progressive web apps. The idea behind PWA is to replace native mobile apps.

In this talk, I will show you what is possible to build with PWA, what is the limitation and what we learn. And of course, you will see where PWA can be a good fit for your projects. Progressive Web Apps use modern web capabilities to deliver an app-like user experience.

They evolve from pages in browser tabs to immersive, top-level apps, maintaining the web's low friction at every moment. They are reliable, fast, engaging and delivering amazing UX to end users. And they are here!

Antonio Peric-Mazar

April 11, 2019
Tweet

More Decks by Antonio Peric-Mazar

Other Decks in Programming

Transcript

  1. A year with progressive
    web apps!
    Antonio Perić-Mažar, Locastic

    11.04.2019., #DevConMu

    View full-size slide

  2. Antonio Perić-Mažar
    CEO @ Locastic
    Co-Founder @ Blockada
    [email protected]
    @antonioperic

    View full-size slide

  3. Locastic
    • We help clients create amazing web and mobile apps (since 2011)
    • mobile development
    • web development
    • UX/UI
    • Training and Consulting
    • Shift Conference, Symfony Croatia
    • www.locastic.com t: @locastic

    View full-size slide

  4. USERS.
    CUSTOMERS.

    View full-size slide

  5. [Mobile] User
    Experience on Web

    View full-size slide

  6. And trust me… you will never had enough cheese

    View full-size slide

  7. “I don't care how many kick-ass Visio architecture
    diagrams you have; as far as the user is concerned,
    the UI is the application. I know UI US HARD, but you
    have to build an impressive UI if you want to be taken
    seriously. Give your UI the high priority it deserves.”
    Jeff Atwood, Coding Horror
    blog

    View full-size slide

  8. Capebility
    Reach

    View full-size slide

  9. “A Progressive Web App uses
    modern web capabilities to deliver
    an app-like user experience.”

    View full-size slide

  10. “A Progressive Web App uses
    modern web capabilities to deliver
    an app-like user experience.”

    View full-size slide

  11. PWA is:
    • Progressive - Works for every user, regardless of browser choice because
    it's built with progressive enhancement as a core tenet.
    • Responsive - Fits any form factor: desktop, mobile, tablet, or whatever is
    next.
    • Connectivity independent - Enhanced with service workers to work
    offline or on low-quality networks.
    • App-like - Feels like an app, because the app shell model separates the
    application functionality from application content .
    • Fresh - Always up-to-date thanks to the service worker update process.

    View full-size slide

  12. PWA is:
    • Safe - Served via HTTPS to prevent snooping and to ensure content hasn't
    been tampered with.
    • Discoverable - Is identifiable as an "application" thanks to W3C manifest
    and service worker registration scope, allowing search engines to find it.
    • Re-engageable - Makes re-engagement easy through features like push
    notifications.
    • Installable - Allows users to add apps they find most useful to their home
    screen without the hassle of an app store.
    • Linkable - Easily share the application via URL, does not require complex
    installation.

    View full-size slide

  13. Fast Integrated
    Reliable Engaging
    4
    things to focus on

    View full-size slide

  14. Fast Integrated
    Reliable Engaging

    View full-size slide

  15. Fast
    • No junky scrolling
    • No slow load performance
    • Measure and improve all the time
    • Bad connection (or no connection) is not excuse

    View full-size slide

  16. 53%
    of users abandon
    sites that take longer
    than 3 seconds to load

    View full-size slide

  17. https://cloudfour.com/thinks/ios-doesnt-support-progressive-web-apps-so-what/

    View full-size slide

  18. App shell model
    • Reliable performance that is consistently fast
    • Native-like interactions
    • Economical use of data

    View full-size slide

  19. Fast Integrated
    Reliable Engaging

    View full-size slide

  20. Integrated
    • User should not reach browser to reach your app
    • They should be able to interact same as with any
    other app on their device
    • They expect to have all possibilities as other apps
    • Users should be able to start app from their home
    screen

    View full-size slide

  21. https://www.mobigyaan.com/android-8-0-oreo-vs-ios-11-which-is-better

    View full-size slide

  22. https://techcrunch.com/2017/08/25/majority-of-u-s-consumers-still-download-zero-apps-per-month-says-comscore/

    View full-size slide

  23. https://techcrunch.com/2017/08/25/majority-of-u-s-consumers-still-download-zero-apps-per-month-says-comscore/

    View full-size slide

  24. https://www.technology.org/2017/07/28/progressive-web-apps-vs-native-which-is-better-for-your-business/

    View full-size slide

  25. 80%
    users intentionally moved
    apps to their home screen

    View full-size slide

  26. Broken experience
    • Required user interaction
    • Where it will start?
    • Would it work offline?
    https://medium.com/@saigeleslie/how-to-create-a-progressive-web-app-with-react-in-5-mins-or-less-3aae3fe98902

    View full-size slide

  27. Web manifest
    • Simple JSON file
    • Tell browsers about your app and how it should behave
    once app is ‘installed’
    • Having manifest is required to show add to home screen
    pop-up
    • Works for desktop and mobile apps (chrome)
    • https://manifest-validator.appspot.com/
    • https://app-manifest.firebaseapp.com/

    View full-size slide

  28. {
    "background_color":"#ffffff",
    "display":"standalone",
    "icons":[

    ],
    "name":"Twitter",
    "short_name":"Twitter",
    "start_url":"/",
    "theme_color":"#ffffff",
    “scope":"/"
    }

    You can add it now to 

    your app

    View full-size slide

  29. https://ponyfoo.com/articles/progressive-app-serviceworker

    View full-size slide

  30. 40%
    higher interaction rate from
    Home screen

    View full-size slide

  31. Web Payment API!

    View full-size slide

  32. 66%
    of purchases on mobile are
    on the web

    View full-size slide

  33. 1/3
    of purchases on Web are
    via mobile
    That means the UX

    is BROKEN!

    View full-size slide

  34. Checkout forms today:
    • Manual
    • Tedious
    • Slow
    • N-taps
    http://www.alyssatucker.com/improving-hollars-ios-checkout-process/

    View full-size slide

  35. Autofill - step forward
    • Autocomplete attributes
    • 30% faster
    • Automatic
    • Simple
    • Slow
    • n-taps
    http://www.alyssatucker.com/improving-hollars-ios-checkout-process/

    View full-size slide

  36. Web Payment API!

    View full-size slide

  37. Web Payment API!
    • Automatic
    • Simple
    • Fast
    • 1-tap
    • Supportes payment gatway,

    or some applications payment 

    (Google Pay)
    https://paymentrequest.show/images/pr-woocommerce.gif

    View full-size slide

  38. More APIs
    • Media Session
    • Media Capture API
    • Casting support
    • Web bluetooth
    • Web Share

    View full-size slide

  39. More APIs
    • Media Session
    • Media Capture API
    • Casting support
    • Web bluetooth
    • Web Share

    View full-size slide

  40. Remember
    users expect to be able to
    interact with your app in the
    same way that they do all of
    the other apps on their device

    View full-size slide

  41. Fast Integrated
    Reliable Engaging

    View full-size slide

  42. Reliable
    Reliability means, never showing the Downsaur

    View full-size slide

  43. Reliable
    • The quality of a network connection can be affected by a number of factors such as:
    • Poor coverage of a provider.
    • Extreme weather conditions.
    • Power outages.
    • Users travelling into “dead zones” such as buildings that block their network
    connections.
    • Travelling on a train and going through a tunnel.
    • Internet connection is managed by a third party and time boxed when it will be
    active or inactive like in an airport or hotel.
    • Cultural practises that require limited or no internet access at specific times or
    days.

    View full-size slide

  44. Reliable
    • We need instant loading offline
    • 60% of mobile connection is 2G
    • Fast Application is UX
    • 14 sec to load average website on 4g
    • 19 sec to load average website on 3G

    View full-size slide

  45. Service Workers

    View full-size slide

  46. Service Workers

    View full-size slide

  47. Service Workers
    Service Workers

    View full-size slide

  48. Service Workers
    • Rich offline experiences
    • Background syncs
    • Push notifications
    • …

    View full-size slide

  49. Service Workers
    • Script that browser runs in background, separated
    from web page
    • It is Javascript worker, so it cannot access to DOM
    directly
    • Service worker is a programmable network proxy,
    allowing you to control how network requests from
    your page are handled.

    View full-size slide

  50. Service Workers
    • It's terminated when not in use, and restarted when
    it's next needed, so you cannot rely on global state
    within a service worker's onfetch and
    onmessage handlers.
    • Service workers make extensive use of promises
    • Service worker is for second load

    View full-size slide

  51. Service Workers lifecycle

    View full-size slide

  52. Register a Service Worker
    if ('serviceWorker' in navigator) {
    window.addEventListener('load', function() {
    navigator.serviceWorker.register('/sw.js').then(function(registration)
    {
    // Registration was successful
    console.log('ServiceWorker registration successful with scope: ',
    registration.scope);
    }, function(err) {
    // registration failed :(
    console.log('ServiceWorker registration failed: ', err);
    });
    });
    }

    View full-size slide

  53. Service workers Events
    • On install - as a dependency
    • On install - not as a dependency
    • On activate
    • On user interaction
    • On network response
    • Stale-while-revalidate
    • On push message
    • On background-sync

    View full-size slide

  54. On install - as a dependency

    View full-size slide

  55. On install - as a dependency
    self.addEventListener('install', function(event) {
    event.waitUntil(
    caches.open('mysite-static-v3').then(function(cache) {
    return cache.addAll([
    '/css/whatever-v3.css',
    '/css/imgs/sprites-v6.png',
    '/css/fonts/whatever-v8.woff',
    '/js/all-min-v4.js'
    // etc
    ]);
    })
    );
    });

    View full-size slide

  56. On install - not as a dependency

    View full-size slide

  57. On install - not as a dependency
    self.addEventListener('install', function(event) {
    event.waitUntil(
    caches.open('mygame-core-v1').then(function(cache) {
    cache.addAll(
    // levels 11-20
    );
    return cache.addAll(
    // core assets & levels 1-10
    );
    })
    );
    });

    View full-size slide

  58. Update Service Worker
    • Update your service worker JavaScript file. When the user navigates to your
    site, the browser tries to redownload the script file that defined the service
    worker in the background. If there is even a byte's difference in the service
    worker file compared to what it currently has, it considers it new.
    • Your new service worker will be started and the install event will be fired.
    • At this point the old service worker is still controlling the current pages so the
    new service worker will enter a waiting state.
    • When the currently open pages of your site are closed, the old service
    worker will be killed and the new service worker will take control.
    • Once your new service worker takes control, its activate event will be
    fired.

    View full-size slide

  59. self.addEventListener('activate', function(event) {
    event.waitUntil(
    caches.keys().then(function(cacheNames) {
    return Promise.all(
    cacheNames.filter(function(cacheName) {
    // Return true if you want to remove this cache,
    // but remember that caches are shared across
    // the whole origin
    }).map(function(cacheName) {
    return caches.delete(cacheName);
    })
    );
    })
    );
    });
    On Activate

    View full-size slide

  60. Update Service Worker
    • self.skipWaiting()
    • Skips waiting for refresh to start using new SW

    View full-size slide

  61. On User Interaction

    View full-size slide

  62. // on user interaction
    document.querySelector('.cache-article').addEventListener('click',
    function(event) {
    event.preventDefault();
    var id = this.dataset.articleId;
    caches.open('mysite-article-' + id).then(function(cache) {
    fetch('/get-article-urls?id=' + id).then(function(response) {
    // /get-article-urls returns a JSON-encoded array of
    // resource URLs that a given article depends on
    return response.json();
    }).then(function(urls) {
    cache.addAll(urls);
    });
    });
    });

    View full-size slide

  63. On Network response

    View full-size slide

  64. self.addEventListener('fetch', function(event) {
    event.respondWith(
    caches.open('mysite-dynamic').then(function(cache) {
    return cache.match(event.request).then(function (response) {
    return response || fetch(event.request).then(function(response) {
    cache.put(event.request, response.clone());
    return response;
    });
    });
    })
    );
    });

    View full-size slide

  65. State-while-revalidate

    View full-size slide

  66. State-while-revalidate
    self.addEventListener('fetch', function(event) {
    event.respondWith(
    caches.open('mysite-dynamic').then(function(cache) {
    return cache.match(event.request).then(function(response) {
    var fetchPromise =
    fetch(event.request).then(function(networkResponse) {
    cache.put(event.request, networkResponse.clone());
    return networkResponse;
    })
    return response || fetchPromise;
    })
    })
    );
    });

    View full-size slide

  67. On Background Sync

    View full-size slide

  68. self.addEventListener('sync', function(event) {
    if (event.id == 'update-leaderboard') {
    event.waitUntil(
    caches.open('mygame-dynamic').then(function(cache) {
    return cache.add('/leaderboard.json');
    })
    );
    }
    });

    View full-size slide

  69. SW: Serving suggestions - responding to
    requests
    • Cache only
    • Network only
    • Cache, falling back to network
    • Cache & network race
    • Network falling back to cache
    • Cache then network
    • Generic fallback

    View full-size slide

  70. Cache only
    self.addEventListener('fetch', function(event) {
    // If a match isn't found in the cache, the response
    // will look like a connection error
    event.respondWith(caches.match(event.request));
    });

    View full-size slide

  71. Network only

    View full-size slide

  72. Network only
    self.addEventListener('fetch', function(event) {
    event.respondWith(fetch(event.request));
    // or simply don't call event.respondWith, which
    // will result in default browser behaviour
    });

    View full-size slide

  73. Cache, falling back to network

    View full-size slide

  74. Cache, falling back to network
    self.addEventListener('fetch', function(event) {
    event.respondWith(
    caches.match(event.request).then(function(response) {
    return response || fetch(event.request);
    })
    );
    });

    View full-size slide

  75. Cache & network race

    View full-size slide

  76. Cache & network race
    // Promise.race is no good to us because it rejects if
    // a promise rejects before fulfilling. Let's make a proper
    // race function:
    function promiseAny(promises) {
    return new Promise((resolve, reject) => {
    // make sure promises are all promises
    promises = promises.map(p => Promise.resolve(p));
    // resolve this promise as soon as one resolves
    promises.forEach(p => p.then(resolve));
    // reject if all promises reject
    promises.reduce((a, b) => a.catch(() => b))
    .catch(() => reject(Error("All failed")));
    });
    };
    self.addEventListener('fetch', function(event) {
    event.respondWith(
    promiseAny([
    caches.match(event.request),
    fetch(event.request)
    ])

    View full-size slide

  77. Network, falling back to Cache

    View full-size slide

  78. Network, falling back to Cache
    self.addEventListener('fetch', function(event) {
    event.respondWith(
    fetch(event.request).catch(function() {
    return caches.match(event.request);
    })
    );
    });

    View full-size slide

  79. Cache, than Network

    View full-size slide

  80. Cache, than Network
    var networkDataReceived = false;
    startSpinner();
    // fetch fresh data
    var networkUpdate = fetch('/data.json').then(function(response) {
    return response.json();
    }).then(function(data) {
    networkDataReceived = true;
    updatePage(data);
    });
    // fetch cached data
    caches.match('/data.json').then(function(response) {
    if (!response) throw Error("No data");
    return response.json();
    }).then(function(data) {
    // don't overwrite newer network data
    if (!networkDataReceived) {
    updatePage(data);
    }
    }).catch(function() {
    // we didn't get cached data, the network is our last hope:
    return networkUpdate;
    }).catch(showErrorMessage).then(stopSpinner);

    View full-size slide

  81. Cache, than Network
    self.addEventListener('fetch', function(event) {
    event.respondWith(
    caches.open('mysite-dynamic').then(function(cache) {
    return fetch(event.request).then(function(response) {
    cache.put(event.request, response.clone());
    return response;
    });
    })
    );
    });

    View full-size slide

  82. Generic fallback

    View full-size slide

  83. Generic fallback
    self.addEventListener('fetch', function(event) {
    event.respondWith(
    // Try the cache
    caches.match(event.request).then(function(response) {
    // Fall back to network
    return response || fetch(event.request);
    }).catch(function() {
    // If both fail, show a generic fallback:
    return caches.match('/offline.html');
    // However, in reality you'd have many different
    // fallbacks, depending on URL & headers.
    // Eg, a fallback silhouette image for avatars.
    })
    );
    });

    View full-size slide

  84. Reliable conclusion
    • Think how you design for the success, failure and instability of
    a network connection
    • Data may be expensive, so be considerate to the user
    • Make sure performance is part of your design process and UX
    • Try to provide offline by default if your app doesn't require
    much data
    • Inform users of their current state and of changes in states
    • https://serviceworke.rs/ - different examples

    View full-size slide

  85. https://serviceworke.rs/ - different examples

    View full-size slide

  86. Your goal is to provide a
    good experience that
    lessens the impact of
    changes in connectivity

    View full-size slide

  87. Fast Integrated
    Reliable Engaging

    View full-size slide

  88. Engaging
    • Charming and Attractive
    • Shift way how we think in patterns and designs
    from web patterns to some native patterns
    • Push notifications (browser doesn’t need to be
    opened)
    • Push Notification API

    View full-size slide

  89. What makes good notification
    Push. Push. Back upon now. (Courtesy Enrique Iglesias)

    View full-size slide

  90. What makes good notification
    • Timely - I feel I need and it matters now
    • Precise - specific info, what happens
    • Personal - make it personal

    View full-size slide

  91. What makes good notification

    View full-size slide

  92. https://www.slideshare.net/SeulgiChoi4/pwa-push-notification

    View full-size slide

  93. https://www.slideshare.net/SeulgiChoi4/pwa-push-notification

    View full-size slide

  94. self.addEventListener('push', function(event) {
    if (event.data.text() == 'new-email') {
    event.waitUntil(
    caches.open('mysite-dynamic').then(function(cache) {
    return fetch('/inbox.json').then(function(response) {
    cache.put('/inbox.json', response.clone());
    return response.json();
    });
    }).then(function(emails) {
    registration.showNotification("New email", {
    body: "From " + emails[0].from.name
    tag: "new-email"
    });
    })
    );
    }
    });
    self.addEventListener('notificationclick', function(event) {
    if (event.notification.tag == 'new-email') {
    // Assume that all of the resources needed to render
    // /inbox/ have previously been cached, e.g. as part
    // of the install handler.
    new WindowClient('/inbox/');
    }
    });

    View full-size slide

  95. Push Notifications
    {
    "body": "Did you make a $1,000,000 purchase at Dr. Evil...",
    "icon": "images/ccard.png",
    "vibrate": [200, 100, 200, 100, 200, 100, 400],
    "tag": "request",
    "actions": [
    { "action": "yes", "title": "Yes", "icon": "images/yes.png" },
    { "action": "no", "title": "No", "icon": "images/no.png" }
    ]
    }

    View full-size slide

  96. 43%
    agrees to an app’s request to
    allow push notifications

    View full-size slide

  97. http://mobilehtml5.org/
    http://mobilehtml5.org/ts/
    http://mobilehtml5.org/tests/
    http://mobilehtml5.org/tests/

    te

    http://mobilehtml5.org/tests/

    t

    http://mobilehtml5.org/tests/

    View full-size slide

  98. Some show case!

    View full-size slide

  99. 65% increase in page per session
    75% increase in Tweets sent

    20% decrease in bounce rate
    Twitter Lite

    View full-size slide

  100. 76% higher conversation across browsers
    4x higher interaction rate from Add to screen
    Increased usage of native apps also
    Alibaba

    View full-size slide

  101. 80%+ conversation
    54% smaller than Android app
    120% smaller than iOS app
    BookMyShow

    View full-size slide

  102. Cut load times from 11.91 to 4.69
    seconds
    90% smaller than Native Android
    App
    https://medium.com/@addyosmani/a-tinder-progressive-web-app-
    performance-case-study-78919d98ece0
    Tinder

    View full-size slide

  103. Myths about PWA!

    View full-size slide

  104. PWAs are only for
    offline apps

    View full-size slide

  105. PWAs are a mobile thing

    View full-size slide

  106. PWAs are a Google-only
    thing

    View full-size slide

  107. PWAs are not ready yet

    View full-size slide

  108. https://pwa.rocks/

    View full-size slide

  109. Don’t be driven with hype,
    choose the best fit for your
    project

    View full-size slide

  110. It is not about PWA vs
    Native apps, it is about
    Users

    View full-size slide

  111. [email protected]
    @antonioperic
    www.locastic.com
    Questions?

    View full-size slide