方言を話すおしゃべり猫型ロボット『ミーア』をリリースしました(こちらをクリック)

【Flutter×FCM】Push通知を実装:テスト送信まで(iOS/Android)

この記事は約13分で読めます。

はじめに

方言を話すおしゃべり猫型ロボット「ミーア」を開発中。

https://mia-cat.com

以前、こちらの記事で、バックエンドで天気予報情報を取得し、Flutterアプリに表示する機能を実装した。

今回は、ホーム画面上部に表示されている、毎日の天気情報をユーザーが設定した時刻にPush通知できるように改善したいと思う。Firebase Cloud Messagingを利用する。

Firebase Cloud Messagingを使ったPush送信の流れ

Firebase Cloud Messaging(FCM)は、メッセージを無料で確実に送信するためのクロスプラットフォーム メッセージング ソリューション。

https://firebase.google.com/docs/cloud-messaging/fcm-architecture?hl=ja

FCMを用いたプッシュ通知のプロセスは、下記3ステップで構成される。

  • サーバーからのメッセージ送信
  • FCMによる配信処理
  • アプリによる受信と表示

サーバーからFCMへの通知リクエストの送信

  • メッセージの作成: バックエンドサーバー(例えばAWSで動作するGoサーバー)が、FCMに送信するメッセージを作成する。このメッセージには、通知の内容(タイトル、本文など)、送信対象のデバイストークンやトピック、必要に応じて送信時刻(例えば、特定のイベントが発生した時刻)などが含まれる。
  • HTTPリクエストの送信: 作成したメッセージをJSON形式でFCMのAPIエンドポイントにHTTP POSTリクエストとして送信する。このとき、リクエストヘッダーには認証用のサーバーキーが必要。

FCMによるメッセージの処理とデバイスへの送信

  • デバイスの識別: FCMは、サーバーから受け取ったメッセージに基づき、どのデバイスに通知を送るかを決定する。これは、メッセージ送信時に指定されたデバイストークンやトピックによって識別される。デバイストークンはアプリがユーザーのデバイスで初めて起動されたときにFCMから発行され、アプリによって保存されている必要がある。
  • プラットフォーム別の処理: FCMはAndroid、iOS、Webなど、異なるプラットフォームに合わせてメッセージを適切に処理する。例えば、iOSデバイスではAPNsを介して通知が送られ、Androidでは直接FCMから送られる。

アプリによるメッセージの受信と表示

  • メッセージの受信: アプリはFCMからのメッセージをバックグラウンドまたはフォアグラウンドで受信することができる。受信したときの挙動は、アプリが起動している状態(フォアグラウンド)か、バックグラウンド(または終了状態)かによって異なる。
  • 通知の表示: アプリがフォアグラウンドにある場合、アプリは受信したデータを基にカスタム通知を表示するか、または何も表示しないかを選択できる。バックグラウンドの場合、デバイスのシステムはFCMから受信した通知を自動的に表示する。

FCMを用いたPush通知の全体のプロセスを把握できたので、実装開始。

アプリ(Flutter)側の実装

FCMクライアントアプリを設定

下記に沿って、FLutterでFCMクライアント設定を進めていく。

https://firebase.google.com/docs/cloud-messaging/flutter/client?hl=ja

iOS:プッシュ通知とバックグランドモードを有効にする

アプリでメッセージを受信できるようにするには、Xcode プロジェクトでプッシュ通知とバックグラウンド モードを有効にする必要がある。

Xcodeを開いて、ターゲットを選択し、「Runner」をクリック
→Signing & Capabilities」タブを選択
→左上の「+ Capability」ボタンをクリック
検索ボックスに「Push Notifications」「Background Modes」と入力し、追加する。

Background Modesの方は、プッシュ通知を受け取るために必要な「Background fetch」と「Remote notifications」にチェックをつけて有効にする。

iOS:APNs認証キー(p8)を作成してFirebaseにアップロード

FCMでAPNs(Apple Push Notification service)に接続する際、APNs認証キーとAPNs証明書の主に2つの方法があるが、Firebaseでは認証キーを推奨している。

APNs 認証キーは、複数のアプリケーションや異なる環境(開発と本番)に対して再利用可能なキーであり、APNs証明書と違い、一度設定すると何度も再発行する必要がなく、管理が容易。

認証キーは Apple デバイスに通知を送信する最新の方法であるため、これを使用して構成することをおすすめします

というわけで、認証キーをApple Developer Accountで作成し、Firebaseに登録する。

認証キー作成方法はこちら

https://developer.apple.com/jp/help/account/manage-keys/create-a-private-key/

Certificates, Identifiers & Profilesのタブをクリックして、左サイドバーからKeysを選択し、Keysの右隣の+ボタンより、新規作成する。

「Key Name(キーの名前)」で、鍵の固有名を入力し、Apple Push Notification とMedia Servicesにチェックを入れる。

Screenshot

また、認証キーを作成するには、メディアIDの登録が事前に必要。

