Can a PWA schedule notifications when closed? - progressive-web-apps

I'm developing a TODO-list PWA which should display the tasks of the day every day at 8:00am, even when the app is closed (similar to an alarm clock).
Is there a way to achieve this with PWAs? (Mainly targeting Chrome/Android)
So far, in my service worker I have (simplified)
self.addEventListener("message", (event) => {
// Workflow: The app sends a message that the timer should be set.
const prom = new Promise((resolveIt) =>
self.setTimeout(() => showAlarmAndResolve(resolveIt, event.data), '<timeout_in_ms>')
);
// tell the service worker to remain alive until promise is resolved
event.waitUntil(prom);
}
});
async function showAlarmAndResolve(resolveIt, text) {
const options = {
body: text,
tag: "todo-alert",
};
await self.registration.showNotification(
"Your tasks for today",
options
);
resolveIt(); // allow the service worker to terminate
}
Without the event.waitUntil, the notification is not displayed when I close the app on my phone (or close the browser tab), with it, it is displayed.
However, keeping the service worker alive for half a day somehow seems like a really bad idea - and furthermore, I read (here: Best practice for keeping timer running in PWA) that on Android the service worker might terminate anyway after ~20 minutes.
How can I implement such an alarm clock like functionality?
Some (older) questions do not really give that much help / hope...
Background events in progressive web apps? (building an alarm clock app)
Is it possible to set the alarm by PWA? (building a timer/alarm clock app)

Chrome experimented with this functionality via Notification Triggers, and after running an origin trial, the team decided not to move ahead. If anything changes, there will be updates in the Chromium issue tracker.
You can achieve similar once-a-day functionality, without any guarantees about when during the day the event will fire, by using the Periodic Background Sync API. If your users needed exact control over when the notification is shown, this wouldn't help. Additionally, it's only supported if a user has gone through the installation flow for your PWA.

Related

MIKROS Analytics not showing (Unity project)

I do not see any tracked events on MIKROS Analytics. I have followed all the steps from the Get Started guide.
My app is approved and I already have an "appGameId" and my "apiKey". I am using the Production "apiKey" for now.
Some other information. I have auto initialization enabled under Mikros Settings in the Inspector. I also tested without auto initialization and initialized the MIKROS SDK at app start like this,
MikrosManager.Instance.InitializeMikrosSDK();
I tried to log a custom event like this,
// log events
AnalyticsController.LogEvent("mikros_analytics_test", "parameter", "app_open", (Hashtable customEventWholeData) =>
{
// handle success
},
onFailure =>
{
// handle failure
});
Any idea why I am unable to see any events on the dashboard? Or what step(s) I am missing?
Don't forget to define the namespaces at the top of your scripts as well.
using MikrosApiClient;
using MikrosApiClient.MikrosAnalytics;
It takes some time after the app is not being used before all events are fully logged. However, you can manually log them by calling:
AnalyticsController.FlushEvents();
Also make sure you have the following at the top of your script:
using MikrosApiClient;
using MikrosApiClient.MikrosAnalytics;
Ref: https://developer.tatumgames.com/documentation/log-events
Similar to other analytic services such as Firebase Analytics or Unity Analytics, MIKROS Analytics queues your events and will send them out in batches. There are certain criteria required in order for the batched events to be sent off.
While both Firebase and Unity can take up to a maximum of 24 hours before events can be viewed on their dashboards, MIKROS has a max time of about 10 hours. However, in most cases MIKROS tracked events will be posted more immediately. The 10 hour timeframe is when the entire batch of events is forced cleared from the queue.
If you want more control of how immediate events are sent off you could force event uploads using flush(). Call the method after you have tracked your events.
MikrosManager.Instance.AnalyticsController.FlushEvents();
It is not really necessary, but it's an option available to you.

Which service worker event indicates it has controlled the page, and will intercept web traffic?

In my web app, some web requests must be intercepted and modified by the service worker, otherwise the requests will fail. This is especially important on the very first visit for a new user. I use clientsClaim() to ensure that.
Since I need to make sure the service worker is ready before I make the request, I tried to wait for navigator.serviceWorker.ready:
await navigator.serviceWorker.ready;
fetch(myRequest);
However, I found it doesn't work as intended. The very first request on the first visit is not intercepted. So I tried to add some wait time:
await navigator.serviceWorker.ready;
await twoSeconds();
fetch(myRequest);
This works, but it damages user experience because it delays the first meaningful UI. On the other hand, I also can't be sure 2 seconds is long enough for every computer.
What's the event that can tell me as soon as the the sw is ready to intercept traffic? It's only a problem on the first visit, but ideally the event will fire on every reload, because the code is easier to write if I simply await the same thing on every visit.
I think you're looking for a promise that you can use to signal when the current page is under control of a service worker.
This can be achieved via
await new Promise(r => {
if (navigator.serviceWorker.controller) return r();
navigator.serviceWorker.addEventListener('controllerchange', e => r());
});
// At this point, the page will be controlled by a service worker.
This code is adapted from this GitHub discussion, and there's more context there.
Generally speaking, it's not great to design a page that will only work if it's controlled by a service worker, since service workers are intended to be progressive enhancement, rather than a core requirement. But if you have that use case, the code above will help.

