Progressive Web
Apps
What, why and how
rizafahmi.com
Progressive Web
Apps
What is
We already Did it!
Believe it or not
Responsive, mobile first layout Cache, Localstorage, etc.
HTML5 is a new hotness
2009
HTML5
HTML5
2009 2012
Facebook
Hybrid App
HTML5 is a new hotness
HTML5 is a new Notness
HTML5 is a new Notness
"The biggest mistake we made as a
company was be!ng too much on HTML5
as opposed to na"ve.”
This guy
From Facebook
Native Apps Rules
HTML5
20122009
Facebook
Hybrid App
Mobile apps rules!
Native
Advantages
✓ Great performance
✓ Smooth anima!ons & interac!ons
✓ Instant loading & offline support
✓ Easy access through home screen
x Distribu=on problems
x Upda=ng is a pain
x Extra care with low memory phone
x Applica=on size
Native
disadvantages
Web StrikeS Back!
HTML5
20122009
Facebook
Hybrid App
Mobile apps rules!
2016
Web strikes back
Best of Both Worlds
Progressive Web Apps
What Progressive Web Apps Is
Engaging
App-like Linkable
Reliable Fast
Secure
Case Studies
63% 

users on 2G
70% 

increase on conversion
Case Studies
75% 

increase in Tweet Sent
65% 

increase in pages per session
20% 

decrease in bounce rate
Case Studies
104% 

higher conversion rate
2x

page visit
74% 
increase "me spent
Tech BehindProgressive Web Apps
Service Woker App Manifest
How ToProgressive Web Apps
Our Project✓ Single page app for local meetup videos
✓ Offline support via browser caches
✓ Modern, ES6 JavaScript syntax, no framework
✓ Mul"pla$orm, Android and iOS
✓ Na"ve app look & feel
✓ Fullscreen app
✓ Splash screen
✓ Home screen icon
Engineers.id
Our Plan✓ Design App Shell
✓ Ge!ng The Data from API
✓ Using Service Worker:
✓ Caching App Shell
✓ Caching Data
Engineers.id
✓ Na"ve-like apps:
✓ Standalone app
✓ Adding App Icons
✓ Adding Splas Screen
✓ Deploy and securing our
app
Tools
Chrome DevTools - Device Mode
✓Simulate device web experiences
✓Responsive breakpoint visualiza"on
✓First meaningful paint, metrics, etc
✓Service worker, manifest, etc
Tools
iOS Simulator
✓localhost is your machine
✓Cri"cal for tes"ng apple-specific features
✓Connect to desktop’s safari for debugging
Tools
Android Emulator
✓10.0.2.2 is your machine
✓Connect to desktop’s chrome for debugging
Tools
Lighthouse
✓Chrome extension or CLI
✓Checklist for our progressive enhancements
✓Performance sugges"ons
Designing The App Shell
Content
Applica"on Shell
Step 1
<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta http-equiv="x-ua-compatible" content="ie-edge">
<meta name="description" content="Best video resources for learn programming">
<link rel="stylesheet" href=“styles/main.css" type="text/css" media="screen"
charset="utf-8">
<link rel="stylesheet" href="styles/spinner.css" type="text/css" media="screen"
charset="utf-8">
<title>Engieers.id
</title>

</head>
<body>
<nav>
<div class="left">
<a href="/">
<h1>Engineers.id
</h1>

</a>

</div>
<div class="right">
<button onclick="location.reload()" class="btn card__btn">Refresh
</button>

</div>

</nav>
<div class=“spinner”>

...
</div>
<ul class="cards">

</ul>
<script id="card" type="text/template">
<li class="cards__item">

</li>

</script>

</body>
Test it out!
Get The Data
Step 2

// scripts/app.js
const url = 'https:
//engineers-id-backend-
ybbwzovhnl.now.sh/api/videos'
fetch(url)
.then(response 
=> response.json())
.then(json 
=> {
appendData(json)
})
Test it out!
fetch Polyfill



<!-- index.html 

-->
<html>
<head>

...
</head>
<body>



<!-- 

... 

-->
<script src="scripts/vendors/fetch.js">
</script>
<script src="scripts/app.js">
</script>

</body>

</html>
Test it out!
Zoom it out!
Naviga"on begins

