Подписка на push-уведомления в iOS PWA незаметно не работает из-за того, что SW не контролирует приложение

Привет :wave:

Мы столкнулись с необычным поведением (возможно, это баг…) при настройке push-уведомлений в iOS PWA. Интерфейс ведёт себя так, будто подписка на push-уведомления уже настроена, но на самом деле ничего не произошло, и пользователь никогда не получит push-уведомление.

(tl;dr: я, кажется, знаю, как это исправить, и могу предоставить PR)

Шаги для воспроизведения :footprints:

Проблема заключается в включении push-уведомлений сразу после установки PWA на iOS.

  1. Откройте ваш экземпляр Discourse в мобильном Safari на iOS.
  2. Откройте меню «Поделиться» и выберите «Добавить на главный экран».
  3. Откройте новый PWA с главного экрана.
  4. Вы увидите баннер с вопросом, хотите ли вы включить push-уведомления.
  5. Нажмите на ссылку «Включить уведомления».
    1. Появится системное диалоговое окно: «xyz» хочет отправлять вам уведомления — примите это.
  6. Перейдите в настройки уведомлений: вы увидите, что live notifications не включены.
    1. При проверке на сервере вы также не найдёте новой записи PushSubscription для этого пользователя.

Ожидаемое поведение :books:

После шага 5 вы уже должны получить push-уведомление о том, что уведомления включены. В базе данных также должна появиться новая запись PushSubscription для текущего пользователя, а его настройка получения live notifications в PWA должна быть включена.

Проблема :bug:

В только что запущенном PWA сервис-воркер установлен и активирован, но ещё не контролирует страницу. Из-за этого проверка в lib/push-notifications.js, функция isPushNotificationsSupported(), всё ещё возвращает false, поскольку navigator.serviceWorker.controller возвращает null. Вот эти две важные строки:

navigator.serviceWorker.controller &&
navigator.serviceWorker.controller.state === "activated"

В результате интерфейс сообщает пользователю, что всё в порядке, но он никогда не получит push-уведомление.

При первой загрузке PWA не контролируется, и вызов subscribe() никогда не выполняется, даже несмотря на то, что пользователь дал разрешение… :bug:

Пользователи могут исправить проблему самостоятельно, если знают как:

  1. Полностью закрыть PWA (смахнуть его…).
  2. Снова открыть PWA.
  3. Перейти в настройки уведомлений.
  4. Снова включить live notifications.
  5. → На этот раз должна появиться новая запись PushSubscription, и уведомления будут настроены.

Возможное исправление :adhesive_bandage:

Я обошёл эту проблему, зарегистрировав дополнительный сервис-воркер, который вызывает skipWaiting() и clients.claim() при установке/активации. Благодаря этому он сразу берёт контроль над PWA, подписка выполняется, и создаётся соответствующая запись PushSubscription.

Я развернул это как «горячее исправление» в рамках одного из своих кастомных плагинов, чтобы исправить ситуацию для моих пользователей, не углубляясь в форк ядра Discourse…

Это всего лишь два изменения:

# plugin.rb
register_service_worker "push-notification-setup.js"
// assets/push-notification-setup.js
self.addEventListener("install", function () {
  self.skipWaiting();
});
		
		
self.addEventListener("activate", function (event) {	
  event.waitUntil(self.clients.claim());
});

Однако я считаю, что это исправление должно быть включено в ядро Discourse. В текущем виде плагин берёт на себя излишний контроль над PWA. Я могу предоставить PR, который скорректирует проверки в ядре Discourse так, чтобы подписка работала корректно. Буду рад обсудить это здесь, но не стесняйтесь высказать своё мнение по этой теме.

1 лайк

Это PR с исправлением для ядра Discourse → FIX: Allow push notification subscriptions on first launch of iOS PWA · Pull Request #39885 · discourse/discourse

Оно устраняет проблему, описанную здесь, когда подписка тихо не удаётся и не достигает сервера. Однако для абсолютной безопасности всё ещё не хватает того, чтобы первое полученное push-уведомление можно было обработать внутри приложения без контроля со стороны service worker, то есть без перезапуска приложения пользователем.

Я мог бы добавить это в дополнение к своему PR. Но я не на 100% уверен, что именно такое исправление является желаемым.

1 лайк