ユーザーがアクティブな場合、iOS通知はプッシュ許可を失う可能性があります

iOSの通知は、通知が抑制されるとプッシュ権限を失う可能性があります。Discourseのコードは、オプションで通知を抑制するように構成されており、これが3回発生するとプッシュ権限を失います。

以下は、プッシュ通知サービスワーカーのコードです。

このコードには、178行目に重大なバグがあります。そこで、サービスワーカーはユーザーがアクティブかどうか(つまりアイドル状態でないか)をチェックします。その場合、通知を表示せずにプッシュイベントはfalseを返します。

payload.hide_when_activeもチェックしますが、hide_when_active常にtrueであることが判明したため、ユーザーがアクティブな場合、このコードは常にfalseを返します。)

Appleはサイレントプッシュを禁止しており、通知が表示されないイベントが3回発生するとプッシュ権限が取り消されます

これは、プッシュ通知に関するAppleの規則では許容されません。

https://webkit.org/blog/12945/meet-web-push/

電力とプライバシー

WebKitオープンソースプロジェクトとAppleは、プライバシーを基本的な人権として扱っています。Webプラットフォームの他の特権機能と同様に、プッシュサブスクリプションをリクエストするには、明示的なユーザー操作が必要です。また、userVisibleOnlyフラグをtrueに設定し、プッシュメッセージに応答して常に通知を表示することで、その約束を果たす必要があります。

Web Push APIは、サイレントバックグラウンドランタイムの招待ではありません。それはユーザーの信頼を損ない、ユーザーのバッテリー寿命に影響を与える可能性があるためです。

userVisibleOnlyの約束の違反は、プッシュサブスクリプションの取り消しにつながります。

(私の強調)

これは、AppleのWWDCビデオ「Web Pushes」の9:57でさらに詳しく説明されています。

https://developer.apple.com/videos/play/wwdc2022/10098/?time=596

プッシュのサブスクライブをリクエストしたときに、JavaScriptが常にユーザーに表示されることを約束したことを思い出してください。これは、各プッシュに応答して常にプラットフォームネイティブ通知を表示する必要があることを意味します。プッシュイベントハンドラーでできるだけ早くこれを行うのが最善です。

…そして13:35で:

https://developer.apple.com/videos/play/wwdc2022/10098/?time=814

プッシュサブスクリプションをリクエストする方法のコードを示したときに言及したように、プッシュがユーザーに表示されることを約束する必要があります。プッシュイベントを処理することは、JavaScriptがサイレントバックグラウンドランタイムを取得するための招待ではありません。そうすることは、ユーザーの信頼とユーザーのバッテリー寿命の両方を損なうことになります。プッシュイベントを処理する場合、実際にはNotification Centerに通知を投稿する必要があります。他のブラウザは、プッシュをユーザーに表示するという約束に違反することに対する対策をすべて講じており、Safariも同様です。macOS Venturaのベータ版では、通知を適時に投稿できなかった3回のプッシュイベントの後、サイトのプッシュサブスクリプションは取り消されます。再度、権限ワークフローを通過する必要があります。

(私の強調)

Appleは通知を閉じた後ではなく、すぐに通知を表示することを推奨しています

Appleが推奨するコードは、11:39で次のようになっています。

https://developer.apple.com/videos/play/wwdc2022/10098/?time=699

self.addEventListener('push', (event) => {
    let pushMessageJSON = event.data.json();

    // Our server puts everything needed to show the notification
    // in our JSON data.
    event.waitUntil(self.registration.showNotification(pushMessageJSON.title, {
        body: pushMessageJSON.body,
        tag: pushMessageJSON.tag,
        actions: [{
            action: pushMessageJSON.actionURL,
            title: pushMessageJSON.actionTitle,
        }]
    }));
}

プッシュをサブスクライブしたときに、JavaScriptが常にユーザーに表示されることを約束したことを思い出してください。これは、各プッシュに応答して常にプラットフォームネイティブ通知を表示する必要があることを意味します。プッシュイベントハンドラーでできるだけ早くこれを行うのが最善です。

Discourseのコードは、推奨されるベストプラクティスに従っていません。Discourseのコードは、まずすべての通知を閉じ、その後通知を表示します。

Discourseは、プッシュイベントに応答して常にshowNotificationを呼び出す必要があり、できるだけ早く行う必要があります。

「いいね!」 8

@Falco / @featheredtoast これについてどう思いますか?

終了チェックとアイドルチェックの両方を削除した場合、どのような影響がありますか?

全体として、このアイドルチェックについてはまったく確信が持てず、混乱を招くだけのように感じます。

最悪の場合、AndroidまたはDesktop(Safari以外)でこれを必須とする場合は、ここに条件を追加することができますが、私の感覚では、単にコードを削除すべきです。

素晴らしいデバッグです @dfabulich :hugs:

「いいね!」 3

showNotification を呼び出した であれば、通知を閉じることは問題ない、ということですね。\n\n…しかし、通知を閉じることの必要性が全く理解できません。通知トレイに溜めておいても良いのではないでしょうか。\n\nhttps://developer.mozilla.org/en-US/docs/Web/API/Notification/close\n\n> 注意: このAPIは、単に一定時間後に通知を画面から削除するために使用すべきではありません。このメソッドは通知トレイからも通知を削除するため、ユーザーが最初に表示された通知を操作できなくなります。このAPIの有効な使用例としては、もはや関連性のない通知を削除することです(例:メッセージアプリの場合、ユーザーがすでにウェブページで通知を読んだ場合や、音楽アプリで次の曲がすでに再生されている場合)。

「いいね!」 3

この週末にこの件について読んだことから、プッシュイベントハンドラでasync/promisesを使用することさえできないことがわかりました。すぐに表示しないと、Appleは通知権限を剥奪します。

これはすべて、@dfabulichが言っていることと一致しています

ここでは「賢すぎる」のだと思います。すべてのチェックを削除して、通知を表示するだけでよいはずです。

「いいね!」 6

@dfabulich、これを発見してくれて本当にありがとう!

「いいね!」 3

はい、これに対するPRがあります。

@Falco / @featheredtoast これをマージすべきでしょうか?

プッシュ通知をスキップする必要がある場合は、クライアントではなくサーバーで行う必要があります。折りたたみロジックは常にいくらか疑問があり、アプリは通常それを実行しません。

「いいね!」 2

確かに、通知を表示する必要がある場合にのみプッシュする方がはるかに理にかなっています。最終的には他の場所での過剰な通知を抑制すべきですが、ここではマージしても問題ありません :+1:

通知の折りたたみについてですが、失われるのは残念ですが…理想的には、ログイン中のデバイスで通知を確認していくにつれて、他のアプリのように自動的に消去できるようにしたいですが、それには追加の労力がかかるかもしれません。ここでの意図は、古い通知が積み重なるのを防ぐという点では同じです。これも問題ありませんが、後でどれだけ問題を引き起こすかによって、再検討する必要があります。

「いいね!」 2