Progressive Web App: skipWaiting() with multiple service worker versions

[CONTEXT]
I worked through Jake Archibald's fantastic Udacity course found here: Offline Web Applications. His work provides a Toasts dialog alerting the user that there is an update available, and they are invited to update:
Refresh / Dismiss Dialog
While this dialog is available to the user, there's a corner case on hand that I can't seem to resolve:
The service-worker can be updated any number of times prior to the client updating the local instance, pushing the numbered version of the service worker past 'just one more'. For example, the current and active service worker is #821, while the service worker that is waiting is now #824
active and waiting service workers
[PROBLEM]
I cannot find the right way to alert the browser that the next service worker to install needs to be #824, instead of #822, the dialog-box + PWA tell me that the current browser is 'redundant', and that I can't get to service-worker #824 without refreshing, and then clicking the update button.
I can recreate this with any version of Jake's code once the service-worker is set, and skipWaiting() is introduced.
I literally just want to be able to cover the corner case where the service-worker is updated 2 or more times before the user decides to update their local PWA.
You can find Jake's code on github: Jakearchibald/wittr
[ASK]: Has anyone found a solution for this corner case? If so, how do you solve it? What I'm seeing doesn't make sense as the service-worker lifecycle seems to respected per Googles documentation: service-workers/lifecyle
I did quite a bit of additional reading/research and found the following discussion threads on Github:
- Provide an easier way to listen for waiting/activated/redundant Service Workers
- Immediate Service Worker
- Recommended Approach for Refreshing Page on new SW
- Provide a one-line way to listen for a waiting Service Worker
It looks like this idea was brought up in 2017, and has for the most part gone stale. However, you can double down on using
navigator.serviceWorker.waiting.then(reg => {
if (confirm('refresh now?')) reg.waiting.postMessage('skipWaiting');
});
That will give you the ability to listen for a new Web Worker, after activating Web Worker #1, then setting Web Worker #2 to redundant, and moving Web Worker #3 into a waiting state. It's obtuse and indirect, but at least you can now move the 3# thread up and into the right slot.
A real shout-out to dfabulich, Matt Gaunt, and Beatrix Perez

Recurring function at date/time

I'm trying to call a function when my macOS application is in any state, including terminated. Here is what i'm trying to accomplish:
Schedule a function (much like DispatchQueue.main.asyncAfter()) to run daily at a given time (let's say 9AM). I would like to add a feature to my application that allows a user to pick a time of day, and have an Alamofire POST request run at that time every day.
I have tried using a Runloop, and more recently Grand Central Dispatch:
DispatchQueue.main.asyncAfter(wallDeadline: DispatchWallTime.now() + .seconds(60)) {
//Alamofire
}
I can easily accomplish this while the application is running with a timer, but have yet to find a way to accomplish this in the background, with the app running.
This may be pretty heavy to implement (i.e. not straightforward), but if you want a task to run even if your app is terminated, you might need to consider writing your own LaunchAgent.
The trick here would be for the agent to be able to interact with your application (retrieving or sending shared information).

Google Calendar API Watch Channel Should I get a push notification for each resource?

When subscribing to Calendar Channel, https://developers.google.com/google-apps/calendar/v3/push, should I expect to get a push notification for each new event created?
In testing, if I create 21 events (each at 2 second intervals), I get about 7 notifications.
It's hard to tell from the docs if I should be getting a notification for each event created, or if I should use the notification to do a sync?
What are you guys doing for your apps?
Google Calendar watches only make sense when you're also using the sync token feature. They are basically instructions to do another sync, which will bring in 1 or more event changes. The reason you got less than 21 messages is because Google rate limits the messages (in your case to what looks like every ~3 seconds... my experience is closer to 10s).
The callout about not being 100% reliable is actually a bit of a different concern than the "only 7" callbacks issue. Until yesterday, my experience was that watches were 99.9% reliable in terms of delivering a notification within a few seconds of a change. But for the 0.1%, you'll want to have some sort of fallback force sync... could be once an hour, could be upon login, etc.
I've noticed similar. Scroll down to the very bottom of that page you linked:
Notifications are not 100% reliable. Expect a small percentage of messages to get dropped under normal working conditions. Make sure to handle these missing messages gracefully, so that the application still syncs even if no push messages are received.
If you've called watch on the calendar to register/create a notification channel, I'm assuming they're doing some throttling/bucketing to push out notifications at a coarse-grained level. Testing this out myself but I believe the original intention of asking for incremental changes via setting timeMin equal to a previously requested syncTime still holds true:
https://developers.googleblog.com/2013/07/google-calendar-api-push-notifications.html