I'm building my first app with Capacitor 5.4 and Svelte 3.49. The app is going to send notifications based on a remote API, it is fetching periodically an endpoint and based on the last API activity it calculates the date to schedule a LocalNotification and shows a countdown to the user. In foreground this logic works fine and with battery optimization off notifications are firing in time and with the exact frequency on Android emulator and my physical device.
In background and/or with off-screen notifications already scheduled work fine but if user doesn't open the app again there will never be a new scheduled notification. So my goal is to use Capacitor Background Runner with built-in interval which should make the API call and schedule the next notification. I managed to make it work in this way:
capacitor.config.json
"plugins": {
...
"BackgroundRunner": {
"label": "com.myproject.background.task",
"src": "background.js",
"event": "myCustomEvent",
"repeat": true,
"interval": 5,
"autoStart": true
},
...
}
public/background.js
addEventListener('myCustomEvent', (resolve, reject, args) => {
CapacitorNotifications.schedule([
{
id: 100,
title: 'myCustomEvent public',
body: 'New scheduling available',
scheduleAt: new Date(Date.now() + 10000),
},
]);
resolve();
});
The interval
isn't accurate but even a random periodical run is enough for my goal.
As APIs here are limited and I cant just import my async calculations method, I need to rewrite it, fetch endpoint passing some variables and schedule a new notification using CapacitorNotifications.schedule
.
I really like capacitor, it makes it easy to develop apps but it's really hard to find examples of code implementation.
My questions are:
dispatchEvent
without success)Thank you!
I finally managed to solve this by using "BackgroundRunner.dispatchEvent" and "CapacitorKV". When we run the UI we can send the event manually with some data, then we can store it with CapacitorKV api so it remains inside our runner.js and we can restore it.
details.customData
with BackgroundRunner.dispatchEvent
runner.js
and if args.customData
is available, save it with CapacitorKV.set('yourKey', JSON.stringify(customData))
args.customData
so you can retrive the last one you saved by using JSON.parse(CapacitorKV.get('yourKey'))
capacitor.config.json
"plugins": {
...
"BackgroundRunner": {
"label": "com.myproject.background.task",
"src": "runner.js",
"event": "myCustomEventWithReturnData",
"repeat": true,
"interval": 5,
"autoStart": true
},
...
}
App.svelte UI
import { Capacitor, Plugins } from '@capacitor/core';
const { App, BackgroundRunner } = Plugins;
const execBackgroundRunner = () => {
BackgroundRunner.dispatchEvent({
label: 'myCustomEventWithReturnData',
event: 'myCustomEventWithReturnData',
details: {
// custom data
alerts,
},
});
}
if (Capacitor.isNativePlatform()) {
// runner.js background service on app start and send the data you need
execBackgroundRunner();
// BONUS: when you resume your app, force runner.js event again if needed
App.addListener('resume', () => {
execBackgroundRunner();
});
}
runner.js
addEventListener('myCustomEventWithReturnData', async (resolve, reject, args) => {
let alerts = [];
const hasData = !!args && Array.isArray(args.alerts);
// data sent from UI
if (hasData) {
alerts = args.alerts;
let str = JSON.stringify(args.alerts);
CapacitorKV.set('alerts', str)
} else {
// process called in background without any data
// check if data was already saved from UI before
const kvObj = CapacitorKV.get('alerts');
if (kvObj && kvObj.value) {
try {
alerts = JSON.parse(kvObj.value);
} catch (err) {
// invalid JSON str
}
}
}
}
CapacitorNotifications.schedule([
{
id: 100,
title: `myCustomEventWithReturnData alerts: ${alerts.length}, has data ? ${hasData ? 'YES' : 'NO'}`,
body: 'Capacitor background notification',
scheduleAt: new Date(Date.now() + 5000), // show after 5 seconds
},
]);
resolve();
});