(17ms)
First conten$ul paint
(600ms)
First meaningful paint
(1.58s)
Time to interac"ve
(1.8s)
Service Worker
Not really a new
technology
Parallel, not just
concurrent
Independent script
Sends message to
origin
Register Service Worker
Step 3

// scripts/app.js
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('./service-
worker.js').then(function () {
console.log('Service Worker Registered')
})
}
Test it out!
Test it out!
Cache The App Shell
Step 4

// service-worker.js
var cacheName = ‘engineers-id’
var filesToCache = [
'/',
'/index.html',
'/scripts/app.js',
‘/styles/main.css',
'/styles/spinner.css',
‘/images/logo.svg',

// 

...
]
self.addEventListener('install', function (e) {
e.waitUntil(
caches.open(cacheName).then(function (cache) {
return cache.addAll(filesToCache)
})
)
})
Cache The App Shell
Step 4
Wait, How Service Worker actually work?!
Service worker
Installing Ac"vated
Error
Idle Push
Fetch
Terminated
Web page
Caching
App Shell
Cache The Content
Step 5
Caching Strategy - Cache Only

//service-worker.js
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))
})
Caching Strategy - 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
})
Caching Strategy - Cache, failing back to network

//service-worker.js
self.addEventListener('fetch', function (event) {
event.respondWith(
caches.match(event.request).then(function (response) {
return response 
|| fetch(event.request)
})
)
})
Caching Strategy - Cache, NetworK Race

// service-worker.js
function promiseAny (promises) {
return new Promise((resolve, reject) 
=> {
promises = promises.map(p 
=> Promise.resolve(p))
promises.forEach(p 
=> p.then(resolve))
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)])
)
})
Caching Strategy - Cache Then Network

// service-worker.js
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
})
})
)
})
Caching Strategy - Network Falling Back To Cache
self.addEventListener('fetch', function (event) {
event.respondWith(
fetch(event.request).catch(function () {
return caches.match(event.request)
})
)
})
Cache The Content
Step 5

// service-worker.js
self.addEventListener('fetch', e 
=> {
e.respondWith(
caches.open(dataCacheName).then(cache 
=> {
return fetch(e.request)
.then(networkResponse 
=> {
cache.put(e.request, networkResponse.clone())
return networkResponse
})
.catch(() 
=> {
return caches.match(e.request)
})
})
)
})
When Our Users Going Rogue...
Recap
App Manifest
App Manifest
// manifest.json
{
"name": "Engineers.id",
"short_name": "eng.id",
"lang": "en-US",
"start_url": "/index.html",
"display": "standalone",
"theme_color": "#fff",
"icons": [
{
"src": "images/icons/icon-128x128.png",
"sizes": "128x128",
"type": "image/png"
},


...
],
"background_color": “#fff”,
“orientation”: “portrait”
}
App Manifest - Display Mode
‘fullscreen’
‘standalone’
‘minimal-ui’
‘browser’
App Manifest - Icons
144px by 144px
128px by 128px
192px by 192px
256px by 256px
512px by 512px
App Manifest - Home Screen Icons
48 dp icons for
home screen and task switcher
144px by 144px
192px by 192px
48px by 48px
96px by96px
App Manifest - Splash Screen Icons
128 dp icons for
splash screen
128px by 128px
256px by 256px
512px by 512px
{
"name": "Engineers.id",
"short_name": "eng.id",
"lang": "en-US",
"start_url": "/index.html",
"display": "standalone",
"theme_color": "#fff",
"icons": 
[],
"background_color": “#fff”,
“orientation”: “portrait”
}
App Manifest



<!-- index.html 

-->
<link rel="manifest" href="manifest.json">
Install Banners
{
"name": "Engineers.id",
"short_name": "eng.id",
"lang": "en-US",
"start_url": "/index.html",
"display": "standalone",
"theme_color": "#fff",
"icons": [


...
{
"src": "images/icons/icon-144x144.png",
"sizes": "144x144",
"type": "image/png"
},
],
"background_color": “#fff”,
“orientation”: “portrait”
}
Test It Out!
61
Test It Out!
62
One last thing: Deploy h%ps://zeit.co/now
Recap!
More.. More..
PaymentPush No"fica"on Connect to Hardware
THOUGHTS??!
facebook.com/rizafahmi
twi%er.com/rizafahmi22 linkedin.com/in/rizafahmi
github.com/rizafahmi
slideshare.com/rizafahmi
hack"v8.comrizafahmi.com

