Reputation: 860
I am building a progressive web application using Google Workbox. I have setup a service worker with bgSync (in my build folder) so that my POST requests are pushed to a queue and sent to my endpoint when the user regains connection, but when does the syncing event happen exactly?
For development purposes, I use the "Sync" button included in Chrome as detailed in this answer: How to manually trigger a background sync (for testing)?, But this is manual, I'd like the requests to be sent to the endpoint as soon as the application is back online, but when I get connection my requests are not sent, I have to manually click the "Sync" button for that to happen, and it does work beautifully, but users won't be clicking the Sync button in a real scenario.
Precisely, I'd like to know if there is a way, inside my service-worker, to detect when the application is back online, and force a Sync. Or to know when the sync happens. Here is a snippet for reference (Using Workbox 3.0.0):
const bgSyncPlugin = new workbox.backgroundSync.Plugin('myQueue', {
callbacks: {
requestWillEnqueue: (storableRequest) => {}, // I show a push notification indicating user is offline
requestWillReplay: (storableRequest) => {},
queueDidReplay: (storableRequestArray) => {} // If I get a response, I show a push notification
}
},
);
workbox.routing.registerRoute(
"https://myapi.com/action",
workbox.strategies.networkOnly({
plugins: [bgSyncPlugin]
}),
'POST'
);
Upvotes: 5
Views: 2595
Reputation: 494
You can change period over which there will be retries by leveraging of maxRetentionTime
parameter that takes minutes as its value. For example if you want to retry for 2 days you can initialize queue just like this:
const queue = new workbox.backgroundSync.Queue('requestsQueue', {
maxRetentionTime: 48 * 60 //2 days
});
Of course, if request in queue will be executed successfully it will no longer retry to run.
Further info in docs: https://developers.google.com/web/tools/workbox/reference-docs/latest/workbox.backgroundSync.Queue
Upvotes: 0
Reputation: 860
So, as of today, the background-sync in Chrome tries to push the queued requests 3 times (Never explained in the workbox or background sync documentation):
If the user has no connection after 15 minutes, then the requests just get stuck inside indexedDB until a new queued request tries to push the rest. This is not very helpful in a scenario when we are expecting the user to not have internet connection for hours.
There are plans (since 2016!) to implement PeriodicSync, which would let the developer choose how many times and how long would it take for the browser to try to sync, but it's never been really implemented, see: https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerRegistration/periodicSync
I came up with a clunky solution though, might not be the best one but it does what I need. There IS a sync event that we can manipulate to retry the requests that we have stuck in indexedDB. We need to use the Workbox Queue class instead of Plugin though.
// our service worker file
// we create a push notification function so the user knows when the requests are being synced
const notificate = (title, message) => {
self.registration.showNotification(title, {
body: message,
icon: '/image.png',
tag: 'service-worker'
})
}
// let's create our queue
const queue = new workbox.backgroundSync.Queue('myQueue', {
callbacks: {
requestWillEnqueue: () => {
notificate('You are offline! 🛠', 'Your request has been submitted to the Offline
queue. The queue will sync with the server once you are back online.')
}
});
// sync event handler
self.addEventListener("sync", (ev) => {
queue.replayRequests().then((a) => {
notificate('Syncing Application... 💾', 'Any pending requests will be sent to the
server.');
}).catch(
notificate('We could not submit your requests. ❌', 'Please hit the \'Sync Pending
Requests\' button when you regain internet connection.')
);
});
Now inside our HTML/React/Node view file we can do:
// this will trigger our Sync event
<button type="button" onClick={navigator.serviceWorker.ready.then(reg =>
reg.sync.register('myEvent'))}>{'Sync Pending
Requests'}</button>
Notice I created an html button that forces my service worker to run queue.replayRequests(), so the background sync feature does not happen automatically, I have to manually click a button for it to happen.
Upvotes: 5