Service worker cache storage not updated when code changes - javascript

My single page web application, is written as PWA. It's a ASP.NET Web API application, which is hosted under IIS. My application uses a service worker, which precaches my application shell. Other content is written to indexedDB or updated in the service worker cache on-the-fly, as the user clicks through the application.
I have a problem with code changes. When I update code, and refresh my browser, I can see my changes directly in the javascript and html code. When I go offline, my application falls back on the code, which is in my service worker. This code is still old code. It seems not to be updated, when i reload my application. My service worker looks like this:
const urlsToCache = ['.'
, 'index.html'
, 'favicon.ico'
, 'service-worker.js'
// More sources are cached here
];
self.addEventListener('install', function (event) {
console.log('\'Install\' event triggered.');
event.waitUntil(
caches.open(CACHE).then(function (cache) {
console.log('Application shell and content has been cached.')
return cache.addAll(urlsToCache);
}).then(function (event) {
return self.skipWaiting();
})
);
});
self.addEventListener('activate', function (event) {
console.log('\'Activate\' event triggered.');
return self.clients.claim();
});
For registering the service worker, my code looks like this:
navigator.serviceWorker.register('./service-worker.js')
.then(function (registration) {
console.log('Registered: ', registration);
registration.update();
console.log('Updated: ', registration);
resolve(registration);
}).catch(function (error) {
console.log('Registration failed: ', error);
reject(error);
});
So when the service worker get registered, it also checks for updates. However, it only updates when the service worker code itself is changed. Not when code within my application shell is changed without changing the service worker code itself.
How can i make sure that the code in my service worker is reloaded when I change my application code and refresh my page?

Related

How to make PWA detect any HTML changes and update it automatically?

Essentially I'm wondering what to change in my sw.js file so that if I add a new HTML element, when the user opens up the PWA it takes a moment to update and refresh itself and display the new content as well?
Currently when I'm connected to the Wifi, the PWA does not make any changes, and when I disconnect my Wifi, it only caches my HTML file and gets rid of my CSS. So I guess my follow up question is how do I make the other pages cache as well so that they're available offline? (My app is only 200kb).
Here is my sw.js code:
// On install - caching the application shell
self.addEventListener('install', function(event) {
event.waitUntil(
caches.open('sw-cache').then(function(cache) {
// cache any static files that make up the application shell
return cache.add('index.html');
})
);
});
// On network request
self.addEventListener('fetch', function(event) {
event.respondWith(
// Try the cache
caches.match(event.request).then(function(response) {
//If response found return it, else fetch again
return response || fetch(event.request);
})
);
});

Blank Service worker script

