Have you figued this out? I have the same problem, I do have the app installed on the homescreen, I can see that the PWA is subscribed, but I dont get the notification on ios. Android works fine:
My backend logs:
2025-07-07T14:33:18.228Z INFO 1 --- [app] [nio-8080-exec-7] d.v.app.service.NotificationService : Subscribed to Push notification
2025-07-07T14:33:45.400Z INFO 1 --- [app] [nio-8080-exec-1] d.v.app.service.NotificationService : Sending Notification for type: NEW_POLL
2025-07-07T14:33:45.410Z INFO 1 --- [app] [nio-8080-exec-1] d.v.app.service.NotificationService : Found 2 subscriptions for type NEW_POLL
2025-07-07T14:33:45.416Z INFO 1 --- [app] [nio-8080-exec-1] d.v.app.service.NotificationService : Sending Notification via pushService
2025-07-07T14:33:46.143Z INFO 1 --- [app] [nio-8080-exec-1] d.v.app.service.NotificationService : Sending Notification via pushService
My sw.js, inspired by David Randoll above:
import { precacheAndRoute } from 'workbox-precaching';
precacheAndRoute(self.__WB_MANIFEST);
/**
* Fired when the service worker is first installed.
*/
self.addEventListener("install", () => {
console.info("[Service Worker] Installed.");
});
/**
* Fired when a push message is received from the server.
*/
self.addEventListener("push", function (event) {
if (!event.data) {
console.error("[Service Worker] Push event had no data.");
return;
}
const payload = event.data.json();
const notificationTitle = payload.title ?? "Varol Fitness";
const notificationOptions = {
body: payload.body ?? "You have a new message.",
icon: payload.icon ?? "/web-app-manifest-192x192.png",
badge: payload.badge ?? "/web-app-manifest-192x192.png",
image: payload.image,
data: {
url: payload.url ?? "/dashboard", // Default URL if none is provided
},
};
event.waitUntil(
self.registration.showNotification(notificationTitle, notificationOptions)
);
});
/**
* Fired when a user clicks on the notification.
*/
self.addEventListener("notificationclick", function (event) {
console.log("[Service Worker] Notification clicked.");
event.notification.close();
event.waitUntil(
clients
.matchAll({ type: "window", includeUncontrolled: true })
.then(clientList => {
const urlToOpen = event.notification.data.url;
if (!urlToOpen) {
console.log("[Service Worker] No URL in notification data.");
return;
}
for (const client of clientList) {
if (client.url === urlToOpen && "focus" in client) {
console.log("[Service Worker] Found an open client, focusing it.");
return client.focus();
}
}
if (clients.openWindow) {
console.log("[Service Worker] Opening a new window to:", urlToOpen);
return clients.openWindow(urlToOpen);
}
})
);
});