Access localStorage from service worker
Asked Answered
B

5

51

I want to periodically call an API from my service worker to send data stored in the localStorage. This data will be produced and saved in localStorage when a user browses my website. Consider it something like saving stats in localStorage and sending it periodically through the service worker. How should I do this? I understand that I can't access localStorage from the service worker and will have to use the postMessage API. Any help would be highly appreciated.

Bloater answered 30/11, 2016 at 11:49 Comment(2)
Even though this question mentions angular, it contains a possible answer: #52722326Fermanagh
@Bloater did you find any solution?Chamfron
C
42

You cannot access localStorage (and also sessionStorage) from a webworker process, they result will be undefined, this is for security reasons.

You need to use postMessage() back to the Worker's originating code, and have that code store the data in localStorage.

You should use localStorage.setItem() and localStorage.getItem() to save and get data from local storage.

More info:

Worker.postMessage()

Window.localStorage

Pseudo code below, hoping it gets you started:

 // include your worker
 var myWorker = new Worker('YourWorker.js'),
   data,
   changeData = function() {
     // save data to local storage
     localStorage.setItem('data', (new Date).getTime().toString());
     // get data from local storage
     data = localStorage.getItem('data');
     sendToWorker();
   },
   sendToWorker = function() {
     // send data to your worker
     myWorker.postMessage({
       data: data
     });
   };
 setInterval(changeData, 1000)
Cryptogram answered 30/11, 2016 at 12:14 Comment(8)
var myWorker = new YourWorker('YourWorker.js'), > new WebWorker right?Transferase
@Roberrrt thanks for your comment, correct is new Worker() :)Cryptogram
Thanks GibboK, however I have a couple of doubts. The code that you've mentioned would be running in one of my javascripts. And for this to run the user has to have the website running? Isn't it? I am looking for something where in the service worker periodically sends data from the local Storage even if the user is not on my website. Is my scenario even possible?Bloater
@Bloater YES, if your writing JS for browser. You cannot access local storage from we Web Worker for security reasons.Cryptogram
@Bloater if you think my answer helped you out, please do not forget to accept it clicking on the left V icon. Thanks and happy coding :)Cryptogram
@FelipeMorales afik it should work also with a service worker communication’s with the webpage must go through service workers PostMessage method. but please give it a try and let us know :)Cryptogram
This answer is for web workers - the question is about service workers?!Digitize
@GibboK, what is your evidence that not being able to use localStorage in service workers has anything to do with security? The MDN page for service workers mentions only synchronicity, though even that is not backed up.Dissatisfy
R
25

Broadcast Channel API is easier

There are several ways to communicate between the client and the controlling service worker, but localStorage is not one of them. IndexedDB is, but this might be an overkill for a PWA that by all means should remain slim.

Of all means, the Broadcast Channel API results the easiest. It is by far much easier to implement than above-mentioned postMessage() with the MessageChannel API.

Here is how broadcasting works

Define a new broadcasting channel in both the service worker and the client.

const channel4Broadcast = new BroadcastChannel('channel4');

To send a broadcast message in either the worker or the client:

channel4Broadcast.postMessage({key: value});

To receive a broadcast message in either the worker or the client:

channel4Broadcast.onmessage = (event) => {
    value = event.data.key;
}
Recollect answered 24/3, 2021 at 16:3 Comment(2)
What if the website was not open before and service worker just opened this new tab. Shouldn't it wait for channel4Broadcast.onmessage to get initialised first?Lilly
Just a side note that Broadcast API is built specially for Many-to-Many while PostMessage can perform better at one-to-one communication as needed for Service Worker and Client communication.Somali
A
16

I've been using this package called localforage that provides a localStorage-like interface that wraps around IndexedDB. https://github.com/localForage/localForage

You can then import it by placing it in your public directory, so it is served by your webserver, and then calling: self.importScripts('localforage.js'); within your service worker.

Armallas answered 13/3, 2020 at 8:54 Comment(3)
IndexedDB works inside Service Workers--and that's why LocalForage works (it defaults to IndexedDB). This question was about LocalStorage, which is not accessible with Service WorkersAssistant
@Assistant Question says "I understand that I can't access localStorage... Any help would be highly appreciated."Armallas
Even though this answer does not include a solution for using localStorage, it presents an excellent alternative that is easy to implement and maintains a similar syntax the user is acquainted to. I've tried this solution and it was a very easy implementation and migration from local storage.Ballflower
F
5

https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API/Using_Service_Workers says

Note: localStorage works in a similar way to service worker cache, but it is synchronous, so not allowed in service workers.

Note: IndexedDB can be used inside a service worker for data storage if you require it.

Also there is a bit of discussion here: How do I access the local storage using service workers in angular?

Fermanagh answered 16/2, 2019 at 4:49 Comment(0)
C
0

Stumbling over this question myself for a tiny webapp-project, I considered the following solution: When the user is online, the data can be sent immediately. When he is offline, I use the SyncEvent.tag property to send information from the client to the serviceworker. Like this:

//offline_page.html (loads only, when user is offline)

button.onclick = function() {
//on click: store new value in localStorage and prepare new value for synchronization
    localStorage.setItem("actual", numberField.value);
    navigator.serviceWorker.ready.then(function(swRegistration) {
        return swRegistration.sync.register('newval:'+numberField.value);
    });
}
//sw.js

self.addEventListener('sync', function(event) {
//let's say everything in front of ':' is the option, everything afterwards is the value
    let option = event.tag.replace(/(.*?)\:.*?$/, "$1");
    let value = event.tag.replace(/.*?\:(.*?)$/, "$1");

    if(option == "newval") {
        event.waitUntil(
            fetch("update.php?newval="+value)
            .then(function(response) {
                console.log(response);
            })
        );
    }
});

update.php saves the new value to backend, as soon as the user goes online. This won't give the service worker access to the localStorage, but it will send him every change made.

Just starting to get used to this syncing topic. So I would really be interested, wheather this is helpful and what others think about this solution.

Calabash answered 17/6, 2020 at 15:53 Comment(1)
IMO - sync is not always available (browser support AND the user can disable it), besides this looks overly complicated - why not simply send a message? navigator.serviceWorker.controller.postMessage({ value:'...' }, [new MessageChannel().port2]);Priscian

© 2022 - 2024 — McMap. All rights reserved.