Progressive Web Apps are web apps that use specific web-platform features in combination with progressive enhancement to give users an experience on par with native apps.
Web App Manifests
Service Workers
Optional Enhancements: push notifications, native share, device vibration, camera, geolocation, local database (IndexedDB), and others.
Progressive enhancement is a design philosophy that provides a baseline of essential content and functionality to as many users as possible, while delivering the best possible experience only to users of the most modern browsers that can run all the required code. - MDN
Offline mode
Improved performance
App store independent
We can build it on top of an existing web application
Compatibility limitation (iOS)
Can't do everything
Users are not used to it
Web App Manifests
Service Workers
The web app manifest provides information about a web application in a JSON text file, necessary for the web app to be downloaded and be presented to the user similarly to a native app - MDN
Let's do some coding
Web App Manifests
Service Workers
Service Worker is a type of Web Worker that acts intercepting network requests, handling caches, and delivering push messages.
Web Workers are a simple means for web content to run scripts in background threads. The worker thread can perform tasks without interfering with the user interface. - MDN
script.js
// Spin the jollies
document.getElementById('btn-rotate').addEventListener('click', (e) => {
e.target.setAttribute('disabled', true)
let rotation = 0
setInterval(() => {
document.getElementById('jolly').style.transform = `rotate(${rotation += 2}deg)`
}, 10)
})
// Generate Pi
const pi = (n) => { /* generate pi */ }
// Attatch the regular event listener
document.getElementById('btn-main').addEventListener('click', () => {
document.getElementById('result').textContent = 'working...'
document.getElementById('result').textContent = pi(30000)
})
// Initialize the Worker
const piWorker = new Worker('pi-worker.js')
// Attatch the event listener for using the worker
document.getElementById('btn-worker').addEventListener('click', () => {
document.getElementById('result').textContent = 'working...'
piWorker.postMessage(30000)
piWorker.addEventListener('message', (e) => {
document.getElementById('result').textContent = e.data
})
})
pi-worker.js
// Generate Pi
const pi = (n) => { /* generate pi */ }
// Listen for message
self.addEventListener("message", (e) => {
const result = pi(e.data);
self.postMessage(result);
})
But...
Who wants to calculate PI?
What is a realistic use case for Web Workers?
orders.csv
"id","total","created_at"
"42e7c2c0-b5a7-4ec7-a406-aef94b78fae1",41.95,"2020-01-05T07:28:54.703Z"
"b54d8c4f-a1f7-4079-a142-b0908bb26bf7",778.34,"2020-12-20T23:02:54.434Z"
"0f5538b7-4ab8-405d-ad88-218ecfd9143a",116.62,"2020-05-21T14:28:10.448Z"
"f7f3d8f8-129e-4d56-9095-a7f864d783d6",559.8,"2020-10-31T01:05:23.160Z"
"90901bd1-d02a-4c9c-b257-450edaae215b",160.48,"2021-01-30T00:59:50.798Z"
"28473453-205a-4b35-9d7b-2efe0781dc5d",513.36,"2020-08-10T07:53:47.837Z"
"c5b4b4e9-be3f-40aa-91d6-fa67e7bf3b2e",94.76,"2020-09-06T09:28:25.774Z"
"1480e2ad-0c39-41db-9413-b146f3ceba1a",59.7,"2020-08-12T21:12:36.028Z"
"d2dc6d6f-38b3-4c0a-8f3d-b397deda65af",612.44,"2020-05-18T11:09:42.958Z"
"66a54672-3b44-41ab-8bc1-72c22146ee34",576.1,"2021-12-11T10:27:03.245Z"
script.js
// Spin the jollies
document.getElementById('btn-rotate').addEventListener('click', (e) => {
e.target.setAttribute('disabled', true)
let rotation = 0
setInterval(() => {
document.getElementById('jolly').style.transform = `rotate(${rotation += 2}deg)`
}, 10)
})
// File Field event listener
let file = ''
document.getElementById('file-input').addEventListener('change', (e) => {
file = e.target.files[0]
document.getElementById('file-label').innerText = file.name
})
// Generate report function
const generateReportFromCSV = async (file) => {/* read file and generate report */}
// Attatch the regular event listener
document.getElementById('btn-main').addEventListener('click', async () => {
document.getElementById('status').textContent = 'working...'
const result = await generateReportFromCSV(file)
renderChart(result)
document.getElementById('status').textContent = ''
})
// Initialize the Worker
const csvReportWorker = new Worker('csv-report-worker.js')
// Attatch the event listener for using the worker
document.getElementById('btn-worker').addEventListener('click', (e) => {
document.getElementById('status').textContent = 'working...'
csvReportWorker.postMessage({ file: file })
csvReportWorker.addEventListener('message', (e) => {
const result = e.data
renderChart(result)
document.getElementById('status').textContent = ''
})
})
csv-report-worker.js
// Generate report
const generateReportFromCSV = async (file) => {/* read file and generate report */}
// Listen for message
self.addEventListener("message", async (e) => {
const result = await generateReportFromCSV(e.data);
self.postMessage(result);
})
We can do compute intensive tasks
We can do AJAX calls
We can use WebSocket
We can read and parse files
and more...
We cannot access the DOM
What makes Service Workers special?
Let's do some coding
Refreshing the cache
Changing the UI when is offline
Background Syncing with IndexedDB
Authentication support
What is a PWA
Web App Manifest
Web Workers
Service Workers
Caching