Progressive Web Application
Neil Haddley • July 24, 2021
Building a Progressive Web Application
I created a GitHub project/repository.
The clock code is available here:
Create an Azure Static Web App
I navigated to the Azure Portal.
The clock application files have been moved
Adding the Progressive Web Application manifest and service worker
To convert the web page to a Progressive Web Application, I added a manifest and a service worker.
PWA asset generator
I used the pwa-asset-generator to generate a set of application images and icons from the Clock.jpg file.
BASH
1% npx pwa-asset-generator Clock.jpg icons
Service Worker
To have the app run offline, I defined install and fetch event handlers. The install handler copies offline.html to the browser cache. The fetch handler serves the cached file when no network connection is available.
https://developers.google.com/web/fundamentals/primers/service-workers
iOS
I installed the Progressive Web Application on an Apple mobile phone.
Android
I installed the Progressive Web Application on an Android mobile phone.
MacBook
I installed the Progressive Web Application on a MacBook.
Windows 10
I installed the Progressive Web Application on a Windows 10 laptop.
Lighthouse
I ran the Lighthouse report in Google Chrome DevTools to evaluate the Progressive Web Application.
sw.js
JAVASCRIPT
1const CACHE_NAME = 'clock-cache-v1'; 2const urlsToCache = [ 3 '/offline.html', 4]; 5const OFFLINE_URL = "offline.html"; 6 7 8self.addEventListener('install', event => { 9 event.waitUntil( 10 caches.open(CACHE_NAME) 11 .then(function (cache) { 12 console.log('Opened cache'); 13 return cache.addAll(urlsToCache); 14 }) 15 ); 16}); 17 18 19this.addEventListener('fetch', event => { 20 if (event.request.method === 'GET' && 21 event.request.headers.get('accept').includes('text/html')) { 22 event.respondWith( 23 fetch(event.request.url).catch(error => { 24 return caches.match(OFFLINE_URL); 25 }) 26 ); 27 } 28 else { 29 event.respondWith(fetch(event.request)); 30 } 31});
offline.html
HTML
1<!DOCTYPE html> 2<html lang="en-US"> 3 4<head> 5 6 <meta charset="UTF-8"> 7 8 <title>Clock</title> 9 10 <meta name="viewport" content="width=device-width, initial-scale=1"> 11 12 <meta name="apple-mobile-web-app-capable" content="yes"> 13 14 <meta name="description" content="Clock app"> 15 <style> 16 #clockContainer { 17 position: relative; 18 margin: auto; 19 height: 40vw; 20 width: 40vw; 21 22 background: url("data:image/jpg;base64,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"); 23 24 background-size: 100%; 25 } 26 27 #hour, 28 #minute, 29 #second { 30 position: absolute; 31 background: black; 32 border-radius: 10px; 33 transform-origin: bottom; 34 } 35 36 #hour { 37 width: 1.8%; 38 height: 25%; 39 top: 25%; 40 left: 48.85%; 41 opacity: 0.8; 42 } 43 44 #minute { 45 width: 1.6%; 46 height: 30%; 47 top: 19%; 48 left: 48.9%; 49 opacity: 0.8; 50 } 51 52 #second { 53 width: 1%; 54 height: 40%; 55 top: 9%; 56 left: 49.25%; 57 opacity: 0.8; 58 } 59 </style> 60 61 <script> 62 63 setInterval(() => { 64 d = new Date(); 65 hr = d.getHours(); 66 min = d.getMinutes(); 67 sec = d.getSeconds(); 68 hr_rotation = 30 * hr + min / 2; 69 min_rotation = 6 * min; 70 sec_rotation = 6 * sec; 71 72 hour.style.transform = `rotate(${hr_rotation}deg)`; 73 minute.style.transform = `rotate(${min_rotation}deg)`; 74 second.style.transform = `rotate(${sec_rotation}deg)`; 75 76 hour.style.visibility = "visible"; 77 minute.style.visibility = "visible"; 78 second.style.visibility = "visible"; 79 }, 1000); 80 81 </script> 82</head> 83 84<body> 85 86 <div id="clockContainer"> 87 <div id="hour"></div> 88 <div id="minute"></div> 89 <div id="second"></div> 90 </div> 91 92</body> 93 94</html>








































