Skip to content

After ~1 month, push to iOS Safari stops working #12

@jgarplind

Description

@jgarplind

Apologies in advance for convoluted issue that may not be within the scope of your library.

We are finding that push notifications stop being delivered to iOS devices after ~1 month, and the only solution seems to be for the users to re-add the app to their home screen.

Since the dashboard does not show delivery rates for iOS devices, we have no way to know if Pushy considers the messages to be delivered or not. When we fired a message from the test UI in the dashboard, we got a "message sent successfully" toast, but the message was never seen by the test user.

Rather than a fix for the root cause (which I assume to be on the iOS / Safari side of things, given similar reports for FCM-driven push (see e.g. firebase/firebase-js-sdk#8010)), I was hoping to know if there is any more information that you are able to retrieve / see on your side of things, e.g. delivery rates, error codes, or anything else that is not directly surfaced to us.

Note: We are using the fallback FCM solution, could that have any impact?

Sidenote: We are aware of the possibility that iOS stops sending notifications if we do not promptly display them to our users. However, (other than the FCM fallback) we believe that we do always show them promptly and should therefore not have this issue. Our service worker is based on yours with some added/altered domain-specific logic:

// Listen for incoming push notifications
self.addEventListener("push", function (event) {
  // Extract payload as JSON object, default to empty object
  var data = event.data.json() || {};

  // Notification title and body
  var title = data.title || "New message";
  var body = data.message || "";

  // Extract notification image URL
  var image = data.image;

  // Always navigate to chat page if no url is provided
  var fallbackUrl = "https://" + self.location.hostname + "/chat";

  // Notification options
  var options = {
    body: body,
    icon: image,
    data: {
      url: data.url || fallbackUrl,
      // Required for `Pushy.setNotificationListener` to work properly due to https://github.com/pushy/pushy-sdk-web/blob/351734d10785aec599152f5504f95e47be869190/src/lib/pushy.js#L218
      _pushy: true,
    },
  };

  // Support for notification collapsing
  if (data["_pushyCollapseKey"]) options.tag = data["_pushyCollapseKey"];

  // Wait until notification is shown
  event.waitUntil(self.registration.showNotification(title, options));

  // Set app badge count (if supported)
  // For now, we set a 1, not dealing with state at this point
  // Cleared when 'chat' is rendered
  navigator.setAppBadge(1);

  // Send to Pushy notification listener (if webpage is open)
  clients
    .matchAll({ includeUncontrolled: true, type: "window" })
    .then((clients) => {
      // Set pushy notification flag
      data._pushy = true;

      // Send to all open pages
      clients.forEach((client) => {
        client.postMessage(data, [new MessageChannel().port2]);
      });
    });

  // WebExtensions support
  // Dispatch event to current service worker
  serviceWorker.dispatchEvent(new CustomEvent("message", { detail: data }));
});

// Listen for notification click event
self.addEventListener("notificationclick", function (event) {
  // Hide notification
  event.notification.close();

  // Attempt to extract notification URL
  var url = event.notification.data.url;

  // Inspired by example from Mozilla: https://developer.mozilla.org/en-US/docs/Web/API/WindowClient
  const openChatPromise = clients
    .matchAll({
      type: "window",
      includeUncontrolled: true,
    })
    .then((clientList) => {
      // Inspired by: https://stackoverflow.com/a/75247524/4102048
      if (clientList.length === 0) {
        // App not open. Open it for the user
        return clients.openWindow(url).then((windowClient) => {
          // Focus the new window (if it exists)
          return windowClient ? windowClient.focus() : Promise.resolve();
        });
      }

      // Focus the existing app
      const client = clientList[0];
      return client.focus().then((resolvedClient) =>
        // Send a message to the client, asking it to navigate to the given URL
        resolvedClient.postMessage(
          { ...event.notification.data, action: "navigate" },
          [new MessageChannel().port2],
        ),
      );
    });

  // Clarification why `event.waitUntil` is required: https://stackoverflow.com/a/34250261/4102048
  event.waitUntil(openChatPromise);
});

Thankful for any light you could possibly shed on this issue!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions