44

While implementing the chrome push notification, we were fetching the latest change from our server. While doing so, the service-worker is showing an extra notification with the message

This site has been updated in the background

Already tried with the suggestion posted here https://disqus.com/home/discussion/html5rocks/push_notifications_on_the_open_web/
But could not find anything useful till now. Is there any suggestion ?

Hakan Fıstık
  • 16,800
  • 14
  • 110
  • 131

7 Answers7

34

Short Answer: You should use event.waitUntil and pass a promise to it, which returns showNotification eventually. (if you have any other nested promises, you should also return them.)

I was expriencing the same issue but after a long research I got to know that this is because delay happen between PUSH event and self.registration.showNotification(). I only missed return keyword before self.registration.showNotification()`

So you need to implement following code structure to get notification:

var APILINK = "https://xxxx.com";
 self.addEventListener('push', function(event) {
      event.waitUntil(
          fetch(APILINK).then(function(response) {
               
        return response.json().then(function(data) {
                  console.log(data);
                  var title = data.title;
                  var body = data.message;
                  var icon = data.image;
                  var tag = 'temp-tag';
                  var urlOpen = data.URL;

                return  self.registration.showNotification(title, {
                      body: body,
                      icon: icon,
                      tag: tag
                  })
              });
          })
      );
  });

Minimal senario:

self.addEventListener('push', event => {
  const data = event.data.json();
  event.waitUntil(
    // in here we pass showNotification, but if you pass a promise, like fetch,
    // then you should return showNotification inside of it. like above example.
    self.registration.showNotification(data.title, {
      body: data.content
    })
  );
});
yaya
  • 7,675
  • 1
  • 39
  • 38
Yogendra
  • 2,139
  • 3
  • 14
  • 27
  • 6
    For anyone else that has this issue, it's not only "self.registration.showNotification" that needs to be returned. Every single nested function needs to return a promise. I found this out via this post: http://stackoverflow.com/a/33187502/1451657 – Aaron Apr 14 '16 at 01:49
  • @Yogendra: So I need to make a API for the content? – Mehul Tandale Oct 16 '16 at 14:12
  • 1
    @Mehul Tandale, Yes you need to make a API for content to show in notification slider. but now chrome started delivering arbitrary content (Payload) too, that you can further explore. – Yogendra Oct 17 '16 at 06:40
  • 1
    Thanks for clarifying @Yogendra – Mehul Tandale Oct 17 '16 at 13:21
  • I have a simplified answer, check below. –  Feb 21 '18 at 08:15
  • I have the same issue when handling web app foreground push notification with Firebase FCM JS SDK. When my web app is on foreground, I get the notification payload from onMessage() listener, then using this code block to show notification on notification tray, and finally I got 2 notification (one by myselft and one is "This site has been update..."): ```navigator.serviceWorker.ready.then(registration => { registration.showNotification(title, options); });``` – Dev Jan 22 '20 at 02:47
21

I've run into this issue in the past. In my experience the cause is generally one of three issues:

  1. You're not showing a notification in response to the push message. Every time you receive a push message on the device, when you finish handling the event a notification must be left visible on the device. This is due to subscribing with the userVisibleOnly: true option (although note this is not optional, and not setting it will cause the subscription to fail.
  2. You're not calling event.waitUntil() in response to handling the event. A promise should be passed into this function to indicate to the browser that it should wait for the promise to resolve before checking whether a notification is left showing.
  3. For some reason you're resolving the promise passed to event.waitUntil before a notification has been shown. Note that self.registration.showNotification is a promise and async so you should be sure it has resolved before the promise passed to event.waitUntil resolves.
owencm
  • 8,384
  • 6
  • 38
  • 54
16

Generally as soon as you receive a push message from GCM (Google Cloud Messaging) you have to show a push notification in the browser. This is mentioned on the 3rd point in here:

https://developers.google.com/web/updates/2015/03/push-notificatons-on-the-open-web#what-are-the-limitations-of-push-messaging-in-chrome-42

So it might happen that somehow you are skipping the push notification though you got a push message from GCM and you are getting a push notification with some default message like "This site has been updated in the background".

Ripon Al Wasim
  • 36,924
  • 42
  • 155
  • 176
Satyajit Dey
  • 310
  • 4
  • 7
  • 1
    What does GCM stand for? – Ripon Al Wasim Jul 02 '15 at 09:48
  • 4
    Note that you must in fact show a notification before resolving the promise you pass into event.waitUntil(). Essentially by passing a promise into that you're saying "I'm done when this resolves, and at that time feel free to verify I showed a notification to my users" – owencm May 10 '16 at 02:14
  • 1
    in other words, as mentioned in @Yogendra 's answer, you need to return the show notification so that the notification is solved before the promise is resolved – jaafar Nasrallah Dec 28 '16 at 11:33
  • That link is old, check my answer (2018) –  Feb 21 '18 at 08:15
  • This answer is valid also for firebase(FCM) notifiactions. – nilsmagnus Aug 29 '18 at 06:56
5

This works, just copy/paste/modify. Replace the "return self.registration.showNotification()" with the below code. The first part is to handle the notification, the second part is to handle the notification's click. But don't thank me, unless you're thanking my hours of googling for answers.

Seriously though, all thanks go to Matt Gaunt over at developers.google.com

self.addEventListener('push', function(event) {
  console.log('Received a push message', event);

  var title = 'Yay a message.';
  var body = 'We have received a push message.';
  var icon = 'YOUR_ICON';
  var tag = 'simple-push-demo-notification-tag';
  var data = {
    doge: {
        wow: 'such amaze notification data'
    }
  };

  event.waitUntil(
    self.registration.showNotification(title, {
      body: body,
      icon: icon,
      tag: tag,
      data: data
    })
  );
});

self.addEventListener('notificationclick', function(event) {
  var doge = event.notification.data.doge;
  console.log(doge.wow);
});
4

I was trying to understand why Chrome has this requirement that the service worker must display a notification when a push notification is received. I believe the reason is that push notification service workers continue to run in the background even after a user closes the tabs for the website. So in order to prevent websites from secretly running code in the background, Chrome requires that they display some message.

What are the limitations of push messaging in Chrome?

...

  • You have to show a notification when you receive a push message.

...

and

Why not use Web Sockets or Server-Sent Events (EventSource)?

The advantage of using push messages is that even if your page is closed, your service worker will be woken up and be able to show a notification. Web Sockets and EventSource have their connection closed when the page or browser is closed.

Community
  • 1
  • 1
Carl G
  • 17,394
  • 14
  • 91
  • 115
2

If you need more things to happen at the time of receiving the push notification event, the showNotification() is returning a Promise. So you can use the classic chaining.

const itsGonnaBeLegendary = new Promise((resolve, reject) => {
    self.registration.showNotification(title, options)
        .then(() => {
            console.log("other stuff to do");

            resolve();
        });
});

event.waitUntil(itsGonnaBeLegendary);
0

i was pushing notification twice, once in the FCM's onBackgroundMessage()

click_action: "http://localhost:3000/"

and once in self.addEventListener('notificationclick',...

 event.waitUntil(clients.matchAll({
        type: "window"
    }).then...

so i commented click_action, ctrl+f5 to refresh browsers and now it works normal

codmitu
  • 636
  • 7
  • 9