PWA serviceWorker not always registered - javascript

We have in-house built CMS and recently we added PWA to it. Now, when accessing the home page www.ourdomain.com everything is fine, but when accessing an article, we are a news website, someting like www.ourdomain.com/section/article a message appears:
Failed to register a ServiceWorker: A bad HTTP response code (404) was
gwreceived when fetching the script.
First the scope was
./
and in that case ServiceWorker wasn't registered at all.
Now I changed the scope to
/
and not it is registered for the home page but not for any article. I went through docs, reading questions and answers but doesn't seem able to solve this. By they way, PWA installs well on a mobile an works well.

Your service worker js file should preferably be in your root public directory of your app since you're just starting out with PWAs, and specifying the correct scope is essential, Here's a perfect example of installing your service worker from Google PWA guide.
main.js
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/service-worker.js')
.then(function(registration) {
console.log('Registration successful, scope is:', registration.scope);
})
.catch(function(error) {
console.log('Service worker registration failed, error:', error);
});
}
service-worker.js
Expecting that you have /service-worker.js and main.js in your root directory, and if you get to work with manifest, you render the same scope as your start_url
You can try the above installation snippet on your app public root directory.

Related

File location of Service Worker affect the accessibility for registration

I am trying to create a PWA with reactjs. There is no service worker when I use create-react-app so I created one myself.
The simplified file structure is this:
-public
-index.html
-manifest.json
-**service-worker.js**
-src
-index.js
-serviceWorkerRegistration.js
The entry point of the app is index.js, which will import serviceWorkerRegistraion.js. Then serviceWorkerRegistration will run the following code:
if ("serviceWorker" in navigator) {
console.log("support service worker");
navigator.serviceWorker.register("/service-worker.js") // Line A
.then(reg => console.log("service worker registered", reg))
.catch(err => console.warn("service worker registration failed", err));
}
else console.log("service worker not supported");
And the service worker is registered successfully.
However, if I move service-worker.js to the src directory and change the file name in Line A to "./service-worker.js", it seems service-worker.js cannot be found and throw an error with The script has an unsupported MIME type ('text/html') (which happens when the file cannot be found and a html file is returned instead)
Why the location of service-worker.js affect the accessibility for registrating service worker in this way? Is there any way I can access the file with service-worker.js living in the src directory?

vercel published sveltekit where vite fails to register serviceworker placed in src folder

This question will need someone from vercel team or sveltekit team as it is related to pwa app that I published to vercel:
1- I placed a minifest1.js file in the "static" folder
2- I placed testserviceworker.js file in the "SRC" folder.
When I run my app locally using my laptop by "npm run build" and "npm run preview", vite registers my serviceworker, my cache is populated and the app is installable as pwa.
When I deploy the same code to vercel, vite fails to register the serviceworker. To experiment I added the serviceworker file to the static folder and added a register button in index.svelte to register the file manully when I press the button.
When I press the button, the serviceworker is registered, I'm able to verify it in dev tools, application tab, cache and able to install my app but my lighthouse report showing pwa part as failed.
So my question is regarding vite registering serviceworker by default upon starting the site. Do I need to add a function in load or onMount with "navigator.serviceWorker.register('testserviceworker.js" or there is some kind of magic configuration in vercel or svelte.config.js to point vite to where the serviceworker.js so it register it upon starting the app?
I added the files for manifest and serviceworker here but I don't think they're relative to my question as it is a configuration issue not code issue:
manifest file:
{
"name": "PWATEST",
"short_name": "PWATESTSHORTNAME",
"start_url": "/",
"scope" : "/",
"background_color": "#1b4079",
"theme_color": "#d62828",
"display": "fullscreen",
"icons": [
{
"src": "/512svelte-welcome.png",
"sizes": "512x512",
"type": "image/png",
"purpose" : "any maskable"
}
]
}
My testserviceworker.js
console.log("*****hello from testserviceworker.js inside static folder********")
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open('v3').then((cache) => {
cache.add(
'./favicon.png'
);
})
);
});
self.addEventListener('activate', function(event) {
console.log("serviceworker activate fn :", event)
});
self.addEventListener('fetch', function(event) {
console.log("fetch fn inside serviceworker.js")
})
Here is the LINK to vercel app running with step by step to test this setup in the home page.
Is there any vercel team member or Sveltekit ninja warriors who can guide me to fix this problem? No one is building websites anymore. Installing pwa is the way to go and Sapper used to include a serviceworker and manifest file by default. For some mysterious reason, vite got involved and that ability disappeared and now after deploying, vite is not able to locate the serviceworker to register it as stated in the sveltekit doc:
Doc-serviceworkers
it's often worth using service workers to speed up navigation by precaching your built JS and CSS.
In SvelteKit, if you have a src/service-worker.js file (or src/service-worker.ts, or src/service-worker/index.js, etc) it will be built with Vite and automatically registered.
In conclusion :
1-How to let vite knows about the serviceworker file in specific location like static folder or scoped to a different part of the app?
2-How to fix vite registering the serviceworker if I place the serviceworker.js in src folder?
Hopefully we will get someone from vercel team or sveltekit team to look into this issue. I found this issue and I can disable it but how to register the serviceworker without pressing a button? Put the code in load or onMount?
The published vercel app link is here again for your convenience.
I ended with registeration in the onMount. Now when the app is loaded, the serviceworker is registered and pwa is installable. It passed the lighthouse checks. All green.
Here is the code for onMount:
import { onMount } from 'svelte';
onMount(async () => {
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('testserviceworker.js', { scope: '/' }).then(function(reg) {
// registration worked
console.log('Registration succeeded. Scope is ' + reg.scope);
}).catch(function(error) {
// registration failed
console.log('Registration failed with ' + error);
});
};
});
To recap:
When you publish to vercel -accordning to the sveltekit docs - vite suppose to register the serviceworker for you.
1- Place your serviceworker.js file in the static folder.
2- Add onMount fn and register the serviceworker there.
3- No need to disable vite auto registeration in the svelte.config.js as
instructed in the docs as I didn't disable it and it didn't cause any
issues.
Hopefully sveltekit 1.0 will be released soon. I'm sure majority of these bugs will be ironed out by then.

