趣味で iOS アプリを作っているのですが、今回、 Firebase Cloud Messaging でのリモートプッシュ通知に対応したので、対応方法を紹介します。 今回リモートプッシュ通知に APNs にダイレクトに通知を行うのではなく、 Firebase Cloud Messaging を使った理由は、実運用するに当たって Vercel などの Serverless 環境では推奨要件を満たすのが難しかったので、マネージドサービスである Firebase Cloud Messaging を使うことで推奨要件を満たすのをお任せしよう、という選択があったりします。
APNs にダイレクトに通知を行う方法は、次の記事が詳しかったです:
事前準備が必要なもの
この記事ではいくつか事前準備が必要です。特に、 iOS 端末実機がないと、プッシュ通知は受け取れないので注意してください。
また、有効な Apple Developer アカウントも必要です、登録して課金しておきましょう。
- iOS 端末 (実機)
- Apple Developer アカウント
Apple Developer Member Center での操作
Apple Developer Member Center の画面から、証明書を作成します。開発環境 (Sandbox) と本番環境 (Production) のそれぞれで証明書を発行します。ここでは本番環境を例に説明しますが、開発環境用の証明書も同様に発行してください。
まず、 Certificates, Identifiers & Profiles のページから Keys タブにて、新しくキーを2つ作成します。
キーを作成する画面に遷移したら、 Key Name に判別しやすい名前を入れて、 Apple Push Notifications service (APNs) にチェックを入れ、 Configure から設定を行います。

Configure から、環境とアプリ選択画面に移動するので、Environment (画像は Production) と、 Key Restriction の設定 (今回は Team Scoped (All Topics)) をして、保存する。

設定後、 Continue をクリックすると確認画面が出るので、登録する。

確認後、登録すると証明書がダウンロードできるので、ダウンロードする。ここでしかダウンロード出来ないので、忘れないように&無くさないように注意。
Xcode での操作
次は Xcode でアプリ側の設定を行います。 プロジェクトを開いて、「Capability」から、「Background Modes」を追加します。


そして、 Background Modes のチェック欄の「Remote notifications」にチェックを入れます。

「Push Notifications」も同様に追加します。

これでアプリ側の設定は完了です。
Firebase での操作
先ほど Apple Developer Member Center にて作成した証明書を、 Firebase のプロジェクトの設定 → Cloud Messaging の設定から、 Apple アプリの構成のところでアップロードします。
開発用 APNs 認証キーには Sandbox 環境の証明書を、本番環境用 APNs 認証キーには Production 環境の証明書をアップロードします。
Key ID には AuthKey_xxx.p8 の xxx の部分を、 Team ID には Apple Developer Member Center の右上に表示されている名前の隣の文字列をいれます。
最終的にはこんな感じになれば OK。

