4

I'm diving into Progressive Web Applications lately. I've linked my manifest.json and sw.js like this:

In the index.html

<link rel="manifest" href="<%= htmlWebpackPlugin.files.publicPath %>static/manifest.json">

And in my main.js

if('serviceWorker' in navigator) {
        const messaging = firebase.messaging()

        navigator.serviceWorker.register('/static/sw.js').then((registration) => {
            messaging.useServiceWorker(registration)
            console.log('Service Worker registered')
        }).catch((error) => {
            console.log('Service Worker failed to register')
        })
    }

It looks like it works, but when I try to add the webapp to my homescreen using DevTools, it shows the following error: "Site cannot be installed: no matching service worker detected. You may need to reload the page, or check that the service worker for the current page also controls the start URL from the manifest"

After doing some research I found people say place the sw.js and manifest.json in the root of the project. But when I do, it won't link the files (when I put them in the static folder, it will link them). By the way, I'm using the standard Vue.js template.

Any suggestions how to fix this?

user3519221
  • 119
  • 2
  • 10

1 Answers1

6

The reason to the service worker failed to register could be 1 of these:

  1. You are not running your application through HTTPS.
  2. The path to your service worker file is not written correctly — it needs to be written relative to the origin, not your app’s root directory.

Structure wise, it should be like this

/static/
    /css/
    /js/
    /img/
manifest.json
index.html
service-worker.js

In the index file, you should register the service worker similar to this

<script type=text/javascript>
  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);
    });
  }
</script>

and connect manifest.json

<link rel=manifest href=/manifest.json>

Content of manifest.json can be

{
  "name": "vue-vanilla-pwa",
  "short_name": "vue-vanilla-pwa",
  "icons": [
    {
      "src": "/static/img/icons/android-chrome-192x192.png",
      "sizes": "192x192",
      "type": "image/png"
    },
    {
      "src": "/static/img/icons/android-chrome-512x512.png",
      "sizes": "512x512",
      "type": "image/png"
    }
  ],
  "start_url": "/index.html",
  "display": "standalone",
  "background_color": "#000000",
  "theme_color": "#4DBA87"
}

sw-precache is designed specifically for the needs of precaching some critical static resources. So if you want to do something else with service worker when user is offline, you don't need it. Here is an example list of what you can do with service workers: https://github.com/GoogleChrome/samples/tree/gh-pages/service-worker

Arielle Nguyen
  • 2,932
  • 1
  • 20
  • 15
  • Hi there, thanks for the great answer but I still have some problems. I tryied to reproduce your same steps but with no luck. The only thing I did differently was to put the manifest inside the `/static` folder. I keep on getting this error when I try to do what you said: `Service worker registration failed, error: DOMException: Failed to register a ServiceWorker: The script has an unsupported MIME type ('text/html').` I don't use dev mode since I already have a production environment so I build the whole application each time. Could it be a problem? – Eugenio Apr 20 '18 at 08:43
  • Okay yeah, I just got the answer by myself. I was pushing all my code to production with the `build` command. The problem was that the `service-worker` file was beeing ignored. I just added it to the static folder (so it gets built with the whole project) and referenced correctly inside the script. Thanks a lot for the help with your answer anyway! I will probably be giving out the bounty at the end of it's duration and if nobody else answers in a better form you'll get it! Thanks again! – Eugenio Apr 20 '18 at 08:48
  • Nevermind, what I stated before is kind of bad for the whole application since placing the service worker file inside a directory that is not `/` is bad since it can't work outside of it's own scope. What I did, and I don't know if this is a workaround or I need to study webpack a bit more, is to just copy the file inside the generated output of webpack's build command (the `/dist` folder). This ensures that the service worker is at the `/` scope and can work properly. If you know how to do this more efficiently please let me know! Cheers! – Eugenio Apr 20 '18 at 09:20
  • Well, after a few days I will give you the bounty since you answered the question in depth and very well. Also, for the posterity, I used webpack to automagically copy the `service worker` during build. Just google it, you'll find it ;P – Eugenio Apr 23 '18 at 07:59
  • 1
    Hi @Eugenio, I'm glad you figured it out! Thank you for the update and bounty :) In regards to making sure service worker is at the / scope to work properly, it is best to add this step to the prod build step in your npm build scripts. The new Vue cli is basically doing this in prod mode. It generates a service worker file at root level of /dist folder using https://www.npmjs.com/package/sw-precache-webpack-plugin :) – Arielle Nguyen Apr 23 '18 at 08:22