プッシュ通知の内容に関する方針
Status: Accepted
要約
このアプリでは、以下のユースケースそれぞれに対して以下の内容のプッシュ通知を送信します。
- 時間割の開始通知
- 通知表示内容:タイトルと本文のみ
- 通知に含めるデータ:時間割の開始通知を表す通知タイプと、チームID・時間割IDのパラメータ
- 優先度:高(スリープ状態を解除する)
- 有効期限:12時間
- 折りたたみ:アプリ単位で集約(デフォルト)
- 通知チャンネル(Android):FCMデフォルトのMiscellaneousを使用
- 通知タイプ(iOS):使用しない
- その他の設定:デフォルト
コンテキスト
リモート通知を設計する上では、通知の表示内容やデータを検討するだけでなく、 優先度や有効期限など送信制御に関する設定も考慮する必要があります。
ここでは次の観点でプッシュ通知に含める内容と設定について議論・調査を進めていきます。
- 通知受信時の挙動
- 通知で表現できる内容
- 通知に含められるデータ
- 優先度
- 有効期限
- 折りたたみ
- 通知チャンネル(Android)
- 通知タイプ(iOS)
- その他のOS固有のオプション
議論
通知受信時の挙動
FCMを介したプッシュ通知では、通知内容に関するパラメータと、 クライアントアプリに渡せる任意のKey-Value構造のデータを指定して送信できます。
通知にデータを含めるかどうかは任意です。また、通知のタイトルや本文などを含まない、データのみの通知も送信できます。 FCMでは、通知のタイトルや本文を含むものはデータの有無にかかわらず通知メッセージと呼ばれています。 データのみを含むものはデータメッセージと呼ばれています。
データメッセージの場合は、OSの通知領域には通知が表示されず、通知音やバイブレーションもありません。 そのため、ユーザに知らせる必要はないが処理をしたい場合や、一旦アプリ側でデータを受け取って何か処理をした後にユーザへ伝える必要がある場合などに用いられます。 ただし、受信時にアプリが起動されていなかった場合、次にアプリが起動されるまでデータを用いた処理を開始できません。 リアルタイムに処理したい場合は、バックグラウンドサービスとして稼働させておく必要があります。
通知メッセージの場合、基本的には受信時にOSの通知領域にそのタイトルや本文などの内容が表示されます。 ただしユーザがアプリを前面で操作中であった場合、通知領域には通知は表示されません。 通知メッセージにデータが含まれている場合、既にアプリを前面で操作中であれば即時、そうでなければ通知のタップ時にそのデータをアプリ側で読み込んで利用できます。
このアプリにおける通知のユースケースでは、いずれも通知メッセージを利用します。 上記の動作をふまえて、このアプリでは通知メッセージの受信時に以下のようにふるまいます。
- アプリが前面で操作中であれば、スナックバーで通知内容をアプリ上に表示する。データに応じた処理があれば行うが、画面遷移などユーザの操作を妨げる処理は行わない。
- アプリが未起動またはバックグラウンド状態の場合、通知領域に通知が表示される。通知タップ時にはアプリの状態の応じて以下のように処理を行う
- アプリのアクティビティがメモリ上に保持されている場合(ホットスタート):通知タップ時にアプリを前面表示し、データに応じた処理を行う。
- アプリのアクティビティがメモリ上に保持されていない場合(ウォームスタート):通知タップ時にアプリを起動して初期化処理を行った後、データに応じた処理を行う。
- アプリが起動されていない場合(コールドスタート):通知タップ時にアプリを起動して初期化処理を行った後、データに応じた処理を行う。
通知で表現できる内容
FCMを介したプッシュ通知では、メッセージの内容として 公式ドキュメント に記載されている項目を設定できます。
ここでは、その中から主な項目について検討します。
通知のタイトル・本文・画像
FCMからプッシュ通知を送信する場合に、送信先OSによらず共通で指定できる要素はタイトル、本文、画像の3種です。 通知領域に通知内容が表示する際のタイトル、本文、画像を指定できます。
画像については、iOSの場合はクライアントアプリ側にも画像を通知に表示するための処理を追加する必要があります。 詳細については、通知ペイロードで画像を送信するを参照してください。
JPEG、PNG、BMPがAndroid、iOSともにサポートされており、iOSではアニメーションGIFとビデオも利用できます。 Androidには1MBの画像サイズ制限があります。
このアプリでは、通知のユースケースに合わせたタイトル、本文を送信します。 画像はこのアプリでは利用しません。
FCMを介したプッシュ通知送信では、パラメータとしてプラットフォームを問わない共通属性と、プラットフォーム個別の属性を指定できます。 タイトルや本文などは、共通属性とプラットフォーム個別の属性の両方で指定できます。 両方指定した場合、プラットフォーム個別の属性が設定されていればそのOSではそちらの設定が優先して使用されます。
通知音・バイブレーションパターン・LEDパターン
AndroidやiOSでは通知音も変更できます。 またAndroidでは、バイブレーションパターン、LEDの発光パターンなども指定できます。 通常はOSのシステム設定を用いるデフォルトの方がユーザの混乱を招かないでしょう。
このアプリではこれらの設定はデフォルトのままとします。
バッジ表示
AndroidやiOSでは、アプリアイコンの上に未読数などを示す数字が表示できます。 これはバッジ表示と呼ばれています。
プッシュ通知のパラメータで、このバッジで表示する数値を変更できます。 0を指定すると、バッジの数値は初期化され表示されなくなります。 指定しなかった場合、デフォルトではAndroidとiOSでは以下の数値が表示されます。
- Android: 通知ドロワーに残っているそのアプリの通知の数
- iOS: バッジの数値は変動しない
また、このバッジの数値はクライアントアプリ側でも変更できます。 例えば未読通知としてバッジ表示を利用する場合は、既読になった時点でバッジの数値を変更する必要があります。 React Nativeの場合、expo-notificationsなどの通知全般を取り扱うライブラリを用いてバッジの数値を変更できます。
このアプリでは、プッシュ通知受信時にはバッジの数値は指定せず、デフォルトの動作に任せます。 また、クライアントアプリ側でのバッジの数値の変更は行いません。
通知内容によっては、通知の目的が達せられた場合にバッジの数値を変化させるだけでなく、さらにOSの通知領域から通知を削除したい場合があります。 例えば新着メールを受信し、メールアプリから通知を受け取ったとします。 ユーザが通知をタップせずに直接アプリを開いて新着メールを確認した場合、そのデバイスの通知領域に残されている通知は不要になります。 通知領域からの通知の削除が必要な場合は、クライアントアプリ側に削除処理を組み込む必要があります。
React Nativeの場合、expo-notificationsなどのライブラリを利用することで、 現在デバイスの通知領域に表示されている通知一覧を取得できます。 また、指定した通知の通知領域からの削除もできます。 削除対象とする通知は、通知一覧や通知タップ時のイベントなどから取得したIDで指定できます。
通知に含められるデータ
通知に含められるデータについては、 FCMメッセージについて のドキュメント内に記載されています。
データには、一部のFCMによって予約されたkeyを除き、任意のKey-Valueペアの文字列データを格納可能です。 最大ペイロードは4KBです。ただし、Firebaseコンソールからメッセージを送信する場合は例外となり、1,024文字の上限が適用されます。
このアプリでは、OSの通知領域に通知を表示し、それをタップするとアプリを起動した上で通知内のデータに応じて適切なアクションを行うことを想定しています。 そのため、通知のタイトルや本文に加えてデータも含めた通知メッセージとして送信します。 通知メッセージでは、デフォルトでは通知をタップするとアプリが起動されます。 既にアプリが起動されている場合は、アプリが現在の状態で前面に表示されます。
データの形式は、通知のタイプとそれに付随するパラメータを含めた以下のような形式で送信します。
{
"type": "StartedTimetable",
"params": {
"teamId": 123,
"timetableId": 456,
}
}
このアプリでは、通知がタップされたタイミングで通知に含まれるデータを確認し、データのタイプに応じて適切なアクションを行います。 どのタイプを受け取ったときにどのようなアクションを行うかは、クライアント側のコードで管理するものとします。 例えば例に挙げた時間割の開始通知の場合は、タップ時にアプリを起動してデータを読み込み、ホーム画面の当日の時間割画面を開きます。 ただし、ユーザがアプリを前面で操作中の場合は操作の妨げにならないよう画面遷移は行わず、スナックバーで内容を通知するにとどめます。
優先度
通知には、メッセージの優先順位を決める優先度を指定できます。
Androidの場合、NORMALとHIGHの2種類を指定できます。 NORMALの場合は、スリープ状態のデバイスには通知は届かず、スリープ状態が解除されてから表示されます。 またバッテリーを節約するために配信を遅らせる場合があります。 HIGHの場合は、デバイスがスリープ状態であってもスリープ状態を解除して通知が表示されます。 デフォルト値はデータメッセージの場合はNORMAL、通知メッセージの場合はHIGHです。
電源管理に関する制限に記載されているように、 デバイスの状態によっては優先度HIGHの通知回数に1日あたりの上限が設けられています。 上限に到達した後のメッセージは、すべて通常の優先度のメッセージとして処理されます。 必要な通知のみHIGHを設定するようにしましょう。
iOSの場合、優先度は5か10の数値で設定できます。 5(標準の優先度)の場合は、AndroidにおけるNORMALと同様に、デバイスの電源状態に応じて通知が届けられます。 10(高優先度)の場合は、AndroidにおけるHIGHと同様に、デバイスがスリープ状態であってもスリープ状態を解除して通知が表示されます。 デフォルト値は10です。
iOSの場合、通知メッセージではなくデータメッセージを送信する場合は、優先度を5(標準の優先度)に設定する必要があります。 10(高優先度)でデータメッセージを送信するとINVALID_ARGUMENTのエラーとなり、送信が拒否されます。
このアプリにおける時間割の開始通知は1日1回程度で、スリープ時にも気づいてもらいたいものです。 そのため、Android、iOSともに高優先度を指定します。
有効期限
デバイスが通知を受信できない状態である場合、通知は各OSのリモート通知サービスで保持され、有効期限に達するまでは定期的に再配信が試み続けられます。
Androidの場合、有効期限を0~2,419,200秒(28日)の間で設定できます。 0を設定した場合は一度だけ配信を試み、到達できなかった場合は即座に破棄されます。 0以外を設定した場合は、一度配信を試みた後も設定された秒数が経過するまでの間は再試行されます。 Androidでは、デフォルトの有効期限は28日となっています。
iOSの場合、有効期限をUNIXエポック秒で設定できます。 0を指定した場合は一度だけ配信を試み、到達できなかった場合は即座に破棄されます。 0以外を設定した場合は、一度配信を試みた後も設定されたUNIXエポック秒に到達するまでの間は再試行されます。 デフォルト値は、公式ドキュメント上では明記されていませんでした。
例えば天気予報通知のようなケースでは、翌日になってから前日の天気予報通知を受信してもあまり意味がありません。 無駄な通知がユーザのデバイスに表示されないよう、ユースケースごとに適切な有効期限を検討する必要があります。 このアプリの時間割の開始通知では、その目的上、翌日以降に通知が届いても役に立たない内容であるため、有効期限は12時間とします。
折りたたみ
AndroidやiOSでは、通知をグループに分類し、同じグループの通知を1つに集約して表示する機能を提供しています。 折りたたみ可能メッセージとも呼ばれています。 この機能により、チャットアプリでの新着通知など高頻度で通知が送信されるユースケースでも、ユーザのデバイスの通知領域を埋め尽くさずに1つの表示に集約できます。
折りたたみは、通知に指定した折りたたみキーの単位で行われます。 同じ折りたたみキーが指定された通知メッセージは、最新のものだけがデバイスの通知領域に表示されます。 FCMから通知を送信する際に折りたたみキーを指定しなかった場合、デフォルトではFCMに設定されたアプリパッケージ名が使用されます。 つまり、同じアプリからの通知が全て1つに集約されて表示されます。
FCMでは同時に配信予約できる折りたたみキーは4種類までという制約があります。 これを超えて通知の配信予約を行った場合、FCMでは4種類の折りたたみキーの通知のみが保持され、あふれた通知は送信されません。 この時、どの通知が保持されるかの保証はありません。 そのため、折りたたみキーも4種類までに抑えておくのが望ましいでしょう。
今回のこのアプリでは通知の種類が1つしか存在しないため、 折りたたみキーは明示的に指定せず全てデフォルト値を用います。
通知チャンネル(Android)
Android 8.0以降では、通知チャンネル(カテゴリ)という機能が提供されています。 これは、通知に関する設定を、通知チャンネルごとに個別に切り替えられる機能です。
例えば、重要なお知らせと通常のお知らせの通知チャンネルを分けておくと、ユーザが重要なお知らせだけ表示するといった設定を行えるようになります。 これにより、ユーザにより通知を全て無効化されてしまう可能性は下がることが期待できます。
Android 8.0以降、Androidでは通知は全ていずれかの通知チャンネルに割り当てることが求められるようになりました。 Android 8.0(APIレベル26)をターゲットとしているアプリで、通知チャネルを指定せずに通知を送信した場合、通知は表示されず、エラーがログに記録されます。
FCMから通知を送信する際にも、この通知チャンネルを指定できます。 クライアントアプリにFirebase SDKを組み込んでいる場合、Firebase SDKは自動的に"Miscellaneous"(その他)という通知チャンネルを作成します。 FCMから明示的に通知チャンネルを指定せずに通知を送信した場合、デフォルトでこの通知チャンネル宛として通知が送信されます。 そのため、クライアントアプリに通知チャンネルを作成する処理を独自に追加していなくても、Android端末で通知が表示されないといった事態は避けられます。
独自の通知チャンネルを使用する場合は、事前にクライアントアプリ側で通知チャンネルを作成しておく必要があります。 Firebase SDKは任意の通知チャンネルを作成する機能までは提供していません。 React Nativeの場合、expo-notificationsなどの通知全般を取り扱うライブラリを用いて通知チャンネルを作成できます。
このアプリでは通知の種類が1つしか存在しないため、FCMデフォルトのMiscellaneousをそのまま使用します。
通知タイプ(iOS)
iOSでは、通知タイプ(カテゴリ)という機能が提供されています。 これは、クライアントアプリ側で通知タイプを定義しておくことで、その通知タイプが指定された通知を受け取った際にカスタマイズされたレイアウトで通知を表示できます。 これは単純なレイアウト変更だけでなく、通知領域へのアクション用ボタンの配置もできます。 例えば会議への招待通知であれば、承諾ボタンと拒否するボタンを通知内に表示することで、アプリを起動することなく返答できます。
FCMから通知を送信する際にも、この通知タイプを指定できます。 しかし、クライアントアプリ側での通知タイプ定義はFirebase SDKのサポート外であるため、別ライブラリなどで対応する必要があります。
このアプリでは、タイトルと本文があれば十分なユースケースしか存在しないため、通知タイプは利用しません。
Androidにも、通知のレイアウトをカスタマイズしてボタンなどを設置できる機能は存在するのですが、ローカル通知でしか利用できません。リモート通知の場合は、デフォルトの通知レイアウトしか利用できません。 どうしてもリモート通知でカスタムレイアウトの通知を表示したい場合は、データメッセージ形式で必要な情報を送り、 クライアント側でそのデータを用いて改めてローカル通知を表示する方法もあります。
その他のOS固有のオプション
上記のオプションの他にも、Android、iOSは様々なOS固有のオプションを提供しており、FCMから通知を送信する際に指定できます。
Androidで指定可能なオプションは、こちらに記載されています。 iOSで指定可能なオプションは、こちらに記載されています。
例えばAndroidでは、通知アイコンや、通知受信時にステータスバーへ表示されるティッカーテキストなどを指定できます。 またiOSでは、通知のサブタイトルや、通知タップによりアプリを開く際の起動時画像などを指定できます。
このアプリでは、タイトルと本文があれば十分なユースケースしか存在しないため、こうした各OS固有のオプションは利用しません。
決定
このアプリでは、以下のユースケースそれぞれに対して以下の内容のプッシュ通知を送信します。
- 時間割の開始通知
- 通知表示内容:タイトルと本文のみ
- 通知に含めるデータ:時間割の開始通知を表す通知タイプと、チームID・時間割IDのパラメータ
- 優先度:高(スリープ状態を解除する)
- 有効期限:12時間
- 折りたたみ:アプリ単位で集約(デフォルト)
- 通知チャンネル(Android):FCMデフォルトのMiscellaneousを使用
- 通知タイプ(iOS):使用しない
- その他の設定:デフォルト
参考:プッシュ通知に関連するAndroidバージョンごとの変更点
Android端末は比較的古いバージョンが長く使われていることが多く、幅広いOSバージョンをサポート対象とすることが多いです。 プッシュ通知の機能はAndroidのバージョンによっては大きな変更が入っているため、 どのバージョンでどのような変更が入ったのか意識しておく必要があります。
Androidにおける通知機能の互換性については以下を参照してください。 https://developer.android.com/guide/topics/ui/notifiers/notifications#compatibility
大きな変更点は以下のとおりです。
- Android 10
- アプリが独自のアクションボタンを提供しない場合、プラットフォームが通知アクションボタンを自動的に生成
- Android 8.0
- 通知チャンネル(カテゴリ)を追加
- 通知バッジ(通知ドット)表示に対応
- Android 7.0
- 通知のグループ化に対応
- 通知上からメッセージに直接返信したりテキストを入力できるダイレクト返信アクションの追加
- 通知テンプレートのスタイルが変更され、ヒーロー画像とアバターが重視されるように
- Android 5.0
- アプリが通知を発行した瞬間に表示されしばらくすると消えるヘッドアップ通知の導入
- ロック画面を刷新。通知がロック画面に表示できるように