"Progressive Web Apps" by Riza Fahmi (Hacktiv8)

  • 1.
    Progressive Web Apps What, whyand how rizafahmi.com
  • 2.
  • 3.
    We already Didit! Believe it or not Responsive, mobile first layout Cache, Localstorage, etc.
  • 5.
    HTML5 is anew hotness 2009 HTML5
  • 6.
  • 7.
    HTML5 is anew Notness
  • 8.
    HTML5 is anew Notness "The biggest mistake we made as a company was be!ng too much on HTML5 as opposed to na"ve.” This guy From Facebook
  • 9.
  • 10.
    Native Advantages ✓ Great performance ✓Smooth anima!ons & interac!ons ✓ Instant loading & offline support ✓ Easy access through home screen
  • 11.
    x Distribu=on problems xUpda=ng is a pain x Extra care with low memory phone x Applica=on size Native disadvantages
  • 12.
    Web StrikeS Back! HTML5 20122009 Facebook HybridApp Mobile apps rules! 2016 Web strikes back
  • 13.
    Best of BothWorlds Progressive Web Apps
  • 14.
    What Progressive WebApps Is Engaging App-like Linkable Reliable Fast Secure
  • 15.
    Case Studies 63% 
 userson 2G 70% 
 increase on conversion
  • 16.
    Case Studies 75% 
 increasein Tweet Sent 65% 
 increase in pages per session 20% 
 decrease in bounce rate
  • 17.
    Case Studies 104% 
 higherconversion rate 2x
 page visit 74% 