The service worker script is blank
I am trying to implement service workers into my web app however i noticed nothing was working. I get confirmation that the service worker starts however when i try to view it in the chrome tools it shows a blank document. All efforts to console log etc are unsuccessful which is leading me to believe that the file is truly blank despite it obviously not being.
I have tried unregistered the service worker and updating manually.
SCRIPT ON INDEX
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('sw_basic.js')
.then(function(reg) {
// registration worked
console.log('Registration succeeded. Scope is ' + reg.scope);
}).catch(function(error) {
// registration failed
console.log('Registration failed with ' + error);
});
}
SERVICE WORKER SCRIPT
var CACHE_NAME = 'my-site-cache-v1';
var urlsToCache = [
'./', './index.php',
'./profile.php',
'./support.php',
'./img/dance3-min.png',
'./css/agency',
'./css/agency.min.css',
'./css/eventform.css',
'./css/loginmodal.css',
'./css/profile.css',
'./css/support.css',
'./css/table.css',
'./css/timer.css'
]
self.addEventListener('install', function(event) {
event.waitUntil(
caches.open(CACHE_NAME)
.then(function(cache) {
console.log('Opened Cache');
return cache.addAll(urlsToCache);
})
);
});
self.addEventListener('fetch', function(event)) {
event.respondWith(
caches.match(event.request)
.then(function(response) {
if (response) {
console.log('Successfully fetched resource from chache: ${event.request.url}');
return response;
} else {
console.error('Failed to fetch resource: ${event.request.url}');
}
return fetch(event.request);
}
)
)
}
EDIT* I have gotten it to update by changing the name of the js file and manually unregistering the service worker in chrome however it doesn't always update this way sometimes requiring several attempts
I still feel like there must be a better way for doing this and in all the tutorials / documentation it seems like it should install the new one and activate once all tabs are unloaded but its not even installing the updated one at all.
EDIT*
I noticed the service worker tries to install and then disappears.
Example- Service worker #12 is active and running. I refresh and then for a second service worker #24 is installing and then suddenly its gone. At this point i really don't know whats going on other feeds are saying its a problem with the cache max age but I have it set to 0 in the htaccess
Cache-Control: max-age= 0
EDIT*
I have tried taking the service worker onto a different page remove the caching and just try to get it to update.
Currently my index looks like
<html>
<head>
<script>
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/beta/sw.js', {scope: '/beta/'})
.then(function(reg) {
// registration worked
console.log('Registration succeeded. Scope is ' + reg.scope);
}).catch(function(error) {
// registration failed
console.log('Registration failed with ' + error);
});
}
</script>
</head>
online page v2.0
</html>
and the service worker looks like
self.addEventListener('install', function(event) {
console.log("SW installed");
});
self.addEventListener('activate', function(event) {
console.log("SW activated");
});
self.addEventListener('fetch', function(event) {
console.log("Hijacked Signal");
event.respondWith(new Response("offline page"));
});
This Works when the user refreshes after visiting the page the text changes from online to offline. The problem occurs when i change the desired text (eg to offline 2.0). Anyone who has already visited the website is running the old service worker and so will see offline and not offline 2.0
a link to the page if anyone wishes to see whats going on
https://pakcollegelive.tk/beta/index.php
It turns out Cloudflare wasn't playing nice with the service worker files for whatever reason. It wasn't imperative that Cloudflare was used in my case so disabling it fixed the problem.

Changing the name of a service worker?

I'm trying to figure out what happens if I have a service worker registered on a live site called sw.js and then I rename the service worker to service-worker.js. Now the old one isn't found but it is still showing the old cached version.
How long does it take for it to register the new renamed service worker or how does this work at all?
Edit
This is how I have register the service worker in a react application:
componentDidMount() {
if ("serviceWorker" in navigator) {
navigator.serviceWorker
.register("/service-worker.js")
.then(registration => {
console.log("service worker registration successful: ", registration);
})
.catch(err => {
console.warn("service worker registration failed", err.message);
});
}
}
The newly created service worker (renamed) cannot take over the old one because the old one is still active and controlling the client.
the new service worker(renamed one) will wait until the existing worker is controlling zero clients.
Now imagine a service worker sw.js installed and active (controlling the client),
Chrome will visualize the process for you like this
1. The service worker is registered and active
2. Now let's rename the service worker file to sw2.js
You can see that chrome is telling you that something has changed about the service worker. but the current one will keep controlling the client until you force the new one to take control by clicking on the skipWaitting button or by flushing your cache. clicking on the button will cause the sw2.js to take controll over the sw1.js
Now if you need to do this programmatically, you can do it in the install event inside your service worker by calling self.skipWaiting().
self.addEventListener('install', (e) => {
let cache = caches.open(cacheName).then((c) => {
c.addAll([
// my files
]);
});
self.skipWaiting();
e.waitUntil(cache);
});
The following animated image from Jake Archibald's article The Service Worker Lifecycle can make the idea more clear.
You have to update the instance creation code to reflect this change where your shared worker is being initialized and used, for example your current code would look like
var worker = new SharedWorker("ws.js");
That will need to be updated to
var worker = new SharedWorker("service-worker.js");
I was able to solve it by setting up a server which listens to both /service-worker.js and /sw.js get requests.
Since the service worker was renamed from sw.js to service-worker.js it was not finding the old service worker at http://example.com/sw.js so what I did was the following:
createServer((req, res) => {
const parsedUrl = parse(req.url, true);
const { pathname } = parsedUrl;
// new service worker
if (pathname === "/service-worker.js") {
const filePath = join(__dirname, "..", pathname);
app.serveStatic(req, res, filePath);
// added new endpoint to fetch the new service worker but with the
// old path
} else if (pathname === "/sw.js") {
const filePath = join(__dirname, "..", "/service-worker.js");
app.serveStatic(req, res, filePath);
} else {
handle(req, res, parsedUrl);
}
}).listen(port, err => {
if (err) throw err;
console.log(`> Ready on http://localhost:${port}`);
});
As you can see, I added a second path to serve the same service worker but with the /sw.js endpoint, one for the old sw.js and the other one for the newer service-worker.js.
Now when old visitors that have the old active sw.js will download the newer one and upon revisit, they will automatically fetch the newer renamed service-worker.js service worker.