Firebase messaging in production

I have a project that works with FCM. On the localhost, every thing works fine and I get the token for FCM; but, in production, I get this error:
FirebaseError: Messaging: We are unable to register the default service worker. Failed to register a ServiceWorker for scope ('https://myxdomain.com/firebase-cloud-messaging-push-scope') with script ('https://myxdomain/firebase-messaging-sw.js'): A bad HTTP response code (403) was received when fetching the script. (messaging/failed-service-worker-registration).
I'm deploying my app in an Apache server.
As mentioned here, you need to add firebase-messaging-sw.js file in correct folder. It may be something like this: /var/www/...

An unknown error occurred when fetching the script (Service Worker)

When going offline, I get the following error by my service worker:
(unknown) #3016 An unknown error occurred when fetching the script
my service worker looks like this:
var version = 'v1'
this.addEventListener('install', function(event){
event.waitUntil(
caches.open(version).then(cache => {
return cache.addAll([
'https://fonts.googleapis.com/icon?family=Material+Icons',
'https://fonts.googleapis.com/css?family=Open+Sans:400,600,300',
'./index.html'
])
})
)
})
this.addEventListener('fetch', function(event) {
event.respondWith(
caches.match(event.request).then(function(resp) {
// if it's not in the cache, server the regular network request. And save it to the cache
return resp || fetch(event.request).then(function(response) {
return caches.open(version).then(function(cache) {
cache.put(event.request, response.clone())
return response
})
})
})
)
})
It is at top level directory, right next to a manifest importing like this in index.html:
<link rel="manifest" href="/manifest.json">
I import the service worker in my entry js file. And register it right after.
require('tether-manifest.json')
import serviceWorker from 'sw'
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register(serviceWorker)
.then(() => {
// registration worked
}).catch(error => {
throw new Error(error)
})
}
It registers fine. I don't encounter the error until I go offline.
I am using webpack with React, and doing the following in webpack to copy my sw.js file to the dist folder:
loaders: [
{ // Service worker
test: /sw\.js$/,
include: config.src,
loader: 'file?name=[name].[ext]'
},
{ // Manifest
test: /manifest\.json$/,
include: config.src,
loader: 'file?name=[name].[ext]'
}
]
The error doesn't give any information as to what is happening.
Anyone have any idea how to fix this?
I had exactly the same problem, I spent an hour trying to figure it out and it turned out that I had another tab for the same origin left opened somewhere (so using the same shared Service Worker) that had the "Offline" checkbox left checked and that prevented another tabs from requesting sw.js for some reason.
It seems that the offline state is leaking from the tab within Service Worker scope while not being properly reflected nor managed by the other tabs than the one that was turned Offline first.
So make sure you have no other clients running the same Service Worker - you should be able to find them listed in DevTools > Application > Service Workers.
Before trying anything else, check whether your https certificate is invalid or not matching the url you are accessing.
E.g. In my case I was trying to access https://localhost with a certificate that was registered for another domain.
While hitting "proceed" would allow me to enter to the site, this exact error would be printed to the console:
An unknown error occurred when fetching the script
For me this error went away when i added sw.js to the cache upon install. Simply forgot to do that, but it solved the issue.
I had this issue while working in an Angular project. My problem was that I was using Angular CLI's built-in ng serve -prod command.
To make it work, I used ng build -prod and then host the resulting dist folder using http-server
In Chrome, what I did was to check the option Bypass for network and then I was able to reload.

