Alan Semenov
UX Lead at Enonic and blogger at WebAgility.com
The full Safari engine is inside of iPhone. And so, you can write amazing Web apps that look and behave exactly like apps on the iPhone. You’ve got everything you need if you know how to write apps using the most modern web standards to write amazing apps for the iPhone today. So developers, we think we’ve got a very sweet story for you. You can begin building your iPhone apps today.
Steve Jobs, 2007
if ('serviceWorker' in navigator) {
// Register a service worker hosted at the root of the
// site using the default scope.
navigator.serviceWorker.register('/sw.js').then(function(registration) {
console.log('Service worker registration succeeded:', registration);
}).catch(function(error) {
console.log('Service worker registration failed:', error);
});
} else {
console.log('Service workers are not supported.');
}Network First
Cache First
Cache Only
Network Only
Cache, then Network
{
"name": "PWA Starter for Enonic XP",
"short_name": "PWA Starter",
"theme_color": "#FFF",
"background_color": "#FFF",
"display": "standalone",
"start_url": ".?source=web_app_manifest",
"icons": [
{
"src": "precache/icons/icon.png",
"sizes": "512x512",
"type": "image/png"
}
]
}<head>
<meta charset="utf-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>My First PWA</title>
<link rel="manifest" href="manifest.json">
</head>if (containsDataUrl(e.request.url) || responseUpdate) {
let url = responseUpdate ? e.request.url.replace("?update=true", "") : e.request.url;
e.respondWith(
caches.open(responseUpdate ? cacheName : dataCacheName).then(function(cache) {
consoleLog("Fetching data url " + url);
return fetch(e.request)
.then(function (response) {
cache.put(url, response.clone());
return response;
})
.catch(function (ex) {
consoleLog('Network is down. Trying to serve from cache...');
return cache.match(e.request, {
ignoreVary: true
})
.then(function (response) {
consoleLog((response ? 'Serving from cache' : 'No cached response found') + ': ' + e.request.url);
return response || getFallbackPage(e.request.url);
});
});
})
);
}
else {
e.respondWith(
caches.match(e.request, {
ignoreVary: true
})
.then(function (response) {
consoleLog((response ? 'Serving from cache' : 'Requesting from the server') + ': ' + e.request.url);
return response || fetch(e.request);
})
);
}const filesToCache = [
offlineUrl,
offlineCompactUrl,
'{{siteUrl}}',
'{{siteUrl}}/',
'{{assetUrl}}/js/main.js',
'{{assetUrl}}/js/image.js',
'{{assetUrl}}/js/material.js',
'{{assetUrl}}/js/dialog-polyfill.js',
'{{assetUrl}}/css/main.css',
'{{assetUrl}}/css/image.css',
'{{assetUrl}}/css/material.css',
'{{assetUrl}}/css/dialog-polyfill.css',
'{{assetUrl}}/img/cancel.svg',
'{{assetUrl}}/img/download_image.svg',
'{{assetUrl}}/img/info.svg',
'{{assetUrl}}/img/pencil.svg',
'{{assetUrl}}/img/spinner.svg',
'{{assetUrl}}/img/placeholder.png',
'{{assetUrl}}/img/noisy-texture.png',
'//fonts.googleapis.com/icon?family=Material+Icons',
'//fonts.googleapis.com/css?family=Roboto:regular,bold,italic,thin,light,bolditalic,black,medium&lang=en'
];
self.addEventListener('install', function(e) {
consoleLog('Install');
e.waitUntil(self.skipWaiting());
e.waitUntil(
caches.open(cacheName).then(function(cache) {
consoleLog('Caching app shell');
return cache.addAll(filesToCache);
}).catch(function(err) {
console.log(err);
})
);
});
Generates and registers Service Worker
Implements Web Manifest
Bundles JS files and CSS stylesheets
Precaches all assets
Implements fallback page
Routes requests
Complies with PWA audits
importScripts('/node_modules/workbox-sw/build/workbox-sw.vX.X.X.prod.js');
const workboxSW = new WorkboxSW();
workboxSW.precache([]);{
globDirectory: 'resources/assets',
globPatterns: ['precache/**\/*'],
globIgnores: ['*.svg'],
swSrc: 'resources/js/sw-template.js',
swDest: 'build/sw.js'
}importScripts('/node_modules/workbox-sw/build/workbox-sw.vX.X.X.prod.js');
const workboxSW = new WorkboxSW();
workboxSW.precache([
{
url: '/precache/index.html',
revision: 'bb121c',
}, {
url: '/precache/styles/main.css',
revision: 'acd123',
}, {
url: '/precache/scripts/main.js',
revision: 'a32caa',
}
]);importScripts('/node_modules/workbox-sw/build/workbox-sw.vX.X.X.prod.js');
const workboxSW = new WorkboxSW();
workboxSW.router.registerRoute('/schedule', workboxSW.strategies.networkFirst());
workboxSW.router.registerRoute('/news', workboxSW.strategies.cacheFirst());
workboxSW.router.registerRoute('/about', workboxSW.strategies.cacheOnly());
By Alan Semenov
UX Lead at Enonic and blogger at WebAgility.com