HTTPv1による認証とFirebaseでiOSアプリのプッシュ通知を実装した話
はじめに
iOSアプリでFirebase Cloud Messaging(FCM)を利用したプッシュ通知を実装する際に、HTTPv1 APIを使った認証とFirebase Cloud Functionsを導入した。本記事では、なぜこの2つを採用したのか、具体的な設定方法と実装のポイントを説明する。
HTTPv1 APIとCloud Functionsの採用理由
HTTPv1 APIとは?
Firebase Cloud Messaging(FCM)では、クライアントアプリ(iOSやAndroid)にプッシュ通知を送信するためのAPIが提供されている。このAPIには、かつてレガシーAPI(Legacy HTTP API)が使用されていたが、2024年7月22日をもって廃止され、現在はHTTPv1 APIが唯一の正式な方法となっている【公式ドキュメント参照】(Firebase公式ドキュメント)。
HTTPv1 APIの特長
-
IAMによる認証管理
- Google Cloud IAM(Identity and Access Management)を活用し、より細かいアクセス制御が可能。
-
通知の柔軟なカスタマイズ
- JSONペイロードの構造が拡張され、通知の内容や送信方法を細かく設定できる。
-
複数の受信者への一括送信
- トピックベースやデバイスグループを利用し、一括通知が可能。
Firebase Cloud Functionsとは?
Firebase Cloud Functionsは、Google Cloudのサーバーレス環境で実行されるコードをホスティングするサービスである。
Firebase上でイベントが発生した際に、そのイベントに応じた処理を自動的に実行することができる。
Cloud Functionsの特長
-
サーバー管理不要のサーバーレス環境
- 自前でサーバーを用意せずに、イベントに応じた処理を記述・実行できる。
-
Firestoreや認証との連携が容易
- Firestoreのデータ変更、ユーザーの認証状態変更などをトリガーにして、処理を自動化できる。
-
HTTPリクエストのエンドポイントを提供できる
- iOSアプリなどのクライアントが直接呼び出せるAPIを作成可能で、セキュアな通知送信を実装できる。
公式ドキュメントはこちら:
Firebase Cloud Functions
なぜCloud Functionsを採用したのか?
HTTPv1 APIを利用するには、クライアントアプリ(iOS)から直接Firebaseにリクエストを送るのではなく、サーバー経由で認証を行うのが推奨されている。そのため、Firebase Cloud Functionsを活用し、バックエンドで通知を処理する方式を採用した。
Cloud Functionsを利用するメリット
-
FCMの認証情報をクライアントアプリに持たせずに済む
- クライアントアプリから直接HTTPv1 APIを呼び出すと、Googleの認証トークンをアプリ内で管理する必要がある。しかし、これはセキュリティリスクとなる。
- Cloud Functionsを経由することで、サーバーサイドで認証情報を管理できる。
-
スケーラブルな通知処理が可能
- 複数のユーザーへの一括通知、特定のグループへの通知送信を簡単に処理できる。
-
Firestoreと連携しやすい
- Firestoreに保存されているユーザーのデバイス情報やトークンを基に、自動で通知を送信できる。
ディレクトリ構造と関係ファイル
プッシュ通知に関連するコードは、以下のようなディレクトリ構造で管理した。
📂 SharedShoppingList
├── 📂 Firebase
│ ├── NotificationManager.swift // 通知の設定と送信を担当
│ ├── SessionManager.swift // ユーザーのFCMトークンを管理
│ ├── AppDelegate.swift // Firebaseの初期化とAPNs登録
├── 📂 CloudFunctions
│ ├── index.js // Firebase Cloud Functionsで通知を送信
├── 📂 Views
│ ├── SettingsView.swift // 通知設定のUI
Firebase Cloud Messagingの設定手順
Firebaseプロジェクトの作成とCloud Messagingの設定
- Firebase Console にアクセスし、新規プロジェクトを作成する。
-
Cloud Messaging を有効化する。
- 「プロジェクト設定」>「Cloud Messaging」に移動し、「APNs認証キー」を追加。
APNsの設定とFCMの連携
iOSアプリでプッシュ通知を受信するために、**Apple Push Notification Service(APNs)**とFirebaseを統合する。
AppDelegate.swift
import Firebase
import FirebaseMessaging
import UserNotifications
class AppDelegate: NSObject, UIApplicationDelegate, MessagingDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool {
FirebaseApp.configure()
NotificationManager.shared.configure() // 🔥 通知設定
Messaging.messaging().delegate = self
return true
}
// 🔥 APNsトークンの取得
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
Messaging.messaging().apnsToken = deviceToken
}
}
このコードを AppDelegate.swift
に記述する。
Firebase Cloud Functionsで通知を送信する
Google Cloudの設定(IAMと認証)
- Firebase Consoleの「プロジェクト設定」→「サービスアカウント」を開く。
- 「新しい秘密鍵を生成」を選択し、JSONファイルをダウンロード。
- Google Cloud IAMで、「Firebase Cloud Messaging API」を有効化する。
Cloud Functionsの実装
index.js
const functions = require("firebase-functions");
const admin = require("firebase-admin");
const { google } = require("googleapis");
admin.initializeApp();
const PROJECT_ID = "プロジェクトID";
const SCOPES = ["https://www.googleapis.com/auth/firebase.messaging"];
async function getAccessToken() {
const auth = new google.auth.GoogleAuth({ scopes: SCOPES });
const client = await auth.getClient();
const tokens = await client.getAccessToken();
return tokens.token;
}
exports.sendPushNotification = functions.https.onRequest(async (req, res) => {
const { token, title, body } = req.body;
if (!token || !title || !body) return res.status(400).send("Missing fields");
try {
const accessToken = await getAccessToken();
const message = {
message: {
token: token,
notification: { title, body },
},
};
const response = await fetch(`https://fcm.googleapis.com/v1/projects/${PROJECT_ID}/messages:send`, {
method: "POST",
headers: {
"Authorization": `Bearer ${accessToken}`,
"Content-Type": "application/json",
},
body: JSON.stringify(message),
});
res.status(200).send(await response.json());
} catch (error) {
res.status(500).send("Error sending notification");
}
});
このコードを CloudFunctions/index.js
に記述する。