また、同様に Firebase のプロジェクトの設定 → 全般のマイアプリのセクションから、 GoogleService-Info.plist もダウンロードし、プロジェクトルートに設置します。
これは設置するだけで OK で、設置後特に設定をいじったりする必要はありません。
SDK の設定
次に、 Xcode にて Firebase SDK を追加します。
Package Dependencies から、 https://github.com/firebase/firebase-ios-sdk を追加し、 FirebaseAnalytics と FirebaseMessaging を追加します。
SDK を追加後、次のようなコードを (SwiftUI アプリの場合は XXXApp.swift に) 追加します。
// @ CatalystApp.swift import FirebaseCore // 追加 import FirebaseMessaging // 追加 import UserNotifications // 追加 // 追加 class AppDelegate: NSObject, UIApplicationDelegate { // アプリケーション起動時に呼ばれるコード func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil ) -> Bool { FirebaseApp.configure() // テスト用に、一旦ここで通知許可ダイアログを出す // 本当は設定画面などで Toggle を ON にしたときに出すと良いはず UNUserNotificationCenter.current().requestAuthorization(options: [ alert, .sound, .badge, ]) { granted, error in #if DEBUG print("DEBUG: Permission granted: \(granted)") #endif } // Push通知デリゲート設定 UNUserNotificationCenter.current().delegate = self Messaging.messaging().delegate = self // リモート通知に必要 application.registerForRemoteNotifications() return true } // APNs での通知に必要なデバイストークンの取得時に呼ばれるコード func application( _ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data ) { #if DEBUG let tokenParts = deviceToken.map { data in String(format: "%02.2hhx", data) } let token = tokenParts.joined() print("DEBUG: APNs device token: \(token)") #endif Messaging.messaging().apnsToken = deviceToken } // リモート通知の登録に失敗したときに呼ばれるコード func application( _ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error ) { #if DEBUG print("DEBUG: Failed to register for remote notifications: \(error)") #endif } } @main struct CatalystApp: App { // 追加 @UIApplicationDelegateAdaptor(AppDelegate.self) var delegate init() { } var body: some Scene { WindowGroup { SplashScreenView() } } }
次に、 AppDelegate+UserNotificationCenter.swift みたいな感じで、拡張を書きます。
// @AppDelegate+UserNotificationCenter.swift import FirebaseCore import FirebaseMessaging import SwiftUI extension AppDelegate: UNUserNotificationCenterDelegate { // アプリがフォアグラウンド時に通知を受け取ったときの処理 func userNotificationCenter( _ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping ( UNNotificationPresentationOptions ) -> Void ) { // とりあえずありとあらゆる方法で通知する completionHandler([[.banner, .list, .sound]]) } // 通知をタップしたときの処理 func userNotificationCenter( _ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void ) { // いったんは何もしない completionHandler() } } extension AppDelegate: MessagingDelegate { @objc func messaging( _ messaging: Messaging, didReceiveRegistrationToken fcmToken: String? ) { #if DEBUG print("DEBUG: Firebase token: \(String(describing: fcmToken))") #endif // 実際は、ここでアプリケーションに FCM Token を保存する // 設定画面で Toggle などで通知設定を ON にしたとき、サーバーにも FCM Token と Device Token を送信するようにする } }
ここまで来れば、あとはテスト通知をするだけです。
テスト通知
Firebase Cloud Messaging の「最初のキャンペーン」から、「Firebase Notification メッセージ」を選択。

適当にタイトルとテキストを設定し、「テストメッセージを送信」ボタンで表示されるダイアログから、アプリケーション起動時に表示される FCM Token を入力し、「テスト」をクリックすると、数秒後にプッシュ通知がやってきます。

サーバー通知 (おまけ)
おまけとして、 Node.js サーバーから通知する方法も紹介します。 まず、 Firebase のプロジェクトの設定からサービスアカウントタブへ移動し、 Firebase Admin SDK の秘密鍵を発行し、ダウンロードします。 ファイルをそのまま置くのはちょっと気まずいので、ダウンロードした秘密鍵の JSON の中から、次の値を環境変数として設定します:
project_id→FIREBASE_PROJECT_IDclient_email→FIREBASE_CLIENT_EMAILprivate_key→FIREBASE_PRIVATE_KEY
設定したら、次のようなコードで Firebase Admin SDK を初期化します:
// @lib/firebase.ts import * as admin from "firebase-admin"; admin.initializeApp({ credential: admin.credential.cert({ projectId: process.env.FIREBASE_PROJECT_ID, clientEmail: process.env.FIREBASE_CLIENT_EMAIL, privateKey: process.env.FIREBASE_PRIVATE_KEY?.replace(/\\n/g, "\n"), }), }); export { admin as firebase };
あとは、サーバーから通知を送りたい任意のタイミングで、次のようなコードでプッシュ通知を送信できます:
// @services/notifications.ts import { firebase } from "@/lib/firebase"; // ...略... const sub = { token: "サーバーに保存した FCM Token", }; await firebase.messaging().send( { token: sub.token, notification: { title: "タイトル", body: "本文", }, data: { key: "こうやって、任意の値も付与できるよ", }, }, false );
data プロパティに付与した追加データは、次のようなコードで受け取ることが出来ます:
// @AppDelegate+UserNotificationCenter.swift extension AppDelegate: UNUserNotificationCenterDelegate { func userNotificationCenter( _ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping ( UNNotificationPresentationOptions ) -> Void ) { let userInfo = notification.request.content.userInfo let key = userInfo["key"] as? String ?? "" // → こうやって、任意の値も付与できるよ } }
この記事ははてなエンジニア Advent Calendar 2025 もう何日目かわからない日の記事でした。がんばった (2)。
明日だったああああああああ!!!!すみません