Ruby on Rails & service worker

I'm trying to use a service worker in my Ruby on Rails application.
I need to use some erb features in my app/javascripts/service-worker.js.erb file. The service worker registration script looks like this:
var registerServiceWorker = function() {
navigator.serviceWorker.register(
'<%= asset_path('service-worker.js') %>',
{ scope: '/assets/' }
)
.then(function() {
console.info('Service worker successfully registered');
})
.catch(function(error) {
console.warn('Cannot register sercie worker. Error = ', error);
});
}
This does not work; I never get promise here:
navigator.serviceWorker.ready.then
I also tried ./ and / scopes but I got this error:
DOMException: Failed to register a ServiceWorker: The path of the
provided scope ('/') is not under the max scope allowed ('/assets/').
Adjust the scope, move the Service Worker script, or use the
Service-Worker-Allowed HTTP header to allow the scope.
If I move my service-worker.js to the public folder, remove the .erb extension and change scope to ./, everything works great, but I have no template engine there.
Any suggestions?
There's now a gem, serviceworker-rails that will allow you to proxy requests for serviceworker scripts in the asset pipeline. Because of the way browsers register serviceworker scripts, it's best to avoid caching them.
Sprockets will fingerprint assets and Rails (or your webserver) typically serves the compiled files with aggressive caching headers from the /assets directory.
What we want is the ability to customize service worker paths and response headers and still get Sprockets preprocessing, i.e. CoffeeScript/ES2015 to JavaScript transpilation.
The serviceworker-rails gem inserts middleware to your Rails stack that will proxy a request for /serviceworker.js (for example) to the corresponding fingerprinted, compiled asset in production. In development, you get the auto-reloading behavior you would expect. You can set up multiple serviceworkers at different scopes as well.
https://github.com/rossta/serviceworker-rails
Because of the security purpose, you can't register ServiceWorker in higher scope than from where it was executed.
If you really need template engine, you may try to dynamically load JS file from file in your /public folder (How do I include a JavaScript file in another JavaScript file?). Currently Service-Worker-Allowed HTTP header is not implemented yet in Firefox (https://bugzilla.mozilla.org/show_bug.cgi?id=1130101)
I'm running into this problem now as well. Based on my research, I think the proper way for a Rails application to handle service workers is to serve the service worker javascript file normally using the Asset pipeline, scope the service worker using the scope option (like you have done), and then use the Service-Worker-Allowed HTTP header to explicitly allow the new scope.
Unfortunately, at the moment there are several barriers to implementing this method:
Rails 4 (apparently) does not allow adding HTTP headers to Assets it serves, other than the cache-control header. To work around this you could add a Rack plugin as detailed in this S.O. answer, or in production if you are using an Asset server such as Nginx, you can get around this by having Nginx add the HTTP header. Though this doesn't help during development.
Rails 5 does allow adding HTTP headers to Assets however, as detailed in this blog post.
Browser support for service workers and the Service-Worker-Allowed HTTP header is currently poor.
Just do it in the root directory. For example, I put a route
get 'service-worker(.format)', to: 'core#service_worker'
Then just put in your controller (core_controller.rb in my example)
def service_worker
end
Then create app/views/core/service_worker.js.erb and put your JS there (including any <%= stuff %>).
I recently wrote an article about how to do that entirely with Webpacker. Please find my guide to a Progressive Web App with Rails.
In short:
add the webpacker-pwa npm package with yarn add webpacker-pwa and edit environment.js with:
const { resolve } = require('path');
const { config, environment, Environment } = require('#rails/webpacker');
const WebpackerPwa = require('webpacker-pwa');
new WebpackerPwa(config, environment);
module.exports = environment;
This will allow you to use service workers. Please check the README of the project as well: https://github.com/coorasse/webpacker-pwa/tree/master/npm_package
Leave the service worker file in whatever directory imposed by your project structure and set the scope option to / and add the Service-Worker-Allowed HTTP header to the response of your service worker file.
In ASP.Net, I added the following to web.config file:
<location path="assets/serviceWorker.js">
<system.webServer>
<httpProtocol>
<customHeaders>
<add name="Service-Worker-Allowed" value="/" />
</customHeaders>
</httpProtocol>
</system.webServer>
</location>
And register the worker by:
navigator.serviceWorker.register('/assets/serviceWorker.js', { scope: '/' })
.then(function (registration)
{
console.log('Service worker registered successfully');
}).catch(function (e)
{
console.error('Error during service worker registration:', e);
});
Tested on Firefox and Chrome.
Refer to the examples in Service worker specification

Categories