You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
SwPush.messages and SwPush.notificationClicks don't work properly on iOS #54014
Which @angular/* package(s) are the source of the bug?
service-worker
Is this a regression?
No
Description
The messages Observable from SwPush doesn't seem to emit anything when using Push API on iOS. The notifications are correctly set up (and they work) but comparing Windows, Android and iOS side by side shows that only iOS doesn't receive updates on SwPush.messages. SwPush.notificationClicks does work but only when the app is closed and you swipe right on the notification to activate the "open" option. Tapping on the notification (or swiping right) while the client app is actively running doesn't make notificationClicks to emit.
Please provide a link to a minimal reproduction of the bug
The repo for reproduction is also deployed here https://ng-web-push-example.netlify.app for testing (check the readme for explanation). I'm testing this on a real iPhone with iOS v17.2.1 (latest at the moment)
I have similar problem and i am not using firebase or angular, btw
the problem is that when ios pwa is open nothing happens after clicking on the notification that appears. But if the pwa app is closed, then clicking on the link in the notification happens correctly.
Also it works ok on my desktop web app (using Safari with macOS Sonoma)
Here is my code
self.addEventListener('notificationclick',(event: NotificationEvent)=>{try{event.notification.close();}catch(e){// do nothing}letredirectURL: string|null=null;try{redirectURL=event.notification.data.RedirectURL;}catch(e){redirectURL=self.location.origin;}if(!redirectURL)returnevent.waitUntil(Promise.resolve());event.waitUntil(self.clients.openWindow(redirectURL).then((client)=>(client ? client.focus() : null)).catch((e)=>{// do nothing}));});
It seems like a bug or undocumented behaviour in WebKit.
But also what i noticed while debugging this is that when you recieve a notification while your ios pwa opened and focused new service worker instance is created. That is why your notificationclick event most probably triggered correctly but in another service worker context. You can double check it by connecting your phone to your macbook and go to Safari -> Develop -> <Your_iPhone_Name> and open it and wait for some time after you recieve a notification, you will see the new service worker instance is created
@ereshidov I have an iPhone but unfortunately not a MAC machine to debug with. I don't get the part "new service worker instance is created". The service worker instance is always one, but the instances of the client (app) can be many. Maybe you meant that a new client instance is created.
However, by looking at your example, it will always open new client window because you always call openWindow(). You need to find existing clients first, focus one if found or open a new window as a fallback. Besides that, by default nothing will happen if you click notification because it doesn't have any default behavior.
@ereshidov I have an iPhone but unfortunately not a MAC machine to debug with. I don't get the part "new service worker instance is created". The service worker instance is always one, but the instances of the client (app) can be many. Maybe you meant that a new client instance is created. However, by looking at your example, it will always open new client window because you always call openWindow(). You need to find existing clients first, focus one if found or open a new window as a fallback. Besides that, by default nothing will happen if you click notification because it doesn't have any default behavior.
Thanks for your response, based on your response i changed my code to this
// service-worker.tsself.addEventListener('notificationclick',(event: NotificationEvent)=>{try{event.notification.close();}catch(e){// do nothing}letredirectURL: string|null=null;try{redirectURL=event.notification.data.RedirectURL;}catch(e){redirectURL=self?.location?.origin;}if(!redirectURL)return;constmatchAllPromise=clients.matchAll().then((matchedClients)=>{// @ts-expect-errorconstfocusedClient=matchedClients.find((client)=>client?.focused);if(focusedClient){focusedClient?.postMessage({
redirectURL,});}elseif(redirectURL){returnclients.openWindow(redirectURL);}}).catch(()=>{// do nothing});event.waitUntil(matchAllPromise);});
also i am listening to thisfocusedClient.postMessage inside my javascript code
window.navigator.serviceWorker.addEventListener('message',(event)=>{const{ data }=event;if(data?.redirectURL){window.location.href=data.redirectURL;return;}})
It works ok both on desktop safari and desktop chrome, but again when i am using this on my iOS PWA (i am running this code in secure https context, btw) this does not work at all. When i was trying to debug this i saw 'message' event is never triggered inside my javascript code.
Everything is fine with RedirectURL when i clicking to my notification from iOS iPhone Notification Center
@ereshidov There are some flaws in the way you have updated the code and I assume they are the reason for the message not being posted.
First, you call find() on the clients array with client.focused as a condition. This will return only the focused windows/tabs. If you receive a notification and pull it, there is a possibility that the window loses focus (although you visually see it). Then you lose the window client object right there, so there's nobody to post message to. I would rewrite the code as follows:
constmatchAllPromise=clients.matchAll({includeUncontrolled: true}).then((matchedClients)=>{if(matchedClients.length>0){matchedClients[0].focus();// key changematchedClients[0].postMessage({message: 'some message',//key change});}else{returnclients.openWindow(redirectURL);}}).catch(()=>{// do nothing});
See how I added this option includeUncontrolled: true to matchAll(). I'm not 100% sure if it will make difference for you but I had situations where without it, I couldn't match my opened window. First key change is to add a message property to the options argument of postMessage() method because it's required and that's what gets transferred to the client. And the other key change is calling the .focus() method of the client which is what actually does the focusing (you don't need to do this hack on the client side to do the refreshing). Also I removed the if (redirectURL) part from the else statement because you already check for it before this section of code.
I hope it helps but let's cut this discussion because we are kind of drifting away of the main point of the issue which is related to the Angular service worker 😆 If you want to continue the discussion please hit me up on LinkedIn or Twitter
Hello guys, I am facing the same issue described, my pwa is working properly sending push notifications over Edge, Chrome but no working on IOS 17.2.1, I am testing with an Iphone 11, the app is added to the browser, I am getting the error:
WebPushError: Received unexpected response code
I am not getting any response code even. just that string.
I am implementing this function from the onInit appComponent() for handling the incoming push notifications on mobile:
async listenToPushNotifications(){
if (!this.swPush.isEnabled) {
alert('We are sorry but "Push Notifications" are not supported by this browser');
return;
}
this.swPush.messages.subscribe({
next: (message: any) => {
const title = message.notification.title;
const options = {
body: message.notification.body,
icon: message.notification.icon,
actions: message.notification.actions,
data: message.notification.data,
vibrate: message.notification.vibrate
};
window?.navigator?.serviceWorker.getRegistration().then(async (reg) => {
if (reg != null) {
await reg.showNotification(title, options);
}
});
return 'ok';
},
error: (err) => {
console.log(err)
}
});
}
When I use that function I am getting the same push notification multiple times on the laptop browser. Should I create a validation to check and let in run just if it is mobile device, maybe?
On the server side I have this endpoint method to push the message:
@destinotierra it's normal that your notification shows twice with that code. this.swPush.messages - that observable emits when a notification is received (you already see it). Inside the subscription of that observable you create a brand new observable, access the service worker and then you trigger another notification. This is not unexpected behavior, I would rather be surprised if you only see one notification in that case. In general the entire subscription on the client side is unnecessary.
@StefanNedelchev Thank you very much for the answer, I imagined that I did not need that function on the front-end because it is working properly without that, I just got confused after consulting different implementations and some of then talking about a different way of handling push for browser on mobile or something like that. I really appreciate your help!
PD. About the article in the UE... Just in shock right now, politics, no words, just continue coding... xD
Activity
StefanNedelchev commentedon Jan 21, 2024
I see (probably) the same issue reported here firebase/firebase-js-sdk#7309 . I don't use Firebase so it could be an issue with WebKit then?
ereshidov commentedon Jan 23, 2024
I have similar problem and i am not using firebase or angular, btw
the problem is that when ios pwa is open nothing happens after clicking on the notification that appears. But if the pwa app is closed, then clicking on the link in the notification happens correctly.
Also it works ok on my desktop web app (using Safari with macOS Sonoma)
Here is my code
It seems like a bug or undocumented behaviour in WebKit.
But also what i noticed while debugging this is that when you recieve a notification while your ios pwa opened and focused new service worker instance is created. That is why your
notificationclickevent most probably triggered correctly but in another service worker context. You can double check it by connecting your phone to your macbook and go to Safari -> Develop -> <Your_iPhone_Name> and open it and wait for some time after you recieve a notification, you will see the new service worker instance is createdStefanNedelchev commentedon Jan 23, 2024
@ereshidov I have an iPhone but unfortunately not a MAC machine to debug with. I don't get the part "new service worker instance is created". The service worker instance is always one, but the instances of the client (app) can be many. Maybe you meant that a new client instance is created.
However, by looking at your example, it will always open new client window because you always call openWindow(). You need to find existing clients first, focus one if found or open a new window as a fallback. Besides that, by default nothing will happen if you click notification because it doesn't have any default behavior.
ereshidov commentedon Jan 24, 2024
Thanks for your response, based on your response i changed my code to this
also i am listening to this
focusedClient.postMessageinside my javascript codeIt works ok both on desktop safari and desktop chrome, but again when i am using this on my iOS PWA (i am running this code in secure https context, btw) this does not work at all. When i was trying to debug this i saw 'message' event is never triggered inside my javascript code.
Everything is fine with
RedirectURLwhen i clicking to my notification from iOS iPhoneNotification Centerp.s. i found working solution from web version of telegram https://github.com/Ajaxy/telegram-tt https://github.com/Ajaxy/telegram-tt/blob/master/src/serviceWorker/pushNotification.ts i will check how they do this properly
StefanNedelchev commentedon Jan 24, 2024
@ereshidov There are some flaws in the way you have updated the code and I assume they are the reason for the message not being posted.
First, you call
find()on the clients array withclient.focusedas a condition. This will return only the focused windows/tabs. If you receive a notification and pull it, there is a possibility that the window loses focus (although you visually see it). Then you lose the window client object right there, so there's nobody to post message to. I would rewrite the code as follows:See how I added this option
includeUncontrolled: truetomatchAll(). I'm not 100% sure if it will make difference for you but I had situations where without it, I couldn't match my opened window. First key change is to add amessageproperty to the options argument ofpostMessage()method because it's required and that's what gets transferred to the client. And the other key change is calling the.focus()method of the client which is what actually does the focusing (you don't need to do this hack on the client side to do the refreshing). Also I removed theif (redirectURL)part from theelsestatement because you already check for it before this section of code.I hope it helps but let's cut this discussion because we are kind of drifting away of the main point of the issue which is related to the Angular service worker 😆 If you want to continue the discussion please hit me up on LinkedIn or Twitter
StefanNedelchev commentedon Feb 15, 2024
I updated the angular version info in the OP - updated to the latest 17.2.1 and the issue still persists.
The linked repo is also updated
destinotierra commentedon Feb 16, 2024
Hello guys, I am facing the same issue described, my pwa is working properly sending push notifications over Edge, Chrome but no working on IOS 17.2.1, I am testing with an Iphone 11, the app is added to the browser, I am getting the error:
WebPushError: Received unexpected response code
I am not getting any response code even. just that string.
I am implementing this function from the onInit appComponent() for handling the incoming push notifications on mobile:
When I use that function I am getting the same push notification multiple times on the laptop browser. Should I create a validation to check and let in run just if it is mobile device, maybe?
On the server side I have this endpoint method to push the message:
async function pushNotification(subscription)
{
const options = {
vapidDetails: {
subject: VAPID.MAILTO,
publicKey: VAPID.PUBLICKEY,
privateKey: VAPID.PRIVATEKEY
},
TTL: 60
}
}
My ngsw-worker.js file after building has the listener:
this.scope.addEventListener("push", (event) => this.onPush(event));
Hope you can help me. Cheers!
StefanNedelchev commentedon Feb 16, 2024
@destinotierra it's normal that your notification shows twice with that code.
this.swPush.messages- that observable emits when a notification is received (you already see it). Inside the subscription of that observable you create a brand new observable, access the service worker and then you trigger another notification. This is not unexpected behavior, I would rather be surprised if you only see one notification in that case. In general the entire subscription on the client side is unnecessary.Also a comment that's note entirely related to the bug but it's something we all need to know. As of iOS 17.4, PWA (add to home screen) support will be dropped for devices of the EU region: https://www.macrumors.com/2024/02/15/ios-17-4-web-apps-removed-apple/
destinotierra commentedon Feb 16, 2024
@StefanNedelchev Thank you very much for the answer, I imagined that I did not need that function on the front-end because it is working properly without that, I just got confused after consulting different implementations and some of then talking about a different way of handling push for browser on mobile or something like that. I really appreciate your help!
PD. About the article in the UE... Just in shock right now, politics, no words, just continue coding... xD
JeanMeche commentedon Aug 29, 2025
With #63364 we've announced that the service-worker package in now in maintenance mode and will not receive feature PRs and feature requests.
The message is also visible here: https://angular.dev/ecosystem/service-workers
angular-automatic-lock-bot commentedon Sep 29, 2025
This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.
Read more about our automatic conversation locking policy.
This action has been performed automatically by a bot.