mcpField
F
firebase-messaging

firebase-messaging

github.com/evanca開発663205 回インストール3 週間前に更新
リポジトリ

firebase-messaging について

Flutter アプリで Firebase Cloud Messaging を利用するためのガイダンススキルです。各プラットフォームの設定、メッセージ処理、権限、トークン、通知動作を扱います。

主な機能

  1. 01プラットフォーム別の FCM 設定
  2. 02フォアグラウンドとバックグラウンドの処理
  3. 03権限とトークンの管理
  4. 04通知操作とトピックの処理

活用シーン

  1. 01Flutter アプリへのプッシュ通知の追加
  2. 02アプリ状態に応じたメッセージ処理
  3. 03デバイストークンと通知権限の管理

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/evanca/flutter-ai-rules --skill firebase-messaging

インストール後、Claude Code で/firebase-messagingを入力して読み込まれたか確認します。

詳細

Firebase Cloud Messaging スキル

このスキルでは、Flutter アプリケーションで Firebase Cloud Messaging(FCM)を正しく使用する方法を説明します。

使用する場面

次のような場合にこのスキルを使用します。

  • Flutter プロジェクトで FCM によるプッシュ通知を設定する場合。
  • フォアグラウンド、バックグラウンド、終了状態でメッセージを処理する場合。
  • 通知の権限と FCM トークンを管理する場合。
  • プラットフォーム固有の通知表示動作を設定する場合。

1. セットアップと設定

flutter pub add firebase_messaging

iOS:

  • Xcode で Push Notifications と Background Modes を有効にします。
  • FCM を使用する前に、APNs 認証キーを Firebase にアップロードします。
  • メソッドスウィズリングを無効にしないでください。FCM トークンの処理に必要です。
  • APNs 認証キーのバンドル ID がアプリのバンドル ID と一致していることを確認します。

Android:

  • デバイスには Android 4.4 以降が搭載され、Google Play サービスがインストールされている必要があります。
  • onCreate() と onResume() の両方で Google Play サービスとの互換性を確認します。

Web:

  • web/ ディレクトリに firebase-messaging-sw.js という名前のサービスワーカーファイルを作成して登録します。
importScripts("https://www.gstatic.com/firebasejs/10.7.0/firebase-app-compat.js");
importScripts("https://www.gstatic.com/firebasejs/10.7.0/firebase-messaging-compat.js");

firebase.initializeApp({ /* your config */ });

const messaging = firebase.messaging();

messaging.onBackgroundMessage((message) => {
  console.log("onBackgroundMessage", message);
});

2. メッセージ処理

フォアグラウンドのメッセージ:

FirebaseMessaging.onMessage.listen((RemoteMessage message) {
  print('Foreground message data: ${message.data}');
  if (message.notification != null) {
    print('Notification: ${message.notification}');
  }
});

バックグラウンドのメッセージ:

@pragma('vm:entry-point')
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
  // Initialize Firebase before using other Firebase services in background
  await Firebase.initializeApp();
  print("Background message: ${message.messageId}");
}

void main() {
  FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
  runApp(MyApp());
}

バックグラウンドハンドラーのルール:

  • トップレベル関数である必要があります(匿名関数やクラスメソッドは不可)。
  • リリースモードでのツリーシェイキングによる削除を防ぐため、@pragma('vm:entry-point') を付与します(Flutter 3.3.0 以降)。
  • 別のアイソレートで実行されるため、アプリの状態を更新したり、UI に影響するロジックを実行したりできません。
  • 他の Firebase サービスを使用する前に Firebase.initializeApp() を呼び出します。

3. 権限

NotificationSettings settings = await FirebaseMessaging.instance.requestPermission(
  alert: true,
  badge: true,
  sound: true,
  announcement: false,
  carPlay: false,
  criticalAlert: false,
  provisional: false,
);

print('Authorization status: ${settings.authorizationStatus}');
  • iOS/macOS/Web/Android 13 以降: FCM ペイロードを受信する前に権限をリクエストする必要があります。
  • Android 13 未満: OS 設定でユーザーが通知を無効にしていない場合、authorizationStatus は authorized を返します。
  • Android 13 以降: 権限リクエストをアプリ内で追跡してください。ユーザーが許可または拒否を選択したかを判定する方法はありません。
  • iOS では暫定的な権限(provisional: true)を使用すると、最初の通知を受け取った後にユーザーが通知の種類を選択できます。

