コンソールへ移動

iOS で複数のデバイスにメッセージを送信する

Firebase Cloud Messaging では、複数のデバイスをメッセージの対象にする方法が 2 つあります。

このチュートリアルは、FCM 用に HTTP または XMPP プロトコルを使用するアプリサーバーからのトピック メッセージの送信、および iOS アプリでのそれらのメッセージの受信と処理に重点を置いています。このページでは、そのためのセットアップから検証までの全手順を記載しているため、FCM 用の iOS クライアント アプリの設定最初のメッセージ送信をすでに行っている場合は、完了済みの手順が含まれている場合があります。

Firebase を iOS プロジェクトに追加する

このセクションで説明しているタスクは、他の Firebase 機能をアプリですでに有効にしている場合は完了している可能性があります。FCM の場合には、APNs 認証キーをアップロードしてリモート通知に登録する必要があります。

前提条件

  • Xcode 10.1 以降をインストールします。

  • CocoaPods 1.4.0 以降をインストールします。

  • Xcode でプロジェクトを開きます。

    • プロジェクトのターゲットは iOS 8 以降に設定してください。

    • Swift プロジェクトには Swift 3.0 以降を使用してください。

  • アプリを実行する物理 iOS デバイスまたは iOS エミュレータを設定します。

    • Cloud Messaging を使用する場合は、以下のものが必要となります。

      • 物理 iOS デバイス
      • Apple Developer アカウントの Apple Push Notification Authentication Key
      • Xcode で [App] > [Capabilities] でプッシュ通知を有効にする
    • その他のすべての Firebase プロダクトで、物理 iOS デバイスと iOS シミュレータのいずれかを使用できます。

  • Google アカウントを使用して Firebase にログインします。

Xcode プロジェクトをまだ用意していない場合、Firebase プロダクトを試すだけであれば、クイックスタート サンプルをダウンロードしてご利用いただけます。

Firebase プロジェクトを作成する

iOS アプリに Firebase を追加する前に、iOS アプリに接続するための Firebase プロジェクトを作成します。Firebase プロジェクトの詳細については、Firebase プロジェクトについて理解するをご覧ください。

Firebase を使用してアプリを登録する

Firebase プロジェクトを作成したら、プロジェクトに iOS アプリを追加できます。

Firebase プロジェクトにアプリを追加するベスト プラクティス、考慮事項(複数のビルド バリエーションの扱い方など)の詳細については、Firebase プロジェクトについて理解するをご覧ください。

  1. Firebase コンソールの [Project Overview] ページの中央にある iOS アイコンをクリックして設定ワークフローを起動します。

    すでに Firebase プロジェクトにアプリまたはゲームを追加している場合は、[アプリを追加] をクリックするとプラットフォームのオプションが表示されます。

  2. アプリのバンドル ID を [iOS バンドル ID] に入力します。

    • このバンドル ID を見つけるには、XCode でアプリを開き、最上位の Runner ディレクトリの [全般] タブにアクセスします。[バンドル ID] フィールドの値が iOS バンドル ID です(例: com.yourcompany.yourproject)。
  3. (省略可)設定ワークフローの指示に従って他のアプリ情報を入力します。

  4. [アプリの登録] をクリックします。

Firebase 構成ファイルを追加する

  1. [Download GoogleService-Info.plist] をクリックして、Firebase iOS 構成ファイル(GoogleService-Info.plist)を入手します。

    Firebase iOS 構成ファイルはいつでも再ダウンロードできます。

  2. 構成ファイルを Xcode プロジェクトのルートに移動します。メッセージが表示されたら、構成ファイルをすべてのターゲットに追加するオプションを選択します。

プロジェクトに複数のバンドル ID がある場合は、Firebase コンソールで各バンドル ID を登録済みアプリに関連付けて、各アプリで独自の GoogleService-Info.plist ファイルを設定できるようにする必要があります。

アプリに Firebase SDK を追加する

Firebase ライブラリのインストールには CocoaPods を使用することをおすすめします。ただし、CocoaPods を使用せずに、SDK フレームワークを直接統合する方法もあります。

