Nico Martin // nicomartin.ch
Nico Martin // nicomartin.ch
Nico Martin
Web- / Frontend Developer
@nic_o_martin
nicomartin.ch
Say Hello GmbH
@SayHelloGmbH
sayhello.ch
Nico Martin // nicomartin.ch
Nico Martin // nicomartin.ch
a website
+
Nico Martin // nicomartin.ch
installable
has to be installed
can't be installed
open
full access to the device
no unique identifier
works offline
app store
instant updates
online only
growing number APIs
linkable
Nico Martin // nicomartin.ch
.../application.js
.../theme.js
.../plugin.js
serviceworker.js
Nico Martin // nicomartin.ch
if (!'serviceWorker' in navigator){
console.log('serviceworker is not supported');
} else if (navigator.serviceWorker.controller) {
console.log('serviceworker already registered');
} else {
navigator.serviceWorker.register('/serviceworker.js', {
scope: '/'
}).then(function (reg) {
console.log('serviceworker registered');
});
}
application.js
needs to be https
empty serviceworker.js in the root folder
Nico Martin // nicomartin.ch
<head>
...
<link rel="manifest" href="/manifest.json">
</head>
{
name: "Meine App",
short_name: "MeineApp",
start_url: "./",
description: "Meine erste Progressive Web App",
theme_color: "#1d1d1b",
background_color: "#ffffff",
display: "standalone",
lang: "",
orientation: "any",
icons: [
{
src: "/path/to/app-icon-512x512.png",
type: "image/png",
sizes: "512x512"
}
],
}
manifest.json
Nico Martin // nicomartin.ch
Nico Martin // nicomartin.ch
ERR_INTERNET_DISCONNECTED
Nico Martin // nicomartin.ch
application Storage
service worker
Nico Martin // nicomartin.ch
offline Storage
service worker
Nico Martin // nicomartin.ch
const version = '20171127';
const offlinePage = 'offline/';
const staticCachePages = [
'/',
'/' + offlinePage
];
const key = 'HelloTheme';
const staticCacheName = `${key}-Static-${version}`;
self.addEventListener('install', event => {
// install offline pages
event.waitUntil(
caches.open(staticCacheName)
.then(cache => {
return cache.addAll(staticCachePages);
})
.then(function () {
return self.skipWaiting();
})
);
});
self.addEventListener('activate', event => {
event.waitUntil(
caches.keys()
.then(keys => {
return Promise.all(
keys.map(key => {
if (key !== staticCacheName) {
return caches.delete(key);
}
})
);
})
);
});
self.addEventListener('fetch', event => {
let request = event.request;
let url = new URL(request.url);
// only deal with requests on the same domain.
if (url.origin !== location.origin) {
return;
}
//don't do anything if wp stuff
if (request.url.match(/wp-admin/) || request.url.match(/preview=true/)) {
return;
}
// If non-GET request, try the network first, fall back to the offline page
if (request.method !== 'GET') {
event.respondWith(
fetch(request)
.catch(error => {
return caches.match(offlinePage);
})
);
return;
}
if (request.mode === 'navigate' || request.mode === 'cors') {
// if HTML requests, try the network first (and update cache), fall back to the cache, finally the offline page
event.respondWith(
fetch(request)
.then(response => {
addToCache(request);
return response;
})
.catch(error => {
return caches.match(request)
.then(response => {
return response || caches.match(offlinePage);
});
})
);
} else {
// if non-HTML request, look in the cache first, fall back to the network
event.respondWith(
caches.match(request)
.then(response => {
if (response) {
return response;
} else {
addToCache(request);
return fetch(request);
}
})
);
}
const addToCache = function (request) {
return caches.open(staticCacheName)
.then(cache => {
return fetch(request)
.then(response => {
return cache.put(request, response);
});
});
};
});
serviceworker.js
Nico Martin // nicomartin.ch
Nico Martin // nicomartin.ch
Nico Martin // nicomartin.ch
const pushLatestPush = self.registration.scope + 'wp-content/latest_push.json';
self.addEventListener('push', event => {
event.waitUntil(
// pushManager
registration.pushManager.getSubscription()
.then(function (subscription) {
// get latest data
return fetch(pushLatestPush)
.then(function (response) {
return response.json()
.then(function (data) {
// show Notification
return self.registration.showNotification(data.title, {
body: data.body,
badge: data.badge,
icon: data.icon,
image: data.image
});
})
})
})
);
});
self.addEventListener('notificationclick', event => {
const notification = event.notification;
const action = event.action;
if (action === 'close') {
notification.close();
} else {
event.waitUntil(
fetch(pushLatestPush)
.then(function (response) {
return response.json()
.then(function (data) {
if ('' !== data.redirect) {
clients.openWindow(data.redirect);
}
notification.close();
})
})
.catch(function (err) {
notification.close();
})
);
}
});
serviceworker.js
Nico Martin // nicomartin.ch
multi page application single page application
built on the server built on the client
Nico Martin // nicomartin.ch
Nico Martin // nicomartin.ch
Nico Martin // nicomartin.ch
Nico Martin // nicomartin.ch
app.skateparkguide.ch
api.skateparkguide.ch
WP REST API
service worker
Nico Martin // nicomartin.ch
Nico Martin // nicomartin.ch
Nico Martin // nicomartin.ch
in Development (since August 2017)
in Development
https://developer.microsoft.com/en-us/microsoft-edge/platform/status/serviceworker/
Nico Martin // nicomartin.ch