Catch up on everthing we announced at this year's Firebase Summit. Learn more

Apple プラットフォームで複数のデバイスにメッセージを送信する

Firebase Cloud Messaging では、次の 2 つの方法で複数のデバイスにメッセージを送信できます。

このチュートリアルでは、アプリサーバーから Admin SDK または REST API を使用して FCM 向けにトピック メッセージを送信し、それらのメッセージを Apple アプリで受信して処理する手順に焦点を絞っています。このページには、このチュートリアルに必要なセットアップから検証までのすべての手順が記載されています。Apple クライアント アプリでの FCM の設定または最初のメッセージを送信する手順がすでに済んでいる場合は、一部の手順を省略できます。

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

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

前提条件

  • 以下をインストールします。

    • Xcode 12.2 以降
    • CocoaPods 1.10.0 以降
  • プロジェクトが次の要件を満たしていることを確認します。

    • 以下のバージョンまたはそれ以降をターゲットにする必要があります。
      • iOS 10
      • macOS 10.12
      • tvOS 12
      • watchOS 6
  • アプリを実行する物理的な Apple デバイスを設定し、次のタスクを完了します。

    • Apple Developer アカウントの Apple Push Notification Authentication Key を取得します。
    • プッシュ通知を XCode の [App] > [Capabilities] で有効にします。

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

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

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

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

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

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

  1. Firebase コンソールに移動します。

  2. プロジェクトの概要ページの中央にある iOS+ アイコンをクリックして、設定ワークフローを起動します。

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

  3. アプリのバンドル ID を [バンドル ID] フィールドに入力します。

  4. (省略可)その他のアプリ情報(アプリのニックネームApp Store ID)を入力します。

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

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

  1. [GoogleService-Info.plist をダウンロード] をクリックして、Firebase Apple プラットフォーム構成ファイル(GoogleService-Info.plist)を取得します。

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

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

アプリに Firebase SDK を追加する

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

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

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

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

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

    Firebase Cloud Messaging のエクスペリエンスを最適化するために、プロジェクトで Google アナリティクスを有効にすることをおすすめします。また、アナリティクスの設定の一環として、アナリティクス用 Firebase SDK をアプリに追加する必要があります。

    アナリティクスが有効な場合

    # Add the Firebase pod for Google Analytics
    pod 'Firebase/Analytics'
    
    # For Analytics without IDFA collection capability, use this pod instead
    # pod ‘Firebase/AnalyticsWithoutAdIdSupport’
    
    # Add the pod for Firebase Cloud Messaging
    pod 'Firebase/Messaging'

    デバイスレベルの広告識別子である IDFA について詳しくは、Apple のユーザーのプライバシーとデータの使用と、App Tracking Transparency に関するドキュメントをご覧ください。

    アナリティクスが無効な場合

    # Add the pod for Firebase Cloud Messaging
    pod 'Firebase/Messaging'
  3. ポッドをインストールし、.xcworkspace ファイルを開いて Xcode でプロジェクトを確認します。

    pod install
    open your-project.xcworkspace

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

APNs 認証キーを Firebase にアップロードします。まだ APNs 認証キーを用意していない場合は、Apple Developer Member Center で作成してください。

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

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

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

アプリで Firebase を初期化する

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

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

    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 は、他のダウンストリーム メッセージと同じようにトピック メッセージを配信します。

次のように application(_:didReceiveRemoteNotification:fetchCompletionHandler:) を実装します。

Swift

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
    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 full message.
  NSLog(@"%@", userInfo);

  completionHandler(UIBackgroundFetchResultNewData);
}

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

トピックを作成した後、クライアント側でクライアント アプリ インスタンスをトピックに登録するか、またはサーバー API を使用することによって、トピックにメッセージを送信できます。FCM の送信リクエストを初めて作成する場合は、サーバー環境と FCM に関するガイドで、重要な背景と設定に関する情報をご覧ください。

バックエンドの送信ロジック内で、次のように目的のトピック名を指定します。

Node.js

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

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

// Send a message to devices subscribed to the provided topic.
getMessaging().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)"

FCM はまず、かっこ内の条件を評価し、次に左から右に式を評価していきます。上記の式では、いずれか 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.
const condition = '\'stock-GOOG\' in topics || \'industry-tech\' in topics';

// See documentation on defining a message payload.
const message = {
  notification: {
    title: '$FooCorp up 1.43% on the day',
    body: '$FooCorp 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.
getMessaging().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(Notification.builder()
        .setTitle("$GOOG up 1.43% on the day")
        .setBody("$GOOG gained 11.80 points to close at 835.67, up 1.43% on the day.")
        .build())
    .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

次のステップ