push-notification-best-practices について
iOS、Android、React Native、Expo、Flutterを扱うモバイルアプリ開発者向けのプッシュ通知ガイドです。プラットフォーム別の設定、トークン管理、メッセージ処理、ディープリンクを整理し、問題別に参照できるルールを備えています。
主な機能
- 015種類のモバイルプラットフォームに対応
- 02プラットフォーム別の設定チェックリスト
- 03トークン登録と更新の指針
- 04ペイロードとバックグラウンド処理の指針
- 05ディープリンクのトラブルシューティング
活用シーン
- 01モバイルアプリの通知設定
- 02通知が届かない・表示されない問題の調査
- 03バックグラウンドメッセージ処理の実装
- 04トークン管理やディープリンクの確認
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ clix-so/ skills --skill push-notification-best-practicesインストール後、Claude Code で/push-notification-best-practicesを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
AGENTS.mdLICENSE.txtmetadata.jsonrules/_sections.mdandroid-google-services.mdandroid-messaging-service.mdandroid-notification-channel.mdandroid-notification-icon.mdandroid-permission.mdandroid-priority-high.mddeeplink-navigation-conflict.mddeeplink-terminated-state.mdinfra-backup-fid.mdinfra-firewall-ports.mdinfra-rate-limiting.mdios-apns-auth-key.mdios-delegate-setup.mdios-extension-build.mdios-foreground-display.mdios-method-swizzling.mdios-permission-request.mdios-token-registration.mdmessage-background-handler.mdmessage-data-vs-notification.mdmessage-foreground-handler.mdpermission-timing.mdtesting-debugging.mdtoken-invalidation.mdtoken-refresh.mdtoken-registration.md詳細
プッシュ通知のベストプラクティス
概要
モバイルアプリケーションでプッシュ通知を実装し、トラブルシューティングするための包括的なガイドです。iOS(APNS)、Android(FCM)、React Native、Expo、Flutter の各プラットフォームを対象に、プラットフォーム固有の設定、トークン管理、メッセージ処理、ディープリンクのパターンについて説明します。
プラットフォームのサポート一覧
| プラットフォーム | オフラインプッシュ | 通知バー | タップ時のナビゲーション | アプリ内トースト | バックグラウンドハンドラー |
|---|---|---|---|---|---|
| iOS | APNS | システム | ディープリンク | カスタム | data-only payload |
| Android | FCM | システム | Intent | カスタム | data-only payload |
| React Native | APNS/FCM | システム | React Navigation | カスタム | setBackgroundMessageHandler |
| Expo | Expo Push | システム | Linking | カスタム | TaskManager |
| Flutter | APNS/FCM | システム | Navigator | カスタム | onBackgroundMessage |
判断フローチャート
権限をリクエストするタイミング
通知の権限はいつリクエストすべきですか?
User installing app
|
v
[First launch?] ──Yes──> [Show value proposition first]
| |
No v
| [User action triggers need?]
v |
[Already granted?] Yes
| |
Yes v
| [Request permission] ──> 70-80% acceptance rate
v
[Notifications ready]
| タイミング | 許可率 | 用途 |
|---|---|---|
| 即時(アプリ起動時) | 15-20% | エンゲージメントの低いアプリ |
| オンボーディング後 | 40-50% | 一般的なアプリ |
| ユーザーが操作を開始したとき | 70-80% | エンゲージメントの高いアプリ |
推奨: 利用価値を説明した後、または関連機能をユーザーが有効にしたときにリクエストしてください。
サイレント通知と表示通知
どのペイロードタイプを使用すべきですか?
[What's the purpose?]
|
+──> Time-sensitive user alert ──> Visible (notification payload)
|
+──> Background data sync ──> Silent (data-only payload)
|
+──> Custom UI required ──> Silent (data-only payload)
|
+──> Need background processing ──> Silent (data-only payload)
| シナリオ | ペイロードタイプ | 理由 |
|---|---|---|
| 新着メッセージの通知 | 表示 | ユーザーにすぐ確認してもらう必要があります |
| 注文状況の更新 | 表示 | 時間的制約のある情報です |
| バックグラウンド同期 | サイレント | ユーザーの操作を妨げる必要がありません |
| カスタム通知 UI | サイレント | 表示を完全に制御できます |
| バッジ数の更新 | サイレント | バックグラウンド処理が必要です |
拡張機能の選択(iOS)
Notification Service Extension を使う場合
- 表示前に通知内容を変更する必要がある
- メディア(画像、動画)をダウンロードして添付する必要がある
- エンドツーエンドで暗号化されたペイロードを復号する必要がある
- アクションボタンを動的に追加する必要がある
Notification Content Extension を使う場合
- システムテンプレートを超えるカスタム通知 UI が必要
- 展開時のビューにインタラクティブな要素が必要
アンチパターン(絶対に避けること)
権限の処理
| 絶対に避けること | 理由 | 代わりに |
|---|---|---|
| 理由を説明せずに初回起動時に権限をリクエストする | 許可率は15-20%です | まず利用価値を説明してからリクエストする |
| ユーザーが拒否した後に再度リクエストする | システムは繰り返しのリクエストを無視します | 設定画面への誘導を表示する |
| 暫定的な許可を無視する | iOS 12以降のサイレント配信を利用できません | .provisional オプションを使用する |
トークン管理
| 絶対に避けること | 理由 | 代わりに |
|---|---|---|
| トークンを長期間キャッシュする | 再インストールや復元時にトークンが変わることがあります | コールバックから取得した最新のトークンを必ず使用する |
| トークンの形式を決めつける | 形式はプラットフォームや SDK によって異なります | 不透明な文字列として扱う |
| ユーザーとの関連付けなしにトークンを送信する | 通知の送信先を指定できません | バックエンドで userId と関連付ける |
| デバイス ID なしでトークンを保存する | ユーザーごとにトークンが重複します | 一意のキーとして deviceId を使用する |
メッセージの処理
| 絶対に避けること | 理由 | 代わりに |
|---|---|---|
バックグラウンド処理に notification ペイロードを使用する | バックグラウンドでは onMessageReceived が呼び出されません | data-only payload を使用する |
| 時間厳守の配信をサイレント通知に頼る | 配信は保証されません | 表示通知を使用する |
| バックグラウンドハンドラーで重い処理を実行する | システムは約30秒後にアプリを終了します | 処理をキューに入れ、すばやく実行する |
| 両方のペイロードタイプを処理するのを忘れる | 通知を受け取れなくなります | notification と data の両方のペイロードを処理する |
iOS 固有
| 禁止事項 | 理由 | 代わりに |
|---|---|---|
| デリゲートの設定前に通知を登録する | デリゲートメソッドが呼び出されません | registerForRemoteNotifications() の前にデリゲートを設定します |
serviceExtensionTimeWillExpire() の実装を省略する | コンテンツの変更に失敗します | 必ずフォールバックを実装します |
| .p12 証明書を使用する | 毎年期限切れになり、非推奨です | .p8 認証キーを使用します |
Android 固有
| 禁止事項 | 理由 | 代わりに |
|---|---|---|
| NotificationChannel の作成を省略する | Android 8.0 以降では通知が表示されません | アプリの起動時にチャンネルを作成します |
バックグラウンドハンドラーで優先度 normal を使用する | Doze モードによって配信がブロックされます | 優先度 high を使用します |
| 色付きの通知アイコンを使用する | Android は色を無視し、白い四角形で表示します | 白色で透明背景のアイコンを使用します |
スキルの形式
各ルールファイルは、すばやい参照と深い理解に適したハイブリッド形式に従っています。
- クイックパターン:すぐにパターンを照合できる、誤ったコードと正しいコードのスニペット
- 適用する場面:このルールが関係する状況
- 詳細解説:手順ごとのガイドとプラットフォーム固有の詳細を含む、完全な背景情報
- よくある落とし穴:よくあるミスとその回避方法
- 関連ルール:関連するパターンへの相互参照
影響度:CRITICAL(すぐに修正)、HIGH(大幅な改善)、MEDIUM(有益な最適化)
適用する場面
次のような場合に、これらのガイドラインを参照してください。
- モバイルアプリでプッシュ通知を設定するとき
- プッシュ通知の配信問題をデバッグするとき
- バックグラウンド通知ハンドラーを実装するとき
- ディープリンクをプッシュ通知と統合するとき
- プッシュトークンとデバイス登録を管理するとき
- プラットフォーム固有のプッシュ通知の問題をトラブルシューティングするとき
- 信頼性を確保するためにプッシュ通知コードをレビューするとき
優先度順のガイドライン
| 優先度 | カテゴリー | 影響度 | プレフィックス |
|---|---|---|---|
| 1 | iOS の設定 | CRITICAL | ios- |
| 2 | Android の設定 | CRITICAL | android- |
| 3 | トークン管理 | HIGH | token- |
| 4 | メッセージ処理 | HIGH | message- |
| 5 | ディープリンク | MEDIUM | deeplink- |
| 6 | インフラストラクチャ | MEDIUM | infra- |
クイックリファレンス
重大:iOS の設定
設定チェックリスト:
- Apple Developer で APNs 認証キー(.p8)を生成します
- キー ID とチーム ID を指定して Firebase Console にアップロードします
- Xcode で Push Notifications 機能を有効にします
- didFinishLaunchingWithOptions で UNUserNotificationCenter デリゲートを設定します
- 登録前に通知の許可をリクエストします
- フォアグラウンド表示オプションを実装します
重大:Android の設定
設定チェックリスト:
- google-services.json を app/ ディレクトリに追加します
- google-services Gradle プラグインを適用します
- NotificationChannel を作成します(Android 8.0 以降)
- POST_NOTIFICATIONS 権限をリクエストします(Android 13 以降)
- FirebaseMessagingService を実装します
- 通知アイコンを設定します(透明な背景+白色)
- Doze モードとの互換性のために優先度 'high' を使用します
高:トークン管理
トークンのライフサイクル:
// 1. Register token with server
const token = await getToken();
await registerToken(token, userId);
// 2. Handle token refresh
onTokenRefresh((newToken) => {
updateTokenOnServer(newToken, userId);
});
// 3. Handle invalidation
if (error.code === 'DeviceNotRegistered') {
deleteTokenFromDatabase(token);
}
高:メッセージ処理
ペイロードの種類:
- データのみ:バックグラウンドハンドラーが両方のプラットフォームで実行されます
- 通知+データ:iOS と Android では動作が異なります
- iOS:常に通知を表示します
- Android:バックグラウンド=システムトレイ、フォアグラウンド=onMessageReceived
優先度の設定:
- 時間的制約のある通知には
priority: 'high'を使用します - Android の Doze モードでバックグラウンドハンドラーを実行するには必須です
ルール
コード例を含むドキュメント全文は rules/ にあります。
iOS のセットアップ(ios-*)
| ファイル | 影響度 | 説明 |
|---|---|---|
ios-apns-auth-key.md | CRITICAL | APNs 認証キーのセットアップ |
ios-delegate-setup.md | CRITICAL | UNUserNotificationCenter デリゲートの設定 |
ios-permission-request.md | CRITICAL | 通知権限のリクエストフロー |
ios-token-registration.md | HIGH | APNS トークンの登録と処理 |
ios-foreground-display.md | HIGH | フォアグラウンドでの通知表示 |
ios-method-swizzling.md | MEDIUM | Method Swizzling の管理 |
ios-extension-build.md | MEDIUM | Notification Service Extension のセットアップ |
Android のセットアップ(android-*)
| ファイル | 影響度 | 説明 |
|---|---|---|
android-notification-channel.md | CRITICAL | 通知チャンネルの作成(Android 8.0 以降) |
android-permission.md | CRITICAL | POST_NOTIFICATIONS ランタイム権限(Android 13 以降) |
android-google-services.md | CRITICAL | Firebase プロジェクトの設定 |
android-messaging-service.md | HIGH | FirebaseMessagingService の実装 |
android-notification-icon.md | MEDIUM | 通知アイコンのアセット要件 |
android-priority-high.md | HIGH | Doze モード用の優先度 high |
トークン管理(token-*)
| ファイル | 影響度 | 説明 |
|---|---|---|
token-registration.md | HIGH | バックエンドへのトークン登録 |
token-refresh.md | HIGH | トークンの更新処理 |
token-invalidation.md | MEDIUM | DeviceNotRegistered エラーの処理 |
メッセージ処理(message-*)
| ファイル | 影響度 | 説明 |
|---|---|---|
message-data-vs-notification.md | CRITICAL | data と notification ペイロードの違い |
message-background-handler.md | HIGH | バックグラウンドでのメッセージ処理 |
message-foreground-handler.md | HIGH | フォアグラウンドでの通知表示 |
ディープリンク(deeplink-*)
| ファイル | 影響度 | 説明 |
|---|---|---|
deeplink-navigation-conflict.md | MEDIUM | React Navigation との競合解決 |
deeplink-terminated-state.md | MEDIUM | アプリ終了時のディープリンク処理 |
インフラストラクチャ(infra-*)
| ファイル | 影響度 | 説明 |
|---|---|---|
infra-firewall-ports.md | MEDIUM | ネットワークファイアウォールの設定 |
infra-backup-fid.md | MEDIUM | Firebase Installation ID のバックアップ除外 |
infra-rate-limiting.md | MEDIUM | プッシュ通知のレート制限 |
ベストプラクティス
| ファイル | 影響度 | 説明 |
|---|---|---|
permission-timing.md | HIGH | 権限リクエストのタイミング最適化(承認率 70~80%) |
testing-debugging.md | HIGH | テストツール、デバッグ手法、ペイロードの検証 |
検索ルール
# Find rules by keyword
grep -l "apns" rules/
grep -l "fcm" rules/
grep -l "token" rules/
grep -l "background" rules/
grep -l "permission" rules/
grep -l "deeplink" rules/
問題 → ルールの対応表
| 問題 | まず確認する項目 |
|---|---|
| iOS でプッシュ通知を受信できない | ios-apns-auth-key → ios-delegate-setup |
| Android でプッシュ通知を受信できない | android-google-services → android-notification-channel |
| バックグラウンドでプッシュ通知が動作しない | android-priority-high → message-background-handler |
| フォアグラウンドでプッシュ通知が表示されない | ios-foreground-display または message-foreground-handler |
| トークンがない、または期限切れ | token-registration → token-refresh |
| ディープリンクの競合エラー | deeplink-navigation-conflict |
| 通知アイコンが壊れている(Android) | android-notification-icon |
| 社内ネットワークで失敗する | infra-firewall-ports |
| バックグラウンドハンドラーが呼び出されない(Android) | android-priority-high → message-data-vs-notification |
| userNotificationCenter が呼び出されない(iOS) | ios-delegate-setup |
| バックアップ復元後に 404 エラーが発生する | infra-backup-fid |
| 送信に失敗する(429 エラー) | infra-rate-limiting |
| 権限の承認率が低い | permission-timing → ios-permission-request |
| プッシュ通知のデバッグ方法、テスト方法 | testing-debugging |
| ローカルでテストできない | testing-debugging → ios-apns-auth-key |
プラットフォーム固有の注意事項
iOS(APNS)
- Apple Developer アカウントと APNs 認証キー(.p8)が必要です
- Development ビルドと Production ビルドでは動作が異なります
- 他の SDK の初期化より前にデリゲートを設定する必要があります
- メソッドスウィズリングがデリゲート呼び出しに干渉する場合があります
Android(FCM)
- Firebase Console から取得した google-services.json が必要です
- Android 8.0 以降では NotificationChannel が必要です
- Android 13 以降では実行時の権限が必要です
- Doze モードでは、バックグラウンドハンドラーに
priority: 'high'が必要です - 通知アイコンは単色(透明背景に白色)にする必要があります
React Native
- よくある問題:フォアグラウンド通知には
gcm.message_idが必要です - ディープリンクが React Navigation と競合します
- 終了状態ではネイティブから JS へのブリッジパターンが必要です
Expo
- ExpoPushToken の形式:
ExponentPushToken[...] - レート制限:プロジェクトあたり毎秒 600 件のプッシュ通知
- SDK 53 以降では Development ビルドが必要です
- APNs キーの管理には
eas credentialsを使用します
コンパイル済みドキュメント全文
すべてのルールを詳しく記載した完全版ガイド:AGENTS.md
クレジット
プッシュ通知のトラブルシューティングガイドと Firebase Cloud Messaging の公式ドキュメントに基づいています。