How to add service worker in Magento 2?

I had written service worker in JS but i am not getting where to add that file in Magento 2 PWA I had use tigren extension for achieving PWA but service worker is not working in that so I had written sevice worker of my own.
This is my service worker
//This is the "Offline page" service worker
//Install stage sets up the offline page in the cache and opens a new cache
self.addEventListener('install', function(event) {
var offlinePage = new Request('offline.html');
event.waitUntil(
fetch(offlinePage).then(function(response) {
return caches.open('pwabuilder-offline').then(function(cache) {
console.log('[PWA Builder] Cached offline page during Install'+ response.url);
return cache.put(offlinePage, response);
});
}));
});
//If any fetch fails, it will show the offline page.
//Maybe this should be limited to HTML documents?
self.addEventListener('fetch', function(event) {
event.respondWith(
fetch(event.request).catch(function(error) {
console.error( '[PWA Builder] Network request Failed. Serving offline page ' + error );
return caches.open('pwabuilder-offline').then(function(cache) {
return cache.match('offline.html');
});
}
));
});
//This is a event that can be fired from your page to tell the SW to update the offline page
self.addEventListener('refreshOffline', function(response) {
return caches.open('pwabuilder-offline').then(function(cache) {
console.log('[PWA Builder] Offline page updated from refreshOffline event: '+ response.url);
return cache.put(offlinePage, response);
});
});
This is service worker register js.
//This is the "Offline page" service worker
//Add this below content to your HTML page, or add the js file to your page at the very top to register service worker
if (navigator.serviceWorker.controller) {
console.log('[PWA Builder] active service worker found, no need to register')
} else {
//Register the ServiceWorker
navigator.serviceWorker.register('pwabuider-sw.js', {
scope: './'
}).then(function(reg) {
console.log('Service worker has been registered for scope:'+ reg.scope);
});
}
please help me out how to add service worker in magneto 2.

get and show push data from serviceworker in chrome

I use pushwoosh for receive push notification in my web app.
every things working well and received push message in serviceworker listener but I want give push messge data from serviceworker and process it in another js class
main.js like this:
if ('serviceWorker' in navigator) {
console.log('Service Worker is supported');
navigator.serviceWorker.register('sw.js').then(function() {
return navigator.serviceWorker.ready;
}).then(function(reg) {
console.log('Service Worker is ready :^)', reg);
// TODO
}).catch(function(error) {
console.log('Service Worker error :^(', error);
});
}
// get push message data in main.js and process it
service worker like this :
self.addEventListener('push', function(event) {
console.log('Push message', event);
var title = 'Push message';
event.waitUntil(
self.registration.showNotification(title, {
'body': 'The Message',
'icon': 'images/icon.png'
}));
});
As I mentioned in a comment, this seems a slightly odd use-case for a service worker rather than a standard worker, but:
You can have your service worker send a message to all connected clients when it gets a message pushed to it.
This answer shows a complete example of a service worker talking to clients, but fundamentally:
The pages it manages listen for messages:
navigator.serviceWorker.addEventListener('message', event => {
// use `event.data`
});
The service worker sends to them like this:
self.clients.matchAll().then(all => all.forEach(client => {
client.postMessage(/*...message here...*/);
}));
Or with ES5 and earlier syntax (but I don't think any browser supporting service workers doesn't also support arrow functions):
Page listening:
navigator.serviceWorker.addEventListener('message', function(event) {
// use `event.data`
});
Worker sending:
self.clients.matchAll().then(function(all) {
all.forEach(function(client) {
client.postMessage(/*...message here...*/);
});
});

Categories

Resources