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

Progressive Web Apps line between web and nativ...

Progressive Web Apps line between web and native apps become thinner

Over the years developers were used to thinking that the web is not user-friendly, performance efficient and powerful as native apps. But things have been changed so far; now you can build offline applications with notifications, Bluetooth and camera access and so on. Web development is great again.

Oleh Zasadnyy

April 07, 2017
Tweet

More Decks by Oleh Zasadnyy

Other Decks in Programming

Transcript

  1. <app-drawer-layout fullbleed>
 <app-drawer>
 <app-toolbar>Menu</app-toolbar>
 <iron-selector selected="[[page]]">
 <a name="view1" href="/view1">View One</a>


    ...
 </iron-selector>
 </app-drawer>
 
 <app-header-layout>
 ...
 </app-header-layout>
 </app-drawer-layout>
  2. class MyApp extends Polymer.Element { static get is() { return

    ‘my-app'; } static get properties() { return { page: { type: String, observer: '_pageChanged', }, }; } _pageChanged(page) { const resolvedPageUrl = this.resolveUrl('my-' + page + '.html'); this.importHref(resolvedPageUrl, null, this._showPage404, true); } _showPage404() { this.page = 'view404'; } }
  3. {
 "name": “GDG DevFest Ukraine 2016",
 "short_name": “GDG DevFest",
 "icons":

    [{
 "src": "images/icons/splash-icon-128.png",
 "sizes": "128x128",
 "type": "image/png"
 },{
 "src": "images/icons/splash-icon-192.png",
 "sizes": "192x192",
 "type": "image/png"
 },{
 "src": "images/icons/splash-icon-384.png",
 "sizes": "384x384",
 "type": "image/png"
 }],
 "start_url": "/",
 "display": "standalone",
 "background_color": "#607d8b",
 "theme_color": "#607d8b"
 }
  4. {
 "name": “GDG DevFest Ukraine 2016",
 "short_name": “GDG DevFest",
 "icons":

    [{
 "src": "images/icons/splash-icon-128.png",
 "sizes": "128x128",
 "type": "image/png"
 },{
 "src": "images/icons/splash-icon-192.png",
 "sizes": "192x192",
 "type": "image/png"
 },{
 "src": "images/icons/splash-icon-384.png",
 "sizes": "384x384",
 "type": "image/png"
 }],
 "start_url": "/",
 "display": "standalone",
 "background_color": "#607d8b",
 "theme_color": "#607d8b"
 }
  5. {
 "name": “GDG DevFest Ukraine 2016",
 "short_name": “GDG DevFest",
 "icons":

    [{
 "src": "images/icons/splash-icon-128.png",
 "sizes": "128x128",
 "type": "image/png"
 },{
 "src": "images/icons/splash-icon-192.png",
 "sizes": "192x192",
 "type": "image/png"
 },{
 "src": "images/icons/splash-icon-384.png",
 "sizes": "384x384",
 "type": "image/png"
 }],
 "start_url": "/",
 "display": "standalone",
 "background_color": "#607d8b",
 "theme_color": "#607d8b"
 }
  6. {
 "name": “GDG DevFest Ukraine 2016",
 "short_name": “GDG DevFest",
 "icons":

    [{
 "src": "images/icons/splash-icon-128.png",
 "sizes": "128x128",
 "type": "image/png"
 },{
 "src": "images/icons/splash-icon-192.png",
 "sizes": "192x192",
 "type": "image/png"
 },{
 "src": "images/icons/splash-icon-384.png",
 "sizes": "384x384",
 "type": "image/png"
 }],
 "start_url": "/",
 "display": "standalone",
 "background_color": "#607d8b",
 "theme_color": "#607d8b"
 }
  7. {
 "name": “GDG DevFest Ukraine 2016",
 "short_name": “GDG DevFest",
 "icons":

    [{
 "src": "images/icons/splash-icon-128.png",
 "sizes": "128x128",
 "type": "image/png"
 },{
 "src": "images/icons/splash-icon-192.png",
 "sizes": "192x192",
 "type": "image/png"
 },{
 "src": "images/icons/splash-icon-384.png",
 "sizes": "384x384",
 "type": "image/png"
 }],
 "start_url": "/",
 "display": "standalone",
 "background_color": "#607d8b",
 "theme_color": "#607d8b"
 }
  8. {
 "name": “GDG DevFest Ukraine 2016",
 "short_name": “GDG DevFest",
 "icons":

    [{
 "src": "images/icons/splash-icon-128.png",
 "sizes": "128x128",
 "type": "image/png"
 },{
 "src": "images/icons/splash-icon-192.png",
 "sizes": "192x192",
 "type": "image/png"
 },{
 "src": "images/icons/splash-icon-384.png",
 "sizes": "384x384",
 "type": "image/png"
 }],
 "start_url": "/",
 "display": "browser",
 "background_color": "#607d8b",
 "theme_color": "#607d8b"
 }
  9. {
 "name": “GDG DevFest Ukraine 2016",
 "short_name": “GDG DevFest",
 "icons":

    [{
 "src": "images/icons/splash-icon-128.png",
 "sizes": "128x128",
 "type": "image/png"
 },{
 "src": "images/icons/splash-icon-192.png",
 "sizes": "192x192",
 "type": "image/png"
 },{
 "src": "images/icons/splash-icon-384.png",
 "sizes": "384x384",
 "type": "image/png"
 }],
 "start_url": "/",
 "display": "standalone",
 "background_color": "#607d8b",
 "theme_color": "#607d8b"
 }
  10. {
 "name": “GDG DevFest Ukraine 2016",
 "short_name": “GDG DevFest",
 "icons":

    [{
 "src": "images/icons/splash-icon-128.png",
 "sizes": "128x128",
 "type": "image/png"
 },{
 "src": "images/icons/splash-icon-192.png",
 "sizes": "192x192",
 "type": "image/png"
 },{
 "src": "images/icons/splash-icon-384.png",
 "sizes": "384x384",
 "type": "image/png"
 }],
 "start_url": "/",
 "display": "browser",
 "background_color": "#607d8b",
 "theme_color": "#607d8b"
 }
  11. {
 "name": “GDG DevFest Ukraine 2016",
 "short_name": “GDG DevFest",
 "icons":

    [{
 "src": "images/icons/splash-icon-128.png",
 "sizes": "128x128",
 "type": "image/png"
 },{
 "src": "images/icons/splash-icon-192.png",
 "sizes": "192x192",
 "type": "image/png"
 },{
 "src": "images/icons/splash-icon-384.png",
 "sizes": "384x384",
 "type": "image/png"
 }],
 "start_url": "/",
 "display": "browser",
 "background_color": "#607d8b",
 "theme_color": "#607d8b"
 }
  12. navigator.serviceWorker.register('/service-worker.js') .then(reg => { console.log('Service Worker Registered', reg); }) .catch(err

    => { console.log('Error registering Service Worker', err); }); Register Service Worker
  13. const cacheName = 'app-shell-cache-v1'; const filesToCache = ['/', '/index.html', ...];

    self.addEventListener('install', event => { event.waitUntil(caches.open(cacheName) .then(cache => cache.addAll(filesToCache)) .then(() => self.skipWaiting()) ); }); Cache Files
  14. const cacheName = 'app-shell-cache-v1'; const filesToCache = ['/', '/index.html', ...];

    self.addEventListener('install', event => { event.waitUntil(caches.open(cacheName) .then(cache => cache.addAll(filesToCache)) .then(() => self.skipWaiting()) ); }); Cache Files
  15. Amsterdam, NL - Go Daddy - Chrome - 3G First

    View (3.7s) Repeat View (0.6s)
  16. self.addEventListener('activate', event => { event.waitUntil(caches.keys() .then(keyList => { return Promise.all(keyList.map(key

    => { if (key !== cacheName) { return caches.delete(key); } })); })); return self.clients.claim(); }); Remove Outdated Resources
  17. self.addEventListener('activate', event => { event.waitUntil(caches.keys() .then(keyList => { return Promise.all(keyList.map(key

    => { if (key !== cacheName) { return caches.delete(key); } })); })); return self.clients.claim(); }); Remove Outdated Resources
  18. Browser quota limits 6% of free disk space per origin

    10% of free disk space (shared across eTLD+1) at least 10% of free disk space 8GB: 10MB 32GB: 50MB 128GB: 250MB >128GB: 500MB
  19. Check if User is Subscribed Ask User To Subscribe User

    Subscribes Send Subscription Save Subscription Browser Server Subscribing Users
  20. const opts = { userVisibleOnly: true, applicationServerKey: pubKey }; navigator.serviceWorker.getRegistration()

    .then(reg => { reg.pushManager.subscribe(opts) .then(sub => { console.log('Update Server with sub obj', sub); }) .catch(error => { console.log('Unable to subscribe user', error); }); }); Subscribe the User
  21. navigator.serviceWorker.getRegistration() .then(reg => { reg.pushManager.getSubscription() .then(sub => { if (sub)

    { sub.unsubscribe(); console.log('Update our server to remove subscription'); } }); }) .catch(error => { console.log('Error while trying to unsubscribe', error); }); Unsubscribe the User
  22. Generate Message Send to End Point Send to Browser Received

    by Browser Server Browser End Point Sending Messages
  23. self.addEventListener('push', event => { let data; if (event.data) { data

    = event.data.json(); } else { // fetch data from server } self.registration.showNotification(data.title, { body: data.body, icon: data.icon, tag: data.tag }); }); Listen For Messages
  24. self.addEventListener('push', event => { let data; if (event.data) { data

    = event.data.json(); } else { // fetch data from server } self.registration.showNotification(data.title, { body: data.body, icon: data.icon, tag: data.tag }); }); Listen For Messages
  25. {
 "body": "Did you make a $1,000,000 purchase at Dr.

    Evil...",
 "icon": "images/card.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" }
 ]
 }
  26. self.registration.showNotification(data.title, {
 body: data.body,
 icon: data.icons,
 tag: data.tag,
 actions: [


    {action: 'like', title: 'Like', icon: ic_li},
 {action: 'reshare', title: 'Reshare', icon: ic_re}
 ]
 }); Add Action BuFons
  27. Respond To The User self.addEventListener('notificationclick', event => {
 if (event.action

    === 'like') {
 event.waitUntil(fetch('/like-from-notification'));
 } else if (event.action === 'reshare') {
 event.waitUntil(fetch('/reshare-from-notification'));
 } else {
 clients.openWindow(event.srcElement.location.origin);
 }
 });
  28. Responsive — 100% Secure — 100% Fast — 100% Instant

    Loading — 62.8% Push NotiCcations — 62.33% Add to Home Screen — 56.83%
  29. BROWSER BROWSER BROWSER BROWSER BROWSER 14% 10% 9% 8% 20%

    158 162 220 251 300 Q1 2013 Q1 2014 Q2 2015 Q4 2015 Q4 2016 Source (Feb 2017): http://flurrymobile.tumblr.com US Daily Mobile Time Spent
  30. Progressive web apps have limited capabilities when it comes to

    offering integration with a smartphone or tablet’s hardware features
  31. const cred = new FederatedCredential({
 id: id, 
 name: name,

    
 provider: 'https://accounts.google.com', 
 iconURL: iconUrl 
 }); 
 navigator.credentials.store(cred); Credential Management API
  32. navigator.credentials.get({
 password: true,
 federated: {
 providers: [
 'https://accounts.google.com',
 'https://www.facebook.com'
 ]


    },
 unmediated: true // `unmediated: true` lets the user automatically sign in
 }).then(cred => {
 if (cred) // auto sign-in possible
 else // auto sign-in not possible
 }); Credential Management API
  33. navigator.credentials.get().then(cred => {
 if (cred.type === 'federated'
 && cred.provider ===

    'https://accounts.google.com') {
 const auth2 = gapi.auth2.getAuthInstance();
 
 return auth2.signIn({
 login_hint: cred.id || ''
 }).then(profile => {
 ...
 });
 }
 }); Credential Management API
  34. Enable users to sign in with just one tap Remember

    the federated account the user has used to sign in with Sign users back in when a session expires
  35. const request = new PaymentRequest( methodData, // required payment method

    data details, // required information about transaction options // optional parameter for things like shipping, etc. ); Payment Request API
  36. const methodData = [{ supportedMethods: [ "basic-card" ], data: {

    supportedNetworks: [ "visa", "mastercard" ] } }]; Payment Request API
  37. const details = { displayItems: [{ label: "Original donation amount",

    amount: { currency: "USD", value : "65.00" }, // US$65.00 }, { label: "Friends and family discount", amount: { currency: "USD", value : "-10.00" }, // -US$10.00 pending: true // The price is not determined yet }], total: { label: "Total", amount: { currency: "USD", value : "55.00" }, // US$55.00 } }; Payment Request API
  38. const request = new PaymentRequest(); request.show() .then(paymentResponse => { //

    Process paymentResponse here paymentResponse.complete("success"); }) .catch(err => { console.error("Uh oh, something bad happened", err.message); }); Payment Request API
  39. Standardize the payment communication flow as much as possible Support

    different secure payment methods Integrates into existing checkout flow
  40. VR

  41. navigator.getVRDisplays().then(displays => { displays = displays.filter(display => display.capabilities.canPresent); if (displays.length

    === 0) { console.warn('No devices available able to present.'); return; } this._vr.display = displays[0]; this._vr.display.depthNear = DemoVR.CAMERA_SETTINGS.near; this._vr.display.depthFar = DemoVR.CAMERA_SETTINGS.far; }); WebVR API
  42. navigator.share({
 title: 'Web Today',
 text: 'Hello, United Dev Conf. #pwa

    #webShareAPI #webrocks',
 url: 'https://web-today.firebaseapp.com/share'
 })
 .then(() => console.log('Successful share'))
 .catch(error => console.log('Error sharing:', error)); Web Share API
  43. Credential Management API — 50.08% Payment Request API — 25.64%

    Web Bluetooth API — 43.32% WebVR API — 0% Web Share API — 0%
  44. 104% for new users across all browsers 82% increase in

    iOS conversion rate 2X more pages visited per session 74% increase in time spent per session across all browsers Live: https://aliexpress.com Source: https://developers.google.com/web/showcase/2016/aliexpress
  45. 76% higher conversions across browsers 14% more monthly active users

    on iOS, 30% on Android 4X higher interaction rate from Add to Homescreen Live: http://www.alibaba.com Source: https://developers.google.com/web/showcase/2016/alibaba
  46. 38% more conversions 40% lower bounce rate 10% longer average

    session 30% faster page load Live: https://housing.com Source: https://developers.google.com/web/showcase/2016/housing
  47. Source: https://developers.google.com/web/progressive-web-apps/checklist Site is served over HTTPS Pages are responsive

    on tablets & mobile devices The start URL (at least) loads while offline Metadata provided for Add to Home screen First load fast even on 3G Site works cross-browser Page transitions don't feel like they block on the network Each page has a URL Progressive Web App Checklist