証明書、ID、プロファイル」で、サイドバーにある「Identifiers(ID)」をクリックし、左上にある追加ボタン(+)をクリックして「Media IDs(メディアID)」を選択し、「Continue(続ける)」をクリックする。

    https://developer.apple.com/jp/help/account/configure-app-capabilities/create-a-media-identifier-and-private-key#register-a-media-identifier

    無事作成を終えると、認証キーをダウンロードできるようになり、拡張子が.p8のテキストファイルとしてダウンロードフォルダに保存される。

    Firebase Consoleでの設定

    Firebaseプロジェクトの設定ページに移動し、「Cloud Messaging」タブを開く。APNs認証キーのアップロードをクリックして、ダウンロードしたp8ファイルをアップロードする。キーIDとチームIDも合わせて記載する。

    ちなみに、Androidに関しては、上記のiOSのような面倒な(?)追加設定は不要。Android4.4以降を搭載しているデバイスもしくはエミュレーターがあれば良い。

    テストメッセージ送信(バックグラウンド)

    FCM を使用する手始めとして、まず、アプリがバックグラウンドで動作しているときに、Notifications Composer から開発デバイスにテスト通知メッセージを送信する。

    FCMプラグインをインストール

    Dart
    $ flutter pub add firebase_messaging
    $ flutter pub get

    https://firebase.google.com/docs/cloud-messaging?hl=ja

    デバイスの登録トークンにアクセスする

    特定のデバイスにメッセージを送信するには、そのデバイスの登録トークンを知っておく必要がある。

    getToken() をmain.dart内で呼び出して、アプリ起動時にトークンを取得して、コンソールに表示する。

    Dart
    import 'package:flutter/material.dart';
    import 'package:firebase_core/firebase_core.dart';
    import 'package:firebase_messaging/firebase_messaging.dart';
    
    void main() async {
      WidgetsFlutterBinding.ensureInitialized();
      await Firebase.initializeApp();
      
      // FCM の通知権限リクエスト
      final 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}');
    
      // トークンを取得して表示(デバッグ用)
      String? fcmToken = await messaging.getToken();
      print('FCM TOKEN: $fcmToken');
    
      runApp(MyApp());
    }

    flutter runしたところ、無事FCM Tokenがログに表示されたので控えておく。

    Dart
    flutter: FCM Token: XXXXXXXXXXXXXXXXXXXXXXXXXXXX

    テスト通知メッセージを送信する

    このトークンを使用してFirebaseコンソールからプッシュ通知を送信する際には、テストの段階では「Cloud Messaging」セクションの「テスト送信」機能を利用して、直接トークンに通知を送ることができる。

    Firebase コンソールで [Messaging] ページを開く。

    [最初のキャンペーンを作成] を選択し、[Firebase Notification メッセージ] を選択し、[作成] を選択する。

    通知タイトルと、通知テキストを記入する。右側に、通知プレビューが表示される。

    「テストメッセージを送信」ボタンをクリック。

    [FCM 登録トークンを追加] というラベルの付いたフィールドで、このガイドの前のセクションで取得した登録トークンを入力し、テストを選択する。

    入力した後に右側の「+」ボタンを押さないと、FCMトークンが反映されず、「テスト」ボタンを押せない。最初分からなくて、FCMトークン取得が間違っているのでは?と無駄に時間を食ってしまった。UI改善してほしい。

    [テスト] を選択すると、アプリをバックグラウンド状態にすることで、iOS実機で通知が表示された!

    ちなみに、Androidエミュレーターでも同様にテストしてバックグラウンド送信できることを確認した。

    バックグラウンドでの通知をタップした際の処理は、FirebaseMessaging.onMessageOpenedAppを追加して制御できるが、今回は、通知をユーザーがタップしたときにアプリのホーム画面に移動すれば良いので、何も設定せずにデフォルトの動作のままで良さそうなので、追加実装せず。

    実際に通知をタップすると、ホーム画面が開いた。

    テストメッセージ送信(フォアグラウンド)

    iOSでフォアグラウンド時にFCMのプッシュ通知を受け取るには、Flutterアプリで特定のリスナーを設定する必要がある。これにより、アプリがアクティブな状態であっても通知メッセージをキャッチし、適切に処理できるようになる。

    FirebaseMessaging.onMessageを使用して、アプリがフォアグラウンドで動作している間に通知を受け取り、overlay_supportパッケージを使用して、オーバーレイ表示する。

    Dart
    // lib/main.dart
    import 'package:firebase_core/firebase_core.dart';
    import 'package:firebase_messaging/firebase_messaging.dart';
    import 'package:flutter/material.dart';
    import 'package:overlay_support/overlay_support.dart';
    
    void main() async {
      WidgetsFlutterBinding.ensureInitialized();
      await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
    
      // FCM の通知権限リクエスト
      final 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}');
    
      // トークンの取得
      final token = await messaging.getToken();
      print('FCM TOKEN: $token');
    
      // フォアグラウンドでの通知受信時のハンドラ
      FirebaseMessaging.onMessage.listen((RemoteMessage message) {
        // 通知があるか確認
        if (message.notification != null) {
          // 通知のタイトルと本文をオーバーレイで表示
          showSimpleNotification(
            Text('${message.notification!.title}'),
            subtitle: Text('${message.notification!.body}'),
            background: Colors.blue,
            duration: const Duration(seconds: 3),
          );
        }
      });
    
      runApp(const ProviderScope(child: ClockyApp()));
    }

    アプリをフォアグラウンド状態にして、FCMでテスト送信を行うと、無事Push通知が表示された。

    その後、通知のUIをアプリカラーに合わせて、右側に閉じるアイコンを表示するなど軽微な修正を行い、Androidエミュレーターで確認した結果が右図。

    iOS実機でのフォアグラウンド送信テスト
    Androidエミュレーターでのフォアグラウンド送信テスト

    これで、FCMのアプリ受信側の設定は完了したので、次は、定期的にFCMへPush通知を送信する機能のサーバー側の実装を行う。

    コメント

    タイトルとURLをコピーしました