Skip to content

Instantly share code, notes, and snippets.

@federicofazzeri
Last active October 16, 2017 16:07
Show Gist options
  • Save federicofazzeri/ccb51bf74bd65098358a0e6ce4058632 to your computer and use it in GitHub Desktop.
Save federicofazzeri/ccb51bf74bd65098358a0e6ce4058632 to your computer and use it in GitHub Desktop.
Service worker setup for a PWA that shows cached data and replace it with fresh network data when available. If network data comes first, the view won't be updated with the old cached data. Cache/network data flows are managed with observables.
const dataUrl = ''; //your data api url
(function(dataUrl, Observable) {
'use strict';
/******** your impure functions: ***********/
//bind data to the view here
const updateViewData = data => {}
//you got data from the network, user is online..
const updateViewSetOnline = () => {}
//retrieve data from the network
const getFromNetwork = url =>
Observable.fromPromise(
fetch(url)
.then( response => response.json() )
.catch( error => ({ err: 'network' }) )
)
//retrieve data from the cache
const getFromCache = url =>
Observable.fromPromise(
caches.match(url)
.then( response => {
if( ! response) throw Error("No cached data")
return response.json();
})
.catch( error => ({ err : error }) )
)
//my cache/network observable stream instances
const getFromNetwork$ = getFromNetwork(dataUrl);
const getFromCache$ = getFromCache(dataUrl);
Observable.merge(
getFromNetwork$,
getFromCache$.takeUntil(getFromNetwork$)
).subscribe( updateViewData )
getFromNetwork$
.subscribe( updateViewSetOnline )
if ('serviceWorker' in navigator) {
navigator.serviceWorker
.register('./service-worker.js')
.then(function() { console.log('Service Worker Registered'); });
}
})(dataUrl, Rx.Observable);
<!DOCTYPE html>
<html>
<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>service worker</title>
</head>
<body>
<p id="connecting">connecting...</p>
<p id="data">loading...</p>
<script src="app.js" type="text/javascript"></script>
</body>
</html>
const staticFilesCacheName = 'static-v1';
const staticFilesToCache = [
'/',
'/index.html',
'/app.js'
];
const dataCacheName = 'my-data-v1';
self.addEventListener('install', function(event) {
event.waitUntil(
caches.open(staticFilesCacheName)
.then(function(cache) {
return cache.addAll(staticFilesToCache);
})
);
});
self.addEventListener('activate', function(event) {
event.waitUntil(
caches.keys().then(function(cacheNames) {
return Promise.all(
cacheNames
.filter(function(cacheName) {
return (cacheName !== staticFilesCacheName && cacheName !== dataCacheName )
})
.map(function(cacheName) {
return caches.delete(cacheName);
})
);
})
);
return self.clients.claim()
});
self.addEventListener('fetch', function(event) {
var myDataUrl = 'myjsondata'; //your data api url
//getting fresh data from the network FIRST, then caching it
if ( event.request.url.includes(myDataUrl) ) {
event.respondWith(
caches.open(dataCacheName)
.then(function(cache) {
return fetch(event.request)
.then(function(response) {
cache.put(event.request, response.clone());
return response;
});
})
);
//static assets: getting data from the cache then falling back to the network
} else {
event.respondWith(
caches.match(event.request)
.then(function(response) {
return response || fetch(event.request);
})
);
}
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment