By Andy Hill
Short answer: Fullest support in Chrome and Firefox, but coming to all platforms and browsers.
SSL is required for progressive web apps.
An exception is made for localhost.
You can create certs for SSL via Let's Encrypt
{
"name": "Informed Electorate",
"short_name": "Informed Electorate",
"start_url": ".",
"display": "standalone",
"background_color": "#000044",
"theme_color": "#8B0000",
"description": "Tools for voters to stay informed.",
"icons": [{
"src": "images/touch/informed_48x48.png",
"sizes": "48x48",
"type": "image/png"
}, {
...
"src": "images/touch/informed_512x512.png",
"sizes": "512x512",
"type": "image/png"
}],
"related_applications": [{
"platform": "web",
"url": "https://informedelectorate.net"
}]
}
A service worker is a script that your browser runs in the background, separate from a web page, opening the door to features that don't need a web page or user interaction.https://developers.google.com/web/fundamentals/primers/service-workers/
Service workers sit between the browser and the web, allowing new possibilities.
(image via https://codeburst.io/work-it-featuring-service-workers-de769bd4917b)
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js')
.then(function (registration) {
console.log('service worker registered');
}).catch(error) {
console.err('something went wrong', error);
});
}
This code is added to your standard JavaScript entry point. ServiceWorkerContainer.register() has a required first argument: a string representing the path to the service worker code, and an optional second options argument. Currently, the only available option is "scope", which indicates what range of URLs a service worker can control. The default value for scope is './'.
Install Event
const STATIC_CACHE = 'informed-cache-v1';
const urlsToCache = [
'/css/app.css',
'/js/app.js',
...
'https://fonts.googleapis.com/css?family=UnifrakturMaguntia',
];
self.addEventListener('install', function(event) {
// Perform install steps
event.waitUntil(
caches.open(STATIC_CACHE)
.then(function(cache) {
return cache.addAll(urlsToCache);
})
);
});
The install event runs once when the service worker is first loaded or modified.
This is when you cache static assets.
this.addEventListener('activate', function(event) {
const cacheWhitelist = [STATIC_CACHE];
event.waitUntil(
caches.keys().then(function(keyList) {
return Promise.all(keyList.map(function(key) {
if (cacheWhitelist.indexOf(key) === -1) {
return caches.delete(key);
}
}));
})
);
});
The activate event runs after the install event.
This is a common place to clear old caches.
self.addEventListener('fetch', function(event) {
const requestUrl = new URL(event.request.url);
if (requestUrl.pathname.startsWith('/api/')) {
event.respondWith(onlineFirstStrategy(event));
return;
}
event.respondWith(cacheFirstStrategy(event));
});
The fetch event intercepts fetches made in your JS code.
Here, you can decide what to do before going to the network.
function cacheFirstStrategy(event) {
return caches.match(event.request)
.then(function(response) {
// Cache hit - return response
if (response) {
return response;
}
var fetchRequest = event.request.clone();
return fetch(fetchRequest)
.then(function(response) {
var responseToCache = response.clone();
caches.open(STATIC_CACHE)
.then(function(cache) {
cache.put(event.request, responseToCache);
});
return response;
})
.catch(function(err) {
toast(event, 'You appear to be offline. Please check your internet connection.');
return;
});
});
}
function onlineFirstStrategy(event) {
return fetch(event.request)
.then(function(response) {
var responseToCache = response.clone();
caches.open(DYNAMIC_CACHE).then(function(cache) {
cache.put(event.request, responseToCache);
})
return response;
})
.catch(function() {
return caches.match(event.request, { cacheName: DYNAMIC_CACHE })
.then(function(match) {
if (match) {
toast(event, 'You appear to be offline. Returning result from local cache.');
return match;
} else {
toast(event, 'You appear to be offline. Please check your internet connection.');
return;
}
})
})
}
//// in service worker
self.addEventListener('message', function(event) {
if (event.data.action === 'skipWaiting') {
self.skipWaiting();
}
});
//// in js code
worker.postMessage({action: 'skipWaiting'});
//// in code
navigator.serviceWorker.addEventListener('message', event => {
indexController._sendMessage(event.data.msg);
});
//// in service worker
if (!event.clientId) return;
// Get the client.
clients.get(event.clientId)
.then(function(client) {
client.postMessage({
msg: message,
});
})
.catch(function(err) {
return;
});
https://developers.google.com/web/ilt/pwa/introduction-to-push-notificationsA notification is a message that pops up on the user's device. Notifications can be triggered locally by an open application, or they can be "pushed" from the server to the user even when the app is not running. They allow your users to opt-in to timely updates and allow you to effectively re-engage users with customized content.
Push Notifications are assembled using two APIs: the Notifications API and the Push API. The Notifications API lets the app display system notifications to the user. The Push API allows a service worker to handle Push Messages from a server, even while the app is not active.