📖

iOS開発でどんだけやっても通知トークンが取得できなかったデスマッチの行方

に公開

こんにちはT-KAWAです。

iOSアプリ開発をしている皆さん、プッシュ通知ってアプリの命運を握る機能の一つですよね。その通知を実装する上で、最初の関門が通知トークンの取得です。

今回、私はこの通知トークン取得という、本来サクッと終わるはずのタスクで、地獄を見ました。
というか、うまく取得できていたのにある時から全く取れなくなりました

ありとあらゆる手を尽くしても、didRegisterForRemoteNotificationsWithDeviceTokenが呼ばれない...!
この記事は、そんな七転八倒の記録と、最終的に私を救ったたった一つの設定について書き記すものです。


1. 😇 AIが示す「通知トークン取得の王道」

色々調べましたが大体はAIに頼りきりだったので、AIに「iOS開発で通知トークンを取得するために必要な手順をすべて教えて!」と尋ねました。
AIはまるで卒業論文の如く、完璧な手順を提示してくれました。


iOS開発で通知トークンを取得するために必要な手順(AI Ver.)

1. Capabilityの設定

  1. Xcodeを開き、ターゲットのSigning & Capabilitiesタブへ移動します。
  2. + Capabilityをクリックし、Push Notificationsを追加します。

2. APNs証明書の準備

  1. Apple Developer Accountで、IdentifiersからアプリのBundle IDを選択します。
  2. Push Notificationsが有効になっていることを確認し、必要であればCertificates, Identifiers & ProfilesからAPNs関連の証明書(開発用/配布用)を作成・ダウンロードし、キーチェーンに登録します。
    • *ただし、現在は**Authentication Key (P8)*の使用が推奨されています。

3. プッシュ通知の利用許可をユーザーに求める

AppDelegate (またはSceneDelegate) で、アプリ起動時などにプッシュ通知の利用許可をユーザーに求めます。

// AppDelegate.swift (例)
import UserNotifications

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    // ...
    UNUserNotificationCenter.current().delegate = self
    UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) { granted, error in
        print("通知許可: \(granted)")
        guard granted else { return }
        
        // 許可が得られたら、トークン取得プロセスを開始
        DispatchQueue.main.async {
            application.registerForRemoteNotifications() // 👈 これが重要
        }
    }
    return true
}

extension AppDelegate: UNUserNotificationCenterDelegate {
    // フォアグラウンドでの通知表示制御など...
}

4. 通知トークンを取得するデリゲートメソッドの実装

application.registerForRemoteNotifications()が成功すると、以下のデリゲートメソッドが呼ばれ、deviceToken(通知トークン)を取得できます。

// AppDelegate.swift (続き)

// ✅ 成功時: 通知トークンを取得!
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
    let token = deviceToken.map { String(format: "%02.2hhx", $0) }.joined()
    print("🚀 通知トークン取得成功: \(token)")
    // ここでトークンをサーバーやFirebaseなどに送信する
}

// ❌ 失敗時: エラー処理
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
    print("🚨 通知トークン取得失敗: \(error.localizedDescription)")
}

この記事を読んでいる人のほとんどはここまでやったら通知トークン取得できてるでしょうね😇

2. 🤯 AIの手順を試しても、トークンは来ない...

私は上記のAIの手順をすべて試しました。

  • Capability? \rightarrow OK! 🤝
  • 証明書/Key? \rightarrow OK! (しかも何度も作り直した) 🔐
  • requestAuthorization? \rightarrow OK! (許可ダイアログは出る) ✅
  • registerForRemoteNotifications()? \rightarrow OK! (もちろん非同期で) ✨

しかし、何をやっても**application(_:didRegisterForRemoteNotificationsWithDeviceToken:)呼ばれないのです。
代わりに
application(_:didFailToRegisterForRemoteNotificationsWithError:)が呼ばれるわけでもない。まるで、トークン取得のプロセス自体が無視されている**かのように...

  • Product > Clean Build Folder
  • シミュレータではなく実機でテスト
  • 実機を再起動
  • プロビジョニングプロファイルの再確認
  • application.isRegisteredForRemoteNotificationsの確認

もはやオカルト的な対処法まで試しましたが、私のコンソールには、通知トークンを吐き出す快感のログは一切現れませんでした。

ファーーーーーーーーーーーーーーーーーーーーー


3. 💡 犯人はFirebase、解決策はInfo.plistの片隅に

数時間、いや体感的には数日...絶望の淵をさまよった時、私は一本の救いの光を見つけました。

そう、それは私が使っている**Firebase Cloud Messaging (FCM)**のドキュメントにひっそりと書かれていた設定でした。

🔥 犯人:Firebaseのメソッドスウィズリング
🔥 解決策:FirebaseAppDelegateProxyEnabledNO にする

この設定一つで、嘘のように**didRegisterForRemoteNotificationsWithDeviceToken**が呼ばれ、歓喜の通知トークンが手に入ったのです!

最終解決手順

  1. **Info.plist**を開きます。
  2. 新しいキーとして FirebaseAppDelegateProxyEnabled を追加します。
  3. そのTypeBoolean に設定し、ValueNO (No) に設定します。

この設定がなぜ必要かというと、Firebase SDKはデフォルトでメソッドスウィズリングという技術を使い、通知関連のデリゲートメソッド(例:didRegisterForRemoteNotificationsWithDeviceToken)を自動的にフック(乗っ取り)して、FCM独自のトークン取得とAPNsトークン(Appleの通知トークン)の関連付けを裏側でやってくれます。

しかし、何らかの理由でこの自動処理がうまく働かない場合や、APNsトークンを手動で取得したい場合に、このプロキシ機能を無効化して、純粋にAppleのデリゲート(上記2章のコード)が呼ばれるようにする必要があります。

📘 参考記事:Firebase Cloud Messaging for iOS - Method Swizzling


4. 🥳 まとめ:泥沼を抜けて、トークンは手元に

結局、通知トークンが取得できなかった原因は、私が実装を間違えていたわけでも、Appleの証明書がおかしかったわけでもなく、Firebaseが良かれと思ってやってくれていた自動処理私の手動実装の相性が悪かった、というオチでした。

もしあなたが、iOS開発で以下の症状に陥ったら、思い出してください!

  • didRegisterForRemoteNotificationsWithDeviceToken一切呼ばれない
  • エラーも出ず、ただ沈黙する
  • Firebaseを導入している。

\rightarrow Info.plist で FirebaseAppDelegateProxyEnabled\text{NO} に!!

この記事もほぼAIが書いてくれました。ありがたいですね。

Discussion