通知の登録を忘れて、誰にも届かなかった話〜Push通知は届いて当然だと思ってた〜

※ChatGPTを使用して記事を作成しています。

「FCMで通知送ってるのに、なんで誰も受け取ってないの?」

そんな問いが、ある朝Slackに飛び込んできた。

通知は「送れば届く」ものだと信じていたあの頃の自分に、「レシーバーの登録は?チャネルの作成は?受信トークンは更新してる?」と問い詰めたい。

今回は、通知を“出しているのに届かない”という一見不可解な現象の裏で、最も基本的な登録処理を忘れていた私の恥ずかしい失敗談を紹介します。

発端:キャンペーン通知を出したのに、反応ゼロ

これは、あるショッピングアプリでの話。

夏のキャンペーン告知として、Firebase Cloud Messaging(FCM)で通知を配信することになりました。

初回キャンペーンということもあり、通知の配信対象は全ユーザー。

私は通知送信のバッチ処理とサーバー連携部分を担当し、FCMの送信コードを整備していました。

そしてリリース初日。

サーバーから送信処理を実行。

「通知、飛ばしました!」

と報告したのに──

「え、誰も届いてないですけど?」

という冷たい声が返ってきたのです。

Push通知が「届かない」原因を探る

最初に疑ったのは、サーバー側の処理

FCMの送信APIは200を返していたし、送信ログにも特に異常は見られなかった。

つぎに疑ったのは、ユーザーの受信トークン(device token)

もしかして、古いトークンで送ってしまっているのでは……?

トークン更新処理は次のようにしていた:

FirebaseMessaging.getInstance().token.addOnSuccessListener { token ->
    Log.d("FCMToken", "Token: $token")
    repository.sendTokenToServer(token)
}

ログを見ると、トークンは正しく生成・送信されている

つまり、トークンは有効で、通知はサーバーから送信されている。

でも、誰も受け取っていない

原因判明:BroadcastReceiverを登録していなかった!

最終的にたどり着いた結論は、アプリ内で通知を受け取る準備ができていなかったということ。

以下のコードを見てください:

class MyFirebaseMessagingService : FirebaseMessagingService() {
    override fun onMessageReceived(remoteMessage: RemoteMessage) {
        Log.d("FCM", "Message received: ${remoteMessage.data}")
        // 通知を作成・表示する処理
    }
}

一見するとこれで受信できそうに見えますが、AndroidManifestにこのServiceの登録がなかったのです!

🙀 AndroidManifest.xml に必要だった記述:

<service
    android:name=".MyFirebaseMessagingService"
    android:exported="false">
    <intent-filter>
        <action android:name="com.google.firebase.MESSAGING_EVENT" />
    </intent-filter>
</service>

この1行の登録ミス(というか完全な忘れ)が、アプリが通知を“受け取れない状況”になっていた最大の原因でした。

さらに…通知チャネルの作成忘れで表示されない!?

ようやく受信はできるようになったものの、次は「通知が表示されない」という問題が発生。

原因は、通知チャネルの未登録

Android 8.0(API 26)以降では、NotificationChannelの作成が必須になります。

これを忘れると、通知を発行しても何も表示されません。

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    val channel = NotificationChannel(
        "default_channel",
        "Default Channel",
        NotificationManager.IMPORTANCE_DEFAULT
    )
    val manager = getSystemService(NotificationManager::class.java)
    manager.createNotificationChannel(channel)
}

このコードを入れて初めて、通知は「ユーザーに見える」形になります。

落とし穴:カスタム通知 vs 通知ペイロード

FCMには2種類の通知方式があります:

種類内容特徴
通知ペイロード(notification)Googleが自動で通知を表示バックグラウンドでも届く
データペイロード(data)アプリ内で制御・カスタマイズ可能フォアグラウンドでの表示制御が必要

私たちの通知は完全にカスタム制御の「データ通知」だったため、アプリ内でonMessageReceived()が呼ばれなければ表示されない仕様

しかも、onMessageReceived()はアプリが完全に停止していると呼ばれないこともあるため、ユーザーがアプリを一度も起動していなければ通知が届かないケースもあるということを、当時は理解していませんでした。

こうすればよかった:通知の「見える化」とチェックリスト

今回の事件を経て、通知機能を実装するときには必ずチェックするポイントを定めました。

✅ 通知実装チェックリスト

  • ✅ FirebaseMessagingService を継承したクラスを作成したか?
  • ✅ AndroidManifest.xml に <service> を登録したか?
  • ✅ 通知チャネルを NotificationManager に登録したか?
  • ✅ 通知の id が重複していないか?
  • ✅ ユーザーが通知をOFFにしていないか?
  • ✅ フォアグラウンド中の通知も表示するロジックがあるか?

また、開発段階で通知が正しく届いているか確認するために、通知ログをFirebase Analyticsと連携することも導入しました。

実際に入れた改善策

1. テスト通知を常設する「デバッグ通知画面」を作成

  • アプリ内に通知送信テスト用のUIを用意し、即時通知・遅延通知・カスタム通知を送信可能に。

2. 通知を受け取ったかのステータスをサーバーに送信

  • onMessageReceived で「受信済み」をAPIで送信。
  • ユーザーごとの通知配信結果をトラッキングできるように。

3. 通知ON/OFF状態をアプリ内でチェック可能に

  • ユーザーが通知を拒否している場合、設定画面で再許可を促すように変更。

まとめ:通知は「送る」だけでなく「受ける」設計が重要

Push通知は、サーバー側の送信処理が整っていれば完了、ではありません。

クライアント側で通知を受け取る準備・表示する設計ができて初めて意味を成すのです。

あのとき私は「送れば届く」と信じていました。

でも実際は「届けるための準備」が9割。

それを怠った私は、結果的に数万人のユーザーに沈黙だけを届けてしまったのです。

タイトルとURLをコピーしました