📚

令和4年のPush通知を改めて整理する

2022/10/06に公開
1

はじめに

iOSのPush通知は定期的にアップデートを繰り返しており、常に最新の技術を追っていく必要があります。
本記事では令和4年時点での全体像把握のため、概要の整理を行いました。
※ あくまで全体感のキャッチアップを目的としているため、各トピックの詳細な解説は行っていません。

Push通知とは


  • サーバーサイドから、任意のタイミングでユーザーの特定端末に通知を送信できる仕組み
    • AppleのPush通知基盤であるAPNsに通知の送信をリクエストし、そこからユーザー端末に対してデータが送信される
  • 似たものとしてローカル通知があるが、こちらはアプリ内の特定タイミングで通知を登録するもの

公式ドキュメントはこちら

要素

APNs

  • Appleが提供しているPush通知基盤
  • ユーザー端末へのPush通知の送信はここを介して行われる
  • Production環境とSandbox環境がある
    • Production環境からは、本番用証明書で配布されたアプリにのみ通知を送信できる
    • 開発時はSandbox環境を用いることで、誤送信のリスクを最小化することができる

デバイストークン

  • プッシュ通知の送信対象端末を一意に識別するもの
  • クライアントアプリからAPNsに対してリクエストを行うことで取得できる
  • 下記の特徴があるため、サーバーサイドではユーザーに対して複数紐付けられるようにしておくのが良い
    • 端末が変わると別のトークンになる
    • アプリやOSの再インストールによって別のトークンになる
    • バックアップからの復元時に別のトークンになる
  • ログアウト済みユーザーに間違って通知を送ってしまうことを避けるため、ログアウト時にはトークンの削除も必要

証明書 or 暗号化キー(トークン)

  • APNsにプッシュ通知の送信をリクエストする際に必要となるもの
  • Apple Developerアカウントかから作成することができ、以下のような違いがある
  • 証明書
    • アプリごとに生成
    • 1年に1度更新が必要
    • 交換によるダウンタイムなし(複数の証明書を作成可能であるため)
  • 暗号化キー(トークン)
    • チームで共通(全アプリで共通)
    • 更新が必要ない
    • 交換によるダウンタイムあり(再生成時にダウンタイムが生まれる)
  • 現在は暗号化キーを用いた方式が推奨されている

通知送信の流れ

  1. クライアントアプリ側で通知の許諾ダイアログを表示する
    ※ あくまで「表示」の許諾なので、バックグラウンドプッシュだけを利用する場合は必要ない
  2. APNsへのデバイス登録を行い、デバイストークンを受け取る
  3. デバイストークンをバックエンドサーバーに送信しユーザーに紐づける
  4. バックエンドサーバーは通知を送信したいタイミングで、ユーザーに紐づくデバイストークンを用い、APNsに通知の送信リクエストを行う
    ※ この際、アプリのBundleIDやAPNsの環境(production/sandbox)などを指定することで、通知対象をコントロールできる

セットアップ

クライアントアプリ

  • iOSプロジェクトのCapabilityにて、Push Notificationを追加する
  • リッチプッシュを用いる場合:Notification Service Extensionの追加を行う
  • サイレントプッシュを用いる場合:Background ModesのRemote notificationへのチェックを行う
  • コミュニケーションプッシュを用いる場合:Notification Service Extension, CapbilityのCommunication Notifications, Info.plistのNSUserActivityTypesを追加

