デバイスの状態に応じて、着信メッセージの処理が異なります。これらのシナリオと、FCM を独自のアプリケーションに統合する方法を理解するには、まず、デバイスが取り得るさまざまな状態を確立することが重要です。
州 | 説明 |
---|---|
前景 | アプリケーションが開いていて、表示され、使用されているとき。 |
バックグラウンド | アプリケーションが開いているが、バックグラウンドで (最小化されている) 場合。これは通常、ユーザーがデバイスの「ホーム」ボタンを押した場合、アプリ スイッチャーを使用して別のアプリに切り替えた場合、またはアプリケーションを別のタブ (Web) で開いた場合に発生します。 |
終了しました | デバイスがロックされている場合、またはアプリケーションが実行されていない場合。 |
アプリケーションが FCM 経由でメッセージ ペイロードを受信するには、いくつかの前提条件を満たす必要があります。
- アプリケーションは、少なくとも 1 回開いている必要があります (FCM への登録を可能にするため)。
- iOS では、ユーザーがアプリ スイッチャーからアプリケーションをスワイプして離した場合、バックグラウンド メッセージが再び機能するように手動で再度開く必要があります。
- Android では、ユーザーがデバイスの設定からアプリを強制終了した場合、メッセージが機能するように手動で再度開く必要があります。
- Web では、Web プッシュ証明書で
getToken()
を使用して) トークンを要求している必要があります。
メッセージを受信する許可をリクエストする (Apple および Web)
iOS、macOS、および Web では、デバイスで FCM ペイロードを受信する前に、まずユーザーの許可を求める必要があります。
firebase_messaging
パッケージは、 requestPermission
メソッドを介してパーミッションをリクエストするためのシンプルな API を提供します。この API は、通知ペイロードを含むメッセージがサウンドをトリガーしたり、Siri を介してメッセージを読み上げたりできるかどうかなど、要求する許可のタイプを定義する多くの名前付き引数を受け入れます。デフォルトでは、メソッドは適切なデフォルトのアクセス許可を要求します。リファレンス API は、各権限の目的に関する完全なドキュメントを提供します。
開始するには、アプリケーションからメソッドを呼び出します (iOS ではネイティブ モーダルが表示され、Web ではブラウザのネイティブ API フローがトリガーされます)。
FirebaseMessaging messaging = FirebaseMessaging.instance;
NotificationSettings settings = await messaging.requestPermission(
alert: true,
announcement: false,
badge: true,
carPlay: false,
criticalAlert: false,
provisional: false,
sound: true,
);
print('User granted permission: ${settings.authorizationStatus}');
リクエストから返されたNotificationSettings
オブジェクトのauthorizationStatus
プロパティを使用して、ユーザーの全体的な決定を決定できます。
-
authorized
: ユーザーが許可した権限。 -
denied
: ユーザーは許可を拒否しました。 -
notDetermined
: ユーザーは、権限を付与するかどうかをまだ選択していません。 -
provisional
: 仮の権限を付与されたユーザー
NotificationSettings
の他のプロパティは、現在のデバイスで特定のアクセス許可が有効、無効、またはサポートされていないかどうかを返します。
アクセス許可が付与され、さまざまな種類のデバイス状態が理解されると、アプリケーションは着信 FCM ペイロードの処理を開始できるようになります。
メッセージ処理
アプリケーションの現在の状態に基づいて、さまざまなメッセージ タイプの受信ペイロードを処理するには、さまざまな実装が必要です。
前景メッセージ
アプリケーションがフォアグラウンドにあるときにメッセージを処理するには、 onMessage
ストリームをリッスンします。
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
print('Got a message whilst in the foreground!');
print('Message data: ${message.data}');
if (message.notification != null) {
print('Message also contained a notification: ${message.notification}');
}
});
ストリームにはRemoteMessage
が含まれており、送信元、一意の ID、送信時刻、通知が含まれているかどうかなど、ペイロードに関するさまざまな情報の詳細が示されます。アプリケーションがフォアグラウンドにある間にメッセージが取得されたため、Flutter アプリケーションの状態とコンテキストに直接アクセスできます。
フォアグラウンドおよび通知メッセージ
アプリケーションがフォアグラウンドにあるときに到着する通知メッセージは、デフォルトでは、Android と iOS の両方で目に見える通知を表示しません。ただし、この動作をオーバーライドすることは可能です。
- Android では、「優先度の高い」通知チャネルを作成する必要があります。
- iOS では、アプリケーションの表示オプションを更新できます。
バックグラウンド メッセージ
バックグラウンド メッセージの処理プロセスは、ネイティブ (Android および Apple) と Web ベースのプラットフォームでは異なります。
Apple プラットフォームと Android
onBackgroundMessage
ハンドラーを登録して、バックグラウンド メッセージを処理します。メッセージを受信すると、Isolate が生成され (Android のみ。iOS/macOS では個別の Isolate は必要ありません)、アプリケーションが実行されていないときでもメッセージを処理できます。
バックグラウンド メッセージ ハンドラーについては、次の点に注意してください。
- 無名関数であってはなりません。
- これは最上位関数でなければなりません (たとえば、初期化が必要なクラス メソッドではありません)。
- 関数宣言のすぐ上に
@pragma('vm:entry-point')
で注釈を付ける必要があります (そうしないと、リリース モードのツリー シェーキング中に削除される可能性があります)。
@pragma('vm:entry-point')
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
// If you're going to use other Firebase services in the background, such as Firestore,
// make sure you call `initializeApp` before using other Firebase services.
await Firebase.initializeApp();
print("Handling a background message: ${message.messageId}");
}
void main() {
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
runApp(MyApp());
}
ハンドラーはアプリケーション コンテキスト外の独自の分離で実行されるため、アプリケーションの状態を更新したり、UI に影響を与えるロジックを実行したりすることはできません。ただし、HTTP リクエストなどのロジックを実行したり、IO 操作 (ローカル ストレージの更新など) を実行したり、他のプラグインと通信したりすることはできます。
できるだけ早くロジックを完成させることもお勧めします。長時間の集中的なタスクを実行すると、デバイスのパフォーマンスに影響を与え、OS がプロセスを終了させる可能性があります。タスクが 30 秒以上実行されると、デバイスがプロセスを自動的に強制終了する場合があります。
ウェブ
Web では、バックグラウンドで実行される JavaScript Service Workerを記述します。 Service Worker を使用してバックグラウンド メッセージを処理します。
開始するには、 web
ディレクトリに新しいファイルを作成し、 firebase-messaging-sw.js
ます。
importScripts("https://www.gstatic.com/firebasejs/8.10.0/firebase-app.js");
importScripts("https://www.gstatic.com/firebasejs/8.10.0/firebase-messaging.js");
firebase.initializeApp({
apiKey: "...",
authDomain: "...",
databaseURL: "...",
projectId: "...",
storageBucket: "...",
messagingSenderId: "...",
appId: "...",
});
const messaging = firebase.messaging();
// Optional:
messaging.onBackgroundMessage((message) => {
console.log("onBackgroundMessage", message);
});
このファイルは、アプリとメッセージング SDK の両方をインポートし、Firebase を初期化し、 messaging
変数を公開する必要があります。
次に、ワーカーを登録する必要があります。エントリ ファイル内で、 main.dart.js
ファイルが読み込まれた後、ワーカーを登録します。
<html>
<body>
...
<script src="main.dart.js" type="application/javascript"></script>
<script>
if ('serviceWorker' in navigator) {
// Service workers are supported. Use them.
window.addEventListener('load', function () {
// ADD THIS LINE
navigator.serviceWorker.register('/firebase-messaging-sw.js');
// Wait for registration to finish before dropping the <script> tag.
// Otherwise, the browser will load the script multiple times,
// potentially different versions.
var serviceWorkerUrl = 'flutter_service_worker.js?v=' + serviceWorkerVersion;
// ...
});
}
</script>
次に、Flutter アプリケーションを再起動します。ワーカーが登録され、バックグラウンド メッセージはこのファイルを介して処理されます。
インタラクションの処理
通知は目に見える合図であるため、ユーザーが (押すことによって) 通知を操作するのは一般的です。 Android と iOS の両方のデフォルトの動作は、アプリケーションを開くことです。アプリケーションが終了すると、アプリケーションが開始されます。バックグラウンドにある場合は、フォアグラウンドに移動します。
通知の内容によっては、アプリケーションが開いたときのユーザーの操作を処理したい場合があります。たとえば、新しいチャット メッセージが通知を介して送信され、ユーザーがそれを押した場合、アプリケーションが開いたときに特定の会話を開くことができます。
firebase-messaging
パッケージは、このやり取りを処理する 2 つの方法を提供します。
-
getInitialMessage()
: アプリケーションが終了状態から開かれた場合、RemoteMessage
を含むFuture
が返されます。消費されると、RemoteMessage
は削除されます。 -
onMessageOpenedApp
: アプリケーションがバックグラウンド状態から開かれたときにRemoteMessage
を投稿するStream
。
ユーザーにとってスムーズな UX を確保するために、両方のシナリオを処理することをお勧めします。以下のコード例は、これを実現する方法の概要を示しています。
class Application extends StatefulWidget {
@override
State<StatefulWidget> createState() => _Application();
}
class _Application extends State<Application> {
// It is assumed that all messages contain a data field with the key 'type'
Future<void> setupInteractedMessage() async {
// Get any messages which caused the application to open from
// a terminated state.
RemoteMessage? initialMessage =
await FirebaseMessaging.instance.getInitialMessage();
// If the message also contains a data property with a "type" of "chat",
// navigate to a chat screen
if (initialMessage != null) {
_handleMessage(initialMessage);
}
// Also handle any interaction when the app is in the background via a
// Stream listener
FirebaseMessaging.onMessageOpenedApp.listen(_handleMessage);
}
void _handleMessage(RemoteMessage message) {
if (message.data['type'] == 'chat') {
Navigator.pushNamed(context, '/chat',
arguments: ChatArguments(message),
);
}
}
@override
void initState() {
super.initState();
// Run code required to handle interacted messages in an async function
// as initState() must not be async
setupInteractedMessage();
}
@override
Widget build(BuildContext context) {
return Text("...");
}
}
インタラクションの処理方法は、アプリケーションのセットアップによって異なります。上記の例は、StatefulWidget を使用した基本的な図を示しています。
メッセージのローカライズ
ローカライズされた文字列は、次の 2 つの方法で送信できます。
- 各ユーザーの優先言語をサーバーに保存し、言語ごとにカスタマイズされた通知を送信します
- アプリにローカライズされた文字列を埋め込み、オペレーティング システムのネイティブ ロケール設定を利用する
2 番目の方法の使用方法は次のとおりです。
アンドロイド
resources/values/strings.xml
でデフォルト言語のメッセージを指定します。<string name="notification_title">Hello world</string> <string name="notification_message">This is a message</string>
values- language
ディレクトリで翻訳されたメッセージを指定します。たとえば、resources/values-fr/strings.xml
でフランス語のメッセージを指定します。<string name="notification_title">Bonjour le monde</string> <string name="notification_message">C'est un message</string>
サーバー ペイロードでは、
title
、message
、およびbody
キーを使用する代わりに、ローカライズされたメッセージにtitle_loc_key
およびbody_loc_key
を使用し、それらを表示するメッセージのname
属性に設定します。メッセージのペイロードは次のようになります。
{ "data": { "title_loc_key": "notification_title", "body_loc_key": "notification_message" }, }
iOS
Base.lproj/Localizable.strings
でデフォルト言語のメッセージを指定します。"NOTIFICATION_TITLE" = "Hello World"; "NOTIFICATION_MESSAGE" = "This is a message";
language .lproj
ディレクトリで翻訳されたメッセージを指定します。たとえば、fr.lproj/Localizable.strings
でフランス語のメッセージを指定します。"NOTIFICATION_TITLE" = "Bonjour le monde"; "NOTIFICATION_MESSAGE" = "C'est un message";
メッセージのペイロードは次のようになります。
{ "data": { "title_loc_key": "NOTIFICATION_TITLE", "body_loc_key": "NOTIFICATION_MESSAGE" }, }
メッセージ配信データのエクスポートを有効にする
さらに分析するために、メッセージ データを BigQuery にエクスポートできます。 BigQuery を使用すると、BigQuery SQL を使用してデータを分析したり、別のクラウド プロバイダにエクスポートしたり、カスタム ML モデルにデータを使用したりできます。 BigQuery へのエクスポートには、メッセージの種類や、メッセージが API または Notifications Composer のどちらを介して送信されたかに関係なく、メッセージで使用可能なすべてのデータが含まれます。
エクスポートを有効にするには、まずこちらで説明されている手順に従ってから、次の手順に従ってください。
アンドロイド
次のコードを使用できます: dart await FirebaseMessaging.instance.setDeliveryMetricsExportToBigQuery(true);
iOS
iOS の場合、 AppDelegate.m
を次の内容で変更する必要があります。
#import "AppDelegate.h"
#import "GeneratedPluginRegistrant.h"
#import <Firebase/Firebase.h>
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[GeneratedPluginRegistrant registerWithRegistry:self];
// Override point for customization after application launch.
return [super application:application didFinishLaunchingWithOptions:launchOptions];
}
- (void)application:(UIApplication *)application
didReceiveRemoteNotification:(NSDictionary *)userInfo
fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
[[FIRMessaging extensionHelper] exportDeliveryMetricsToBigQueryWithMessageInfo:userInfo];
}
@end
ウェブ
Web の場合、v9 バージョンの SDK を使用するには Service Worker を変更する必要があります。 v9 バージョンはバンドルする必要があるため、たとえばesbuild
などのバンドラーを使用して Service Worker を機能させる必要があります。これを実現する方法については、サンプル アプリを参照してください。
v9 SDK に移行したら、次のコードを使用できます。
import {
experimentalSetDeliveryMetricsExportedToBigQueryEnabled,
getMessaging,
} from 'firebase/messaging/sw';
...
const messaging = getMessaging(app);
experimentalSetDeliveryMetricsExportedToBigQueryEnabled(messaging, true);
Service Worker の新しいバージョンをweb
フォルダーにエクスポートするために、 yarn build
を実行することを忘れないでください。