Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I call a method on my ServiceWorker from within my page?

I have a ServiceWorker registered on my page and want to pass some data to it so it can be stored in an IndexedDB and used later for network requests (it's an access token).

Is the correct thing just to use network requests and catch them on the SW side using fetch, or is there something more clever?

Note for future readers wondering similar things to me:

Setting properties on the SW registration object, e.g. setting self.registration.foo to a function within the service worker and doing the following in the page:

navigator.serviceWorker.getRegistration().then(function(reg) { reg.foo; })

Results in TypeError: reg.foo is not a function. I presume this is something to do with the lifecycle of a ServiceWorker meaning you can't modify it and expect those modification to be accessible in the future, so any interface with a SW likely has to be postMessage style, so perhaps just using fetch is the best way to go...?

like image 542
owencm Avatar asked Feb 14 '15 08:02

owencm


People also ask

Do service workers work when browser is closed?

This means the browser can have no windows open, and you'll still receive the push message in your service worker, because the browser in running in the background. The only time a push won't be received is when the browser is completely closed, i.e. not running at all (no marking).

Does service worker have access to window?

Web workers and service workers # Both run in a secondary thread, allowing JavaScript code to execute without blocking the main thread and the user interface. They don't have access to the Window and Document objects, so they can't interact with the DOM directly, and they have limited access to browser APIs.

Does service worker work on HTTP?

Service workers will register and work just fine as long as you access the localhost domain -- without HTTPS.

How do you send a message to a service worker?

Controlled pages can use the ServiceWorker. postMessage() method to send messages to service workers. The service worker can optionally send a response back via the Client. postMessage() , corresponding to the controlled page.


2 Answers

So it turns out that you can't actually call a method within a SW from your app (due to lifecycle issues), so you have to use a postMessage API to pass serialized JSON messages around (so no passing callbacks etc).

You can send a message to the controlling SW with the following app code:

navigator.serviceWorker.controller.postMessage({'hello': 'world'})

Combined with the following in the SW code:

self.addEventListener('message', function (evt) {
  console.log('postMessage received', evt.data);
})

Which results in the following in my SW's console:

postMessage received Object {hello: "world"}

So by passing in a message (JS object) which indicates the function and arguments I want to call my event listener can receive it and call the right function in the SW. To return a result to the app code you will need to also pass a port of a MessageChannel in to the SW and then respond via postMessage, for example in the app you'd create and send over a MessageChannel with the data:

var messageChannel = new MessageChannel();
messageChannel.port1.onmessage = function(event) {
  console.log(event.data);
};
// This sends the message data as well as transferring messageChannel.port2 to the service worker.
// The service worker can then use the transferred port to reply via postMessage(), which
// will in turn trigger the onmessage handler on messageChannel.port1.
// See https://html.spec.whatwg.org/multipage/workers.html#dom-worker-postmessage
navigator.serviceWorker.controller.postMessage(message, [messageChannel.port2]);

and then you can respond via it in your Service Worker within the message handler:

evt.ports[0].postMessage({'hello': 'world'});
like image 151
owencm Avatar answered Sep 18 '22 17:09

owencm


To pass data to your service worker, the above mentioned is a good way. But in case, if someone is still having a hard time implementing that, there is an other hack around for that,

1 - append your data to get parameter while you load service-worker (for eg., from sw.js -> sw.js?a=x&b=y&c=z)

2- Now in service worker, fetch those data using self.self.location.search.

Note, this will be beneficial only if the data you pass do not change for a particular client very often, other wise it will keep changing the loading url of service worker for that particular client and every time the client reloads or revisits, new service worker is installed.

like image 41
Satys Avatar answered Sep 22 '22 17:09

Satys