4. トークン管理

FCM 登録トークンを取得する(特定のデバイスにメッセージを送信する際に使用):

final fcmToken = await FirebaseMessaging.instance.getToken();

Web — VAPID キーを指定する:

final fcmToken = await FirebaseMessaging.instance.getToken(
  vapidKey: "BKagOny0KF_2pCJQ3m....moL0ewzQ8rZu"
);

トークンの更新をリッスンする:

FirebaseMessaging.instance.onTokenRefresh.listen((fcmToken) {
  // Send updated token to your application server
}).onError((err) {
  // Handle error
});

Apple プラットフォーム — FCM を呼び出す前に APNS トークンが利用可能であることを確認する:

final apnsToken = await FirebaseMessaging.instance.getAPNSToken();
if (apnsToken != null) {
  // Safe to make FCM plugin API requests
}

トークンのライフサイクル(認証状態): トークンはユーザーセッションに関連付けてください。ユーザーがサインインしたらトークンをデータベースに保存し、サインアウトしたらトークンを削除する(またはユーザーのドキュメントから削除する)必要があります。FCM トークンはデバイス固有であり、ユーザーの認証データに本質的に紐付いているわけではありません。サインアウト時に削除しないと、そのデバイスを次に使用するユーザーが前のユーザー宛ての通知を受け取る可能性があります。


5. プラットフォーム固有の動作

  • iOS: ユーザーがアプリスイッチャーからアプリをスワイプして閉じた場合、バックグラウンドメッセージを再び動作させるには、アプリを手動で再度開く必要があります。
  • Android: ユーザーがデバイスの設定からアプリを強制終了した場合、アプリを手動で再度開く必要があります。
  • iOS のフォアグラウンド通知: アプリがフォアグラウンドにある間に通知を表示するには、表示オプションを更新します。
    await FirebaseMessaging.instance.setForegroundNotificationPresentationOptions(
      alert: true,
      badge: true,
      sound: true,
    );
    
  • Android のフォアグラウンド通知: アプリがフォアグラウンドにある間に届いた通知メッセージは、デフォルトでは目に見える通知として表示されません。onMessage ストリームでペイロードを受け取り、独自の UI ロジックまたはローカル通知プラグインを使って、視覚的な通知を手動で表示する必要があります。
  • Android のデフォルトチャンネル: バックグラウンド通知のデフォルトチャンネルを設定するには、AndroidManifest.xml の <application> ブロックに次の meta-data を追加します。
    <meta-data
        android:name="com.google.firebase.messaging.default_notification_channel_id"
        android:value="high_importance_channel" />
    

6. 自動初期化の制御

自動初期化を無効にする — iOS(Info.plist):

FirebaseMessagingAutoInitEnabled = NO

自動初期化を無効にする — Android(AndroidManifest.xml):

<meta-data android:name="firebase_messaging_auto_init_enabled" android:value="false" />
<meta-data android:name="firebase_analytics_collection_enabled" android:value="false" />

実行時に再度有効にする:

await FirebaseMessaging.instance.setAutoInitEnabled(true);
  • 自動初期化の設定は、一度設定するとアプリを再起動しても保持されます。

7. iOS の画像通知

重要: iOS シミュレーターでは、プッシュ通知に画像が表示されません。実機でテストしてください。

  • Xcode で Notification Service Extension を追加します。
  • 画像の処理には、Extension 内で Messaging.serviceExtension().populateNotificationContent() を使用します。
  • Swift:Extension ターゲットに FirebaseMessaging Swift パッケージを追加します。
  • Objective-C:Podfile に Firebase/Messaging pod を追加します。

8. 通知の操作処理

ユーザーが通知をタップすると、アプリが開く(またはフォアグラウンドに戻る)ため、どちらの場合も操作を処理します。

アプリが終了していた場合:

RemoteMessage? initialMessage =
    await FirebaseMessaging.instance.getInitialMessage();
if (initialMessage != null) {
  // Navigate based on message content
}

アプリがバックグラウンドにあった場合:

FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
  // Navigate based on message content
});

通知を受信したときのアプリの状態にかかわらず、スムーズなユーザー体験を実現するために、両方のケースを必ず処理してください。