バックエンド

  • APNsへの通信時に、「証明書による署名」または「キーによる暗号化」が必要となるので、いずれかを配置しておく(詳細はこちら

通知の種類

1. 通常のプッシュ

テキスト・サウンド・バッジの指定など、BE側でデータ指定がすべて完結する最も基本的な形式の通知。

実装の流れ

  1. 通知送信の許諾を得る
  2. APNsへデバイス登録を行い、取得したデバイストークンをバックエンドサーバーへ送信する
  3. バックエンドサーバーから通知送信リクエストを行う際に、通知内容を指定するペイロードデータを生成する
  4. 必要なHTTPヘッダーを指定した上でAPNsへリクエストを行う

2. リッチプッシュ

通常のプッシュに加えて下記のような追加要素を付与できる通知。

  • 画像や動画
  • アクション

実装の流れ

  1. 画像・動画データやアクション自体を通知のペイロードに含めることはできないため、アプリ側で一度受け取った通知に対して、追加の変更対応を加えた上でユーザーに表示する対応を取る
  2. この処理はアプリがバックグラウンドの際にも動作させる必要があるため、Notification Service Extensionを追加してそれを実現する
  3. サーバーサイドでは、書き換え可能な通知であることをクライアントに伝えるため、通知ペイロードに "mutable-content": 1 を付与しておく
  4. これにより、アプリ側では通知を受け取ったタイミングで、Extensionに含まれるUNNotificationServiceExtensionのメソッドが呼び出される
    • ここでペイロードの値を参照して画像や動画のURLを取り出し、アプリ側でコンテンツのダウンロードを行う
    • フェッチしたデータは一時ディレクトリに保存しておき、そのパスを指定した上で通知の更新を完了する

詳細はこちら

3. バックグラウンドプッシュ

画面上には通知として現れないサイレント通知。
これを用いてサーバーサイドから端末上のアプリを叩き起こして、30秒までの処理を実行させることができる。

詳細はこちら

4. コミュニケーションプッシュ


メッセージングの機能を提供する場合に活用できる通知で、以下のような特徴がある。

  • 送信者のアバターアイコンが付与できる
  • フォーカスモードによる細かい制御が可能
  • 要約通知を突破できる

実装の流れ

  1. Intentsフレームワークを用いて、システムに特定の通知をコミュニケーション通知であると認識させる
  2. 具体的には、アプリ側で通知を受信した際、Notification Service Extension内でSiriKitインテントを用いてNotificationContentオブジェクトを更新する
  3. その際、コミュニケーション対象であるInPersonオブジェクトを生成し、メッセージング通知の場合は InSendMessageIntent に、通話の場合は InStartCallIntent に受け渡しておく

詳細はこちら

Notification summaryとrelevenceScore

iOS15からは、Notification summaryというユーザーが指定したアプリからの通知を1つにまとめられる機能が追加されました。

まとめられた通知の中での表示優先度は、以下の重みづけロジックによって決定されます。

  • 大きいサムネイルがついた通知は、そうでないものよりも優先される
  • 関連度スコアが高いものが優先される。(ペイロードやローカル通知のプロパティで"relevanceScore"を指定する)

Interuption Level

こちらもiOS15から追加された機能で、通知に対して「重み」を付与するための仕組みです。
この値は、緊急性の度合いによって4つの中断レベルから1つを選択して設定します。

  1. Passive
    • 無音でデバイスを起こさない通知
    • デバイスを手に取ったタイミングで気づくことになる
    • 時間的な制約がない通知に使う
  2. Active(デフォルト)
    • 音と振動で知らさられる通知
    • iOS14以前の通知と同じ挙動になる
  3. Time Sensitive
    • 視覚的に目立つようになる通知
    • ユーザーがタップしない限りロック画面に長くとどまる
    • 早急な対応が必要な通知に利用する
    • この通知はアプリが要約通知に設定されていたとしても、個別の通知として配信される
    • 利用する場合はCapabilityへの追加が必要
  4. Critical
    • 最も緊急性の高い通知
    • デバイスがミュート状態でも音で知らされる
    • 承認されたエンタイトルメントが必要で、健康や安全のためなどの極めて重要な通知だけに利用する

外部サービス

Firebase Cloud Messaging (FCM)

実装概要

  • デバイストークンと紐づいたFCMトークンと呼ばれるものをベースに通知対象を制御する
  • そのため、サービスのバックエンドではこのFCMトークンをユーザーに紐づかせた状態で保持することとなる
  • (Androidでプッシュ通知を行うにはこれを使う)

利用のメリット

  • 通知送信の細かな実装をFCMがになってくれるので、導入コストが低い
  • iOS/Androidで共通のIFで通知の送信ができる
    • Androidの場合はそもそもこれを使う
  • スロットル処理とスケーリングがFirebase内部で行われ、大量のリクエストに対応できる
    • 最大500件まで一度の送信処理で指定が可能
    • トピック機能を利用するとさらに多くのデバイスに対して、1度のリクエストで送信ができる
  • Webコンソールから送信の制御もできる
  • 配信結果をBigQueryにエクスポートできる
  • 最新機能への追従が早い
  • 無料

Webプッシュ

  • MacではVentura移行でサポート
  • iOSとiPadOSは2023年サポート予定
  • APNsを利用するが、開発者アカウントは必要なし
  • Web標準に従ってコーディングした場合、Safari側の実装は必要ない
  • Safariが実行中でなくても通知は表示される

実装概要

  1. ユーザーのアクションを起点として通知のシステム許可ダイアログを表示する
  2. ユーザーがPushを許可した場合、Safari側でPushSubscriptionオブジェクトを取得することができる
  3. 通知を送信する場合は、PushScbscriptionをベースにサーバーサイドからリクエストを行う
  4. Safariが通知を受信すると、ServiceWorkerが起動してプッシュイベントをハンドリングできるので、ここからUIとしての表示処理を行う
  5. ユーザーが通知をクリックした場合も、同様にServiceWorkerが起動される

Q&Aコーナー

Q. プッシュ通知で送信されたデータはアプリ側で全てハンドリングできる?

A. アプリがフォアグラウンド状態である場合はできるが、そうでない場合できない

アプリの状態 ハンドリングできるか
フォアグラウンド できる
バックグラウンド できない
kill できない
  • バックグラウンドorKillされた状態では通知をハンドリングすることがそもそもできない
  • 「通知をタップしてアプリを起動」してもらえた場合のみ、対象の通知に紐づくデータを取得することができる
  • この時、参照できるデータはタップした通知に関連するものだけなので、その他のデータを参照することはできない。

Discussion

hackenbackerhackenbacker

参照できるデータはタップした通知に関連するものだけなので、その他のデータを参照することはできない。

UNUserNotificationCenter.current().getDeliveredNotifications(completionHandler:)を使えば、タップしていない通知のデータも取得できると思います。