いずれかのクイックスタート サンプルを使用する場合、Xcode プロジェクトと(ポッドが含まれる)Podfile がすでに存在していても、Firebase 構成ファイルを追加してポッドをインストールする必要があります。

サポートされている Firebase プロダクトを iOS アプリに追加できます。

  1. Podfile がない場合は作成します。

    cd your-project-directory
    pod init
  2. アプリで使用したいポッドを Podfile に追加します。

    pod 'Firebase/Core'
    pod 'Firebase/Messaging'

    このポッドにより、iOS アプリで Firebase を稼働させるために必要な前提条件のライブラリが追加されます。

  3. ポッドをインストールし、.xcworkspace ファイルを開いて Xcode でプロジェクトを確認します。

    pod install
    open your-project.xcworkspace

APNs 認証キーをアップロードする

APNs 認証キーを Firebase にアップロードします。APNs 認証キーがない場合は、FCM での APNs の構成に関する説明をご覧ください。

  1. Firebase コンソールのプロジェクト内で歯車アイコンを選択し、[プロジェクトの設定]、[クラウド メッセージング] タブの順に選択します。

  2. [iOS アプリの設定] の下の [APNs 認証キー] で [アップロード] ボタンをクリックします。

  3. キーが保存されている場所に移動し、キーを選択して [開く] をクリックします。キーのキー ID(Apple Developer Member Center の [Certificates, Identifiers & Profiles] で確認できます)を追加し、[アップロード] をクリックします。

アプリで Firebase を初期化する

Firebase 初期化コードをアプリケーションに追加する必要があります。Firebase モジュールをインポートして、次に示すように共有インスタンスを構成します。

  1. UIApplicationDelegate で Firebase モジュールをインポートします。

    Swift

    import Firebase

    Objective-C

    @import Firebase;
  2. FirebaseApp 共有インスタンスを構成します。通常はアプリの application:didFinishLaunchingWithOptions: メソッドで行います。

    Swift

    // Use Firebase library to configure APIs
    FirebaseApp.configure()

    Objective-C

    // Use Firebase library to configure APIs
    [FIRApp configure];

リモート通知に登録する

起動時またはアプリケーション フローの必要な時点で、リモート通知にアプリを登録します。次のように registerForRemoteNotifications を呼び出します。

Swift

if #available(iOS 10.0, *) {
  // For iOS 10 display notification (sent via APNS)
  UNUserNotificationCenter.current().delegate = self

  let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]
  UNUserNotificationCenter.current().requestAuthorization(
    options: authOptions,
    completionHandler: {_, _ in })
} else {
  let settings: UIUserNotificationSettings =
  UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil)
  application.registerUserNotificationSettings(settings)
}

application.registerForRemoteNotifications()

Objective-C

if ([UNUserNotificationCenter class] != nil) {
  // iOS 10 or later
  // For iOS 10 display notification (sent via APNS)
  [UNUserNotificationCenter currentNotificationCenter].delegate = self;
  UNAuthorizationOptions authOptions = UNAuthorizationOptionAlert |
      UNAuthorizationOptionSound | UNAuthorizationOptionBadge;
  [[UNUserNotificationCenter currentNotificationCenter]
      requestAuthorizationWithOptions:authOptions
      completionHandler:^(BOOL granted, NSError * _Nullable error) {
        // ...
      }];
} else {
  // iOS 10 notifications aren't available; fall back to iOS 8-9 notifications.
  UIUserNotificationType allNotificationTypes =
  (UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge);
  UIUserNotificationSettings *settings =
  [UIUserNotificationSettings settingsForTypes:allNotificationTypes categories:nil];
  [application registerUserNotificationSettings:settings];
}

[application registerForRemoteNotifications];

クライアント アプリをトピックに登録する

クライアント アプリを既存のトピックに登録することも、新しいトピックを作成することもできます。クライアント アプリを新しいトピック名(Firebase プロジェクトにまだ存在していないトピック名)に登録すると、その名前の新しいトピックが FCM に作成され、その後すべてのクライアントはそのトピック名に登録できるようになります。