9. トピックメッセージング

  • トピックを登録すると、オプトインした複数のデバイスにメッセージを送信できます。
  • トピックメッセージは、スループットを優先して最適化されており、遅延よりもスループットが重視される、一般公開情報(例:天気予報の更新)に最適です。
// Subscribe
await FirebaseMessaging.instance.subscribeToTopic("weather_alerts");

// Unsubscribe
await FirebaseMessaging.instance.unsubscribeFromTopic("weather_alerts");

注: Flutter プラグイン経由の Web クライアントでは、subscribeToTopic() と unsubscribeFromTopic() はサポートされていません。


10. テストメッセージの送信

Firebase の公式ドキュメントでは、テスト用のプッシュ通知を送信する手順がわかりにくいことがよくあります。Firebase Console を使ってテスト用または実際のプッシュ通知を送信するには、次の手順に従います。

  1. デバイスの FCM 登録トークンを取得します(セクション 4 を参照)。
  2. Firebase Console にアクセスし、プロジェクトを選択します。
  3. 左側のナビゲーションパネルで Engage(または Run)セクションを見つけ、Messaging(または Cloud Messaging)をクリックします。
  4. New campaign をクリックし、Notifications を選択します。
  5. Notification title と Notification text を入力します。
  6. Send test message をクリックします(多くの場合、画面右側にあるボタンです)。
  7. ダイアログに FCM 登録トークンを入力し、+ アイコンをクリックして追加します。
  8. トークンにチェックが入っていることを確認し、Test をクリックします。

本番ユーザーに自動で実際のプッシュ通知を送信するには、コンソールではなく、サーバー実装(FCM HTTP v1 API または Firebase Admin SDK 経由)が必要です。


11. サーバー側の認証情報とセキュリティ

従来の FCM サーバーキーエンドポイントは 2024 年 6 月に非推奨となりました。プッシュ通知の送信でサポートされているのは HTTP v1 のみです。

HTTP v1 のサーバー間呼び出しを認証するには、サービスアカウントが必要です。

  1. Firebase Console → Project settings → Service accounts に移動します。
  2. Generate new private key をクリックします(.json ファイルがダウンロードされます)。
  3. 重要: このファイルには非常に機密性の高いシークレットが含まれているため、git にコミットしてはいけません。
  4. ファイルを安全に保管する(例:Secret Manager を使用する)か、文字列化した内容を環境変数(FIREBASE_SERVICE_ACCOUNT など)としてバックエンドに渡します。

12. メッセージの送信(HTTP v1)

FCM HTTP v1 メッセージを送信するには、バックエンドで次の処理を行う必要があります。

  1. OAuth2 JWT の交換を完了します(RS256 で署名し、スコープに https://www.googleapis.com/auth/firebase.messaging、エンドポイントに https://oauth2.googleapis.com/token を使用します)。
  2. JSON ペイロードを作成し、https://fcm.googleapis.com/v1/projects/{project_id}/messages:send に POST します。

以下は、Node.js と google-auth-library を使用した、最小限の完全な動作例です。

const { GoogleAuth } = require('google-auth-library');

// Read the securely-stored service account JSON from environment
const credentials = JSON.parse(process.env.FIREBASE_SERVICE_ACCOUNT);

async function getAccessToken() {
  const auth = new GoogleAuth({
    credentials,
    scopes: ['https://www.googleapis.com/auth/firebase.messaging']
  });
  const client = await auth.getClient();
  const token = await client.getAccessToken();
  return token.token;
}

async function sendPushNotification(fcmToken, title, body) {
  const accessToken = await getAccessToken();
  const projectId = credentials.project_id;
  const url = `https://fcm.googleapis.com/v1/projects/${projectId}/messages:send`;
  
  const payload = {
    message: {
      token: fcmToken,
      notification: {
        title: title,
        body: body,
      },
      // Target specific platform features (e.g., channel on Android, sound on iOS)
      android: {
        notification: {
          channel_id: 'high_importance_channel',
        }
      },
      apns: {
        payload: {
          aps: {
            sound: 'default',
          }
        }
      }
    }
  };

  const response = await fetch(url, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${accessToken}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(payload)
  });

  return response.json();
}

参考資料

コメント