Progressive Web App Overview

What is a PWA?

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-platform features

Web App Manifests

Service Workers

Optional Enhancements: push notifications, native share, device vibration, camera, geolocation, local database (IndexedDB), and others.

Progressive enhancement

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
  • Provide a simpler-but-still-usable experience for users of older browsers and devices with limited capabilities
  • Progresses the user experience up to a more-compelling, fully-featured experience for users of newer browsers and devices with richer capabilities

Why PWA?

Upsides

Offline mode

Improved performance

App store independent

We can build it on top of an existing web application

Downsides

Compatibility limitation (iOS)

Can't do everything

Users are not used to it

PWA Examples

app.starbucks.com

bmw.com

twitter.com

pinterest.com

2048game.com

Web-platform features

Web App Manifests

Service Workers

Web App Manifests

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

Web App Manifests

Let's do some coding

Web-platform features

Web App Manifests

Service Workers

Service Workers

Service Worker is a type of Web Worker that acts intercepting network requests, handling caches, and delivering push messages.

Web Workers

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

Web Workers

Web Workers - Generate PI

Generate Pi - examples/01-web-workers

Web Workers - Generate PI

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
							})
						})
						

Web Workers - Generate PI

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?

Web Workers - Data Analysis

Data Analysis - examples/02-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"
								

Web Workers - Data Analysis

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 = ''
							})
						})
						

Web Workers - Data Analysis

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);
						})
						

With Web Workers...

We can

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

We cannot access the DOM

Web Workers - Recommended links

  • When should you be using Web Workers? - Article
  • Comlink - Library that removes the mental barrier of thinking aboutĀ postMessageĀ and hides the fact that you are working with workers.
  • Partytown - Library that relocate resource intensive third-party scripts off of the main thread and into a web worker.
  • neo.mjs - Web Worker driven frontend framework to create multithreaded Web Apps

Service Workers

What makes Service Workers special?

Service Workers

Service Workers

Let's do some coding

What next?

Refreshing the cache

Changing the UI when is offline

Background Syncing with IndexedDB

Authentication support

Service Workers - Recommended links

Wrapping up

What is a PWA

Web App Manifest

Web Workers

Service Workers

Caching