トピックに登録するには、アプリケーションのメインスレッドから登録メソッドを呼び出します(FCM はスレッドセーフではありません)。最初の登録リクエストが失敗すると、FCM は自動的に再試行します。登録を完了できなければ、その登録はエラーをスローします。その場合、次のように完了ハンドラでエラーをキャッチできます。

Swift

Messaging.messaging().subscribe(toTopic: "weather") { error in
  print("Subscribed to weather topic")
}

Objective-C

[[FIRMessaging messaging] subscribeToTopic:@"weather"
                                completion:^(NSError * _Nullable error) {
  NSLog(@"Subscribed to weather topic");
}];

これにより、FCM バックエンドへの非同期リクエストが作成され、所定のトピックにクライアントが登録されます。subscribeToTopic:topic を呼び出す前には、コールバック didReceiveRegistrationToken を介してクライアント アプリ インスタンスがすでに登録トークンを受信していることを確認します。

アプリが起動するごとに、FCM は、リクエストされたすべてのトピックが登録されたことを確認します。登録解除するには、unsubscribeFromTopic:topic を呼び出します。これにより、FCM がバックグラウンドでトピックからの登録解除を行います。

トピック メッセージを受信して処理する

FCM は、他のダウンストリーム メッセージと同じようにトピック メッセージを配信します。

AppDelegate application:didReceiveRemoteNotification: を次のように実装します。

Swift

func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any]) {
  // If you are receiving a notification message while your app is in the background,
  // this callback will not be fired till the user taps on the notification launching the application.
  // TODO: Handle data of notification

  // With swizzling disabled you must let Messaging know about the message, for Analytics
  // Messaging.messaging().appDidReceiveMessage(userInfo)

  // Print message ID.
  if let messageID = userInfo[gcmMessageIDKey] {
    print("Message ID: \(messageID)")
  }

  // Print full message.
  print(userInfo)
}

func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any],
                 fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
  // If you are receiving a notification message while your app is in the background,
  // this callback will not be fired till the user taps on the notification launching the application.
  // TODO: Handle data of notification

  // With swizzling disabled you must let Messaging know about the message, for Analytics
  // Messaging.messaging().appDidReceiveMessage(userInfo)

  // Print message ID.
  if let messageID = userInfo[gcmMessageIDKey] {
    print("Message ID: \(messageID)")
  }

  // Print full message.
  print(userInfo)

  completionHandler(UIBackgroundFetchResult.newData)
}

Objective-C

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
  // If you are receiving a notification message while your app is in the background,
  // this callback will not be fired till the user taps on the notification launching the application.
  // TODO: Handle data of notification

  // With swizzling disabled you must let Messaging know about the message, for Analytics
  // [[FIRMessaging messaging] appDidReceiveMessage:userInfo];

  // Print message ID.
  if (userInfo[kGCMMessageIDKey]) {
    NSLog(@"Message ID: %@", userInfo[kGCMMessageIDKey]);
  }

  // Print full message.
  NSLog(@"%@", userInfo);
}

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
    fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
  // If you are receiving a notification message while your app is in the background,
  // this callback will not be fired till the user taps on the notification launching the application.
  // TODO: Handle data of notification

  // With swizzling disabled you must let Messaging know about the message, for Analytics
  // [[FIRMessaging messaging] appDidReceiveMessage:userInfo];

  // Print message ID.
  if (userInfo[kGCMMessageIDKey]) {
    NSLog(@"Message ID: %@", userInfo[kGCMMessageIDKey]);
  }

  // Print full message.
  NSLog(@"%@", userInfo);

  completionHandler(UIBackgroundFetchResultNewData);
}

送信リクエストを作成する

トピックを作成した後、クライアント側でクライアント アプリ インスタンスをトピックに登録するか、またはサーバー API を使用することによって、トピックにメッセージを送信できます。バックエンドの送信ロジック内で、次のように目的のトピック名を指定します。

Node.js

// The topic name can be optionally prefixed with "/topics/".
var topic = 'highScores';