increase "me spent
  • 18.
  • 19.
  • 20.
  • 21.
    Our Project✓ Singlepage app for local meetup videos ✓ Offline support via browser caches ✓ Modern, ES6 JavaScript syntax, no framework ✓ Mul"pla$orm, Android and iOS ✓ Na"ve app look & feel ✓ Fullscreen app ✓ Splash screen ✓ Home screen icon Engineers.id
  • 22.
    Our Plan✓ DesignApp Shell ✓ Ge!ng The Data from API ✓ Using Service Worker: ✓ Caching App Shell ✓ Caching Data Engineers.id ✓ Na"ve-like apps: ✓ Standalone app ✓ Adding App Icons ✓ Adding Splas Screen ✓ Deploy and securing our app
  • 23.
    Tools Chrome DevTools -Device Mode ✓Simulate device web experiences ✓Responsive breakpoint visualiza"on ✓First meaningful paint, metrics, etc ✓Service worker, manifest, etc
  • 24.
    Tools iOS Simulator ✓localhost isyour machine ✓Cri"cal for tes"ng apple-specific features ✓Connect to desktop’s safari for debugging
  • 25.
    Tools Android Emulator ✓10.0.2.2 isyour machine ✓Connect to desktop’s chrome for debugging
  • 26.
    Tools Lighthouse ✓Chrome extension orCLI ✓Checklist for our progressive enhancements ✓Performance sugges"ons
  • 27.
    Designing The AppShell Content Applica"on Shell Step 1
  • 28.
    <!DOCTYPE html> <html> <head> <meta charset=utf-8"> <metaname="viewport" content="width=device-width, initial-scale=1"> <meta http-equiv="x-ua-compatible" content="ie-edge"> <meta name="description" content="Best video resources for learn programming"> <link rel="stylesheet" href=“styles/main.css" type="text/css" media="screen" charset="utf-8"> <link rel="stylesheet" href="styles/spinner.css" type="text/css" media="screen" charset="utf-8"> <title>Engieers.id </title> </head>
  • 29.
    <body> <nav> <div class="left"> <a href="/"> <h1>Engineers.id </h1> </a> </div> <divclass="right"> <button onclick="location.reload()" class="btn card__btn">Refresh </button> </div> </nav> <div class=“spinner”> ... </div> <ul class="cards"> </ul> <script id="card" type="text/template"> <li class="cards__item"> </li> </script> </body>
  • 30.
  • 31.
    Get The Data Step2 // scripts/app.js const url = 'https: //engineers-id-backend- ybbwzovhnl.now.sh/api/videos' fetch(url) .then(response => response.json()) .then(json => { appendData(json) })
  • 32.
  • 33.
    fetch Polyfill <!-- index.html --> <html> <head> ... </head> <body> <!-- ... --> <script src="scripts/vendors/fetch.js"> </script> <script src="scripts/app.js"> </script> </body> </html>
  • 34.
  • 35.
    Zoom it out! Naviga"onbegins
 (17ms) First conten$ul paint (600ms) First meaningful paint (1.58s) Time to interac"ve (1.8s)
  • 36.
    Service Worker Not reallya new technology Parallel, not just concurrent Independent script Sends message to origin
  • 37.
    Register Service Worker Step3 // scripts/app.js if ('serviceWorker' in navigator) { navigator.serviceWorker.register('./service- worker.js').then(function () { console.log('Service Worker Registered') }) }
  • 38.
  • 39.
  • 40.
    Cache The AppShell Step 4 // service-worker.js var cacheName = ‘engineers-id’ var filesToCache = [ '/', '/index.html', '/scripts/app.js', ‘/styles/main.css', '/styles/spinner.css', ‘/images/logo.svg', // ... ] self.addEventListener('install', function (e) { e.waitUntil( caches.open(cacheName).then(function (cache) { return cache.addAll(filesToCache) }) ) })
  • 41.
    Cache The AppShell Step 4
  • 42.
    Wait, How ServiceWorker actually work?! Service worker Installing Ac"vated Error Idle Push Fetch Terminated Web page Caching App Shell
  • 43.
  • 44.
    Caching Strategy -Cache Only //service-worker.js 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)) })
  • 45.
    Caching Strategy -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 })
  • 46.
    Caching Strategy -Cache, failing back to network //service-worker.js self.addEventListener('fetch', function (event) { event.respondWith( caches.match(event.request).then(function (response) { return response || fetch(event.request) }) ) })
  • 47.
    Caching Strategy -Cache, NetworK Race // service-worker.js function promiseAny (promises) { return new Promise((resolve, reject) => { promises = promises.map(p => Promise.resolve(p)) promises.forEach(p => p.then(resolve)) 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)]) ) })
  • 48.
    Caching Strategy -Cache Then Network // service-worker.js 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 }) }) ) })
  • 49.
    Caching Strategy -Network Falling Back To Cache self.addEventListener('fetch', function (event) { event.respondWith( fetch(event.request).catch(function () { return caches.match(event.request) }) ) })
  • 50.
    Cache The Content Step5 // service-worker.js self.addEventListener('fetch', e => { e.respondWith( caches.open(dataCacheName).then(cache => { return fetch(e.request) .then(networkResponse => { cache.put(e.request, networkResponse.clone()) return networkResponse }) .catch(() => { return caches.match(e.request) }) }) ) })
  • 51.
    When Our UsersGoing Rogue...
  • 52.
  • 53.
  • 54.
    App Manifest // manifest.json { "name":"Engineers.id", "short_name": "eng.id", "lang": "en-US", "start_url": "/index.html", "display": "standalone", "theme_color": "#fff", "icons": [ { "src": "images/icons/icon-128x128.png", "sizes": "128x128", "type": "image/png" }, ... ], "background_color": “#fff”, “orientation”: “portrait” }
  • 55.
    App Manifest -Display Mode ‘fullscreen’ ‘standalone’ ‘minimal-ui’ ‘browser’
  • 56.
    App Manifest -Icons 144px by 144px 128px by 128px 192px by 192px 256px by 256px 512px by 512px
  • 57.
    App Manifest -Home Screen Icons 48 dp icons for home screen and task switcher 144px by 144px 192px by 192px 48px by 48px 96px by96px
  • 58.
    App Manifest -Splash Screen Icons 128 dp icons for splash screen 128px by 128px 256px by 256px 512px by 512px { "name": "Engineers.id", "short_name": "eng.id", "lang": "en-US", "start_url": "/index.html", "display": "standalone", "theme_color": "#fff", "icons": [], "background_color": “#fff”, “orientation”: “portrait” }
  • 59.
    App Manifest <!-- index.html --> <link rel="manifest" href="manifest.json">
  • 60.
    Install Banners { "name": "Engineers.id", "short_name":"eng.id", "lang": "en-US", "start_url": "/index.html", "display": "standalone", "theme_color": "#fff", "icons": [ ... { "src": "images/icons/icon-144x144.png", "sizes": "144x144", "type": "image/png" }, ], "background_color": “#fff”, “orientation”: “portrait” }
  • 61.
  • 62.
  • 63.
    One last thing:Deploy h%ps://zeit.co/now
  • 64.
  • 65.
  • 66.