firebase / firebase-js-sdk

Firebase Javascript SDK
https://firebase.google.com/docs/web/setup
Other
4.82k stars 884 forks source link

iOS PWA notification click doesn't open the app with proper URL when the app is killed #7698

Open adampfw opened 11 months ago

adampfw commented 11 months ago

Operating System

iOS 16.6, iOS 16.4.1

Browser Version

iOS Safari 16.6, iOS Safari 16.4

Firebase SDK Version

9.22.2

Firebase SDK Product:

Messaging

Describe your project's tooling

App is build with Rails and Turbo, compiled with Webpack. Service Worker is pure JS file available at domain.com/firebase-messaging-sw.js.

Describe the problem

PWA notification click opens PWA with start url, it's not respecting correct URL passed in click_action property inside notification object.

It happens only when the PWA is killed - when the PWA is in the background it gets focused and I receive service worker message with notification-clicked type so I am able to do proper redirect.

It happens only on iOS devices - Android works perfect both when the app is killed and when the app is in the background.

Steps and code to reproduce issue

My manifest.json file has following code:

{
  "name": "My app",
  "short_name": "My app",
  "start_url": "/page",
  "scope": "/",
  "description": "My app description",
  "display": "standalone",
  "theme_color": "#ffffff",
  "background_color": "#ffffff",
  "icons": [
    {
      "src": "https://cdn.mydomain.com/icon192.png",
      "sizes": "192x192",
      "type": "image/png"
    },
    {
      "src": "https://cdn.mydomain.com/icon512.png",
      "sizes": "512x512",
      "type": "image/png"
    }
  ],
  "version": 1
}

My app registers Service Worker with:

navigator.serviceWorker.register('/firebase-messaging-sw.js', { scope: '/page' })

My Service Worker file is available at domain.com/firebase-messaging-sw.js with following code:

self.importScripts('https://www.gstatic.com/firebasejs/9.22.2/firebase-app-compat.js')
self.importScripts('https://www.gstatic.com/firebasejs/9.22.2/firebase-messaging-compat.js')

firebase.initializeApp({
  apiKey: '***',
  authDomain: '***',
  projectId: '***',
  messagingSenderId: '***',
  appId: '',
})

const messaging = firebase.messaging()

self.addEventListener('fetch', function () {})

self.addEventListener('install', function () {})

self.addEventListener('activate', function () {})

When app initializes it execute following code to get messaging token and send it to backend:

const messaging = getMessaging()

getToken(messaging, {
  serviceWorkerRegistration: serviceWorkerRegistration,
  vapidKey: '***'
}).then((token) => {
  registerTokenOnBackend()
})

Also the app has event listener to execute proper redirect on notificaton-click event:

 navigator.serviceWorker.onmessage = (event) => {
    if (event.data.messageType === 'notification-clicked') {
      if (location.href !== event.data.notification.click_action) {
        Turbo.visit(event.data.notification.click_action)
      }
    }
  }

Backend sends FCM notification with following payload:

title: "Hello!",
body: "It's me!",
icon: "https://www.domain.com/favicon.png",
click_action: "https://www.domain.com/page/url_i_want_to_visit"

I receive corrent notification both on Android and iOS devices. But when I click a notification:

On Android devices when the app is killed

adampfw commented 11 months ago

Issue update:

Looks like the same thing happens when iOS app is in the background for several hours (about 6) - in this case notification click also opens the PWA on the root URL, without respecting click_action URL from notification payload.

adampfw commented 9 months ago

@jbalidiong Do you have any info or update about this one?

Merwan1010 commented 8 months ago

@adampfw Can you share a github link ? Thank you

Cyril212 commented 7 months ago

Have the same issue. Hope it will be resolved soon.

givip commented 1 month ago

The root issue discussion is here https://bugs.webkit.org/show_bug.cgi?id=268797

dlarocque commented 2 weeks ago

The root issue discussion is here https://bugs.webkit.org/show_bug.cgi?id=268797

@givip I was relieved to see that there's already an open WebKit bug for this, but unfortunately I can't reproduce the behaviour described in the bug. I've tried a variety of cases, and notificationclick events seem to always be fired. I'm interested to see whether the WebKit team can reproduce this.

dlarocque commented 2 weeks ago

Hey @adampfw, are you still experiencing this issue?

Could you confirm whether you're able to reproduce the behaviour described in the open WebKit bug https://bugs.webkit.org/show_bug.cgi?id=268797?

givip commented 2 weeks ago

@dlarocque You can reproduce it by sending push notification to the killed Mobile Safari PWA ONLY.

Code with the problem is here https://github.com/firebase/firebase-js-sdk/blob/4ff947408728ce4ae20229d7eb0cd71c3e65c885/packages/messaging/src/listeners/sw-listeners.ts#L145

getWindowClient() is not null, but the window is not fully ready to receive channel messages as described here https://bugs.webkit.org/show_bug.cgi?id=268797#c21

In fact, notificationclick is always called, but firebase logic behind this click is not properly working.

We workarounded this issue by posting channel message again after a 5 sec delay.

adampfw commented 2 weeks ago

@dlarocque I'm still expecting the issue. I agree with @givip.

dlarocque commented 2 weeks ago

@dlarocque You can reproduce it by sending push notification to the killed Mobile Safari PWA ONLY.

Code with the problem is here

https://github.com/firebase/firebase-js-sdk/blob/4ff947408728ce4ae20229d7eb0cd71c3e65c885/packages/messaging/src/listeners/sw-listeners.ts#L145

getWindowClient() is not null, but the window is not fully ready to receive channel messages as described here https://bugs.webkit.org/show_bug.cgi?id=268797#c21

In fact, notificationclick is always called, but firebase logic behind this click is not properly working.

We workarounded this issue by posting channel message again after a 5 sec delay.

As I mentioned, I am not able to reproduce the bug- I've tested this on a PWA. It may be that I'm not able to reproduce this on my device because my window client just happens to be ready to receive messages by the time they're sent and so the timing issue doesn't arise.

Thanks for sharing the workaround you're using- hopefully this is useful for others who are seeing this issue. Just to surface it here, this is the workaround suggested in the Bugzilla thread (https://bugs.webkit.org/show_bug.cgi?id=268797#c20):

self.addEventListener('notificationclick', (event) => {
  let client_count = 0;
  return new Promise((resolve, reject) => {
    clients.matchAll({ 
      type:'window', 
      includeUncontrolled:true // optional based on your requirements
    })
    .then((client_list) => {
      client_list.forEach((client) => {
        if (client.url == YOUR_URL) { // or similar based on your requirements
          client_count++;
          delayed_function(() => { client.postMessage(`Scenario A ${event.notification.title}`); });
        }
      });
      if (!client_count) {
        clients.openWindow(YOUR_URL)
        .then((client) => {
          delayed_function(() => { client.postMessage(`Scenario B ${event.notification.title}`); });
        });
      }
    })
    .catch((e) => {
      console.error('clients.matchAll error', e);
    });
    if (client_count) {
      resolve();
    } else {
      reject();
    }
  });
});

function delayed_function(f) {
  setTimeout(() => {
    f();
  }, 3000); // guestimate - ymmv but if it doesn't fire then make this longer
}
dlarocque commented 2 weeks ago

It looks like this WebKit bug is causing other issues in our SDK: https://github.com/firebase/firebase-js-sdk/issues/8002

dlarocque commented 1 week ago

This has been added to a list of known issues with FCM in iOS PWAs caused by WebKit bugs in our Wiki: https://github.com/firebase/firebase-js-sdk/wiki/Known-Issues