var message = {
  data: {
    score: '850',
    time: '2:45'
  },
  topic: topic
};

// Send a message to devices subscribed to the provided topic.
admin.messaging().send(message)
  .then((response) => {
    // Response is a message ID string.
    console.log('Successfully sent message:', response);
  })
  .catch((error) => {
    console.log('Error sending message:', error);
  });

Java

// The topic name can be optionally prefixed with "/topics/".
String topic = "highScores";

// See documentation on defining a message payload.
Message message = Message.builder()
    .putData("score", "850")
    .putData("time", "2:45")
    .setTopic(topic)
    .build();

// Send a message to the devices subscribed to the provided topic.
String response = FirebaseMessaging.getInstance().send(message);
// Response is a message ID string.
System.out.println("Successfully sent message: " + response);

Python

# The topic name can be optionally prefixed with "/topics/".
topic = 'highScores'

# See documentation on defining a message payload.
message = messaging.Message(
    data={
        'score': '850',
        'time': '2:45',
    },
    topic=topic,
)

# Send a message to the devices subscribed to the provided topic.
response = messaging.send(message)
# Response is a message ID string.
print('Successfully sent message:', response)

Go

// The topic name can be optionally prefixed with "/topics/".
topic := "highScores"

// See documentation on defining a message payload.
message := &messaging.Message{
	Data: map[string]string{
		"score": "850",
		"time":  "2:45",
	},
	Topic: topic,
}

// Send a message to the devices subscribed to the provided topic.
response, err := client.Send(ctx, message)
if err != nil {
	log.Fatalln(err)
}
// Response is a message ID string.
fmt.Println("Successfully sent message:", response)

C#

// The topic name can be optionally prefixed with "/topics/".
var topic = "highScores";

// See documentation on defining a message payload.
var message = new Message()
{
    Data = new Dictionary<string, string>()
    {
        { "score", "850" },
        { "time", "2:45" },
    },
    Topic = topic,
};

// Send a message to the devices subscribed to the provided topic.
string response = await FirebaseMessaging.DefaultInstance.SendAsync(message);
// Response is a message ID string.
Console.WriteLine("Successfully sent message: " + response);

REST

POST https://fcm.googleapis.com/v1/projects/myproject-b5ae1/messages:send HTTP/1.1

Content-Type: application/json
Authorization: Bearer ya29.ElqKBGN2Ri_Uz...HnS_uNreA
{
  "message":{
    "topic" : "foo-bar",
    "notification" : {
      "body" : "This is a Firebase Cloud Messaging Topic Message!",
      "title" : "FCM Message"
      }
   }
}

cURL コマンド:

curl -X POST -H "Authorization: Bearer ya29.ElqKBGN2Ri_Uz...HnS_uNreA" -H "Content-Type: application/json" -d '{
  "message": {
    "topic" : "foo-bar",
    "notification": {
      "body": "This is a Firebase Cloud Messaging Topic Message!",
      "title": "FCM Message"
    }
  }
}' https://fcm.googleapis.com/v1/projects/myproject-b5ae1/messages:send HTTP/1.1

トピックの組み合わせにメッセージを送信するには、条件を指定します。条件は、ターゲット トピックを指定するブール式です。たとえば次の条件は、TopicA に加えて TopicBTopicC のどちらか一方にも登録されているデバイスにメッセージを送信します。

"'TopicA' in topics && ('TopicB' in topics || 'TopicC' in topics)"

条件式はまずかっこ内から評価され、次に左から右に評価されます。上記の式では、いずれか 1 つのトピックだけに登録したユーザーにはメッセージは送られません。同様に、TopicA に登録していないユーザーにもメッセージは送られません。メッセージが送られるのは、次の組み合わせに登録している場合のみです。

  • TopicATopicB
  • TopicATopicC

条件式には最大 5 つのトピックを含めることができます。

条件に送信するには:

Node.js

// Define a condition which will send to devices which are subscribed
// to either the Google stock or the tech industry topics.
var condition = "'stock-GOOG' in topics || 'industry-tech' in topics";

// See documentation on defining a message payload.
var message = {
  notification: {
    title: '$GOOG up 1.43% on the day',
    body: '$GOOG gained 11.80 points to close at 835.67, up 1.43% on the day.'
  },
  condition: condition
};

// Send a message to devices subscribed to the combination of topics
// specified by the provided condition.
admin.messaging().send(message)
  .then((response) => {
    // Response is a message ID string.
    console.log('Successfully sent message:', response);
  })
  .catch((error) => {
    console.log('Error sending message:', error);
  });

Java

// Define a condition which will send to devices which are subscribed
// to either the Google stock or the tech industry topics.
String condition = "'stock-GOOG' in topics || 'industry-tech' in topics";

// See documentation on defining a message payload.
Message message = Message.builder()
    .setNotification(new Notification(
        "$GOOG up 1.43% on the day",
        "$GOOG gained 11.80 points to close at 835.67, up 1.43% on the day."))
    .setCondition(condition)
    .build();

// Send a message to devices subscribed to the combination of topics
// specified by the provided condition.
String response = FirebaseMessaging.getInstance().send(message);
// Response is a message ID string.
System.out.println("Successfully sent message: " + response);

Python

# Define a condition which will send to devices which are subscribed
# to either the Google stock or the tech industry topics.
condition = "'stock-GOOG' in topics || 'industry-tech' in topics"

# See documentation on defining a message payload.
message = messaging.Message(
    notification=messaging.Notification(
        title='$GOOG up 1.43% on the day',
        body='$GOOG gained 11.80 points to close at 835.67, up 1.43% on the day.',
    ),
    condition=condition,
)

# Send a message to devices subscribed to the combination of topics
# specified by the provided condition.
response = messaging.send(message)
# Response is a message ID string.
print('Successfully sent message:', response)

Go

// Define a condition which will send to devices which are subscribed
// to either the Google stock or the tech industry topics.
condition := "'stock-GOOG' in topics || 'industry-tech' in topics"

// See documentation on defining a message payload.
message := &messaging.Message{
	Data: map[string]string{
		"score": "850",
		"time":  "2:45",
	},
	Condition: condition,
}

// Send a message to devices subscribed to the combination of topics
// specified by the provided condition.
response, err := client.Send(ctx, message)
if err != nil {
	log.Fatalln(err)
}
// Response is a message ID string.
fmt.Println("Successfully sent message:", response)

C#

// Define a condition which will send to devices which are subscribed
// to either the Google stock or the tech industry topics.
var condition = "'stock-GOOG' in topics || 'industry-tech' in topics";

// See documentation on defining a message payload.
var message = new Message()
{
    Notification = new Notification()
    {
        Title = "$GOOG up 1.43% on the day",
        Body = "$GOOG gained 11.80 points to close at 835.67, up 1.43% on the day.",
    },
    Condition = condition,
};

// Send a message to devices subscribed to the combination of topics
// specified by the provided condition.
string response = await FirebaseMessaging.DefaultInstance.SendAsync(message);
// Response is a message ID string.
Console.WriteLine("Successfully sent message: " + response);

REST

POST https://fcm.googleapis.com/v1/projects/myproject-b5ae1/messages:send HTTP/1.1

Content-Type: application/json
Authorization: Bearer ya29.ElqKBGN2Ri_Uz...HnS_uNreA
{
   "message":{
    "condition": "'dogs' in topics || 'cats' in topics",
    "notification" : {
      "body" : "This is a Firebase Cloud Messaging Topic Message!",
      "title" : "FCM Message",
    }
  }
}

cURL コマンド:

curl -X POST -H "Authorization: Bearer ya29.ElqKBGN2Ri_Uz...HnS_uNreA" -H "Content-Type: application/json" -d '{
  "notification": {
    "title": "FCM Message",
    "body": "This is a Firebase Cloud Messaging Topic Message!",
  },
  "condition": "'dogs' in topics || 'cats' in topics"
}' https://fcm.googleapis.com/v1/projects/myproject-b5ae1/messages:send HTTP/1.1

次のステップ