環境
- Xcode Version 12.3 (12C33)
- iOS 13 以上
- Firebase/Messaging (7.1.0)
初期実装
AppDelegate では以下のような初期化処理を行っていた
AppDelegate.swift
import FirebaseCore
import FirebaseMessaging
import UIKit
// MARK: - AppDelegate
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
self.setupFirebase(application: application)
let window = UIWindow()
self.window = window
return true
}
private func setupFirebase(application: UIApplication) {
FirebaseApp.configure()
Messaging.messaging().delegate = self
application.registerForRemoteNotifications()
}
FCMトークンが必要な場面で以下のように呼び出しを行っていた
import FirebaseMessaging
// 中略
let fcmToken = Messaging.messaging().fcmToken
問題発覚
- アプリをアンインストール後再インストールするとリモート通知が届かなくなった
- サーバーのログを確認すると
"error":"No information found about this instance id."とエラーが出ていた
原因
- 再インストール後
Messaging.messaging().fcmTokenを利用するとアンインストール前の FCM トークンが返却されていた
対応
Messaging.messaging().token(completion:)を一度呼び出すことでcompletion呼び出し後には有効なトークンが取得できるようになる-
setupFirebase(application:)を以下の通り修正
AppDelegate.swift
private func setupFirebase(application: UIApplication) {
FirebaseApp.configure()
Messaging.messaging().delegate = self
// iOSは起動時に意図的にFCMトークンを取得する処理を追加することで「アプリが再インストール」された場合も対応できる
Messaging.messaging().token { (_, error: Error?) in
guard error == nil else { return }
application.registerForRemoteNotifications()
}
}
検証
検証1: アプリ再インストール時の挙動
MessagingDelegate の func messaging(_ messaging:didReceiveRegistrationToken fcmToken:) を以下の通り実装
AppDelegate.swift
func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) {
print("*** fcmToken: \(fcmToken ?? "")")
print("*** fcmToken: \(Messaging.messaging().fcmToken ?? "")")
}
-
didReceiveRegistrationTokenのfcmTokenとMessaging.messaging().fcmTokenが一致することは確認している
- 通知が届く状態のアプリをデバッグ実行し
didReceiveRegistrationTokenデリゲートメソッドに届いたFCMトークンを確認 - FCMトークンが有効か curl コマンドで確認
- デバッグ実行を中止しアプリをアンインストール
- アプリをデバッグ実行(再インストール)し
didReceiveRegistrationTokenデリゲートメソッドに届いたFCMトークンを確認 - FCMトークンが有効か確認
- 1分程度放置し
didReceiveRegistrationTokenデリゲートメソッドが再び発火するか確認 - PUSH通知が届くか確認
- アプリを再起動(デバッグ実行)
-
didReceiveRegistrationTokenデリゲートメソッドに届いたFCMトークンを確認 - FCMトークンが有効か確認
- 1分程度放置し
didReceiveRegistrationTokenデリゲートメソッドが再び発火するか確認 - PUSH通知が届くか確認
結果
- トークンAを得る
- トークンAは curl 有効であることがわかる
- 実行可
- トークンAを得る
- トークンAは curl 無効であることがわかる(
"error":"No information found about this instance id.") - 発火しない(トークンAは更新されない)
- 届かない
- 実行可
- トークンBを得る
- トークンBは curl コマンドより有効であることがわかる
- 発火しない(トークンBは更新されない)
- 届く
検証2: トークン取得メソッドを変更しエラーハンドリングを試みる
MessagingDelegate の func messaging(_ messaging:didReceiveRegistrationToken fcmToken:) を以下の通り実装
AppDelegate.swift
func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) {
print("*** fcmToken: \(fcmToken ?? "")")
print("*** fcmToken: \(Messaging.messaging().fcmToken ?? "")")
Messaging.messaging().token { (token: String?, error: Error?) in
print("*** Messaging.messaging().token")
print("*** fcmToken: \(token ?? "")")
print("*** fcmTokenError: \(error?.localizedDescription ?? "nothing")")
}
}
以下の手順を試みる
- 通知が届く状態のアプリをデバッグ実行し
didReceiveRegistrationTokenログを確認 - FCMトークンが有効か curl コマンドで確認する
- デバッグ実行を中止しアプリをアンインストール
- アプリを再インストール(デバッグ実行)し
didReceiveRegistrationToken内部のログを確認
結果
- 以下の通りトークンAを得る
*** fcmToken: [トークンA]
*** fcmToken: [トークンA]
*** Messaging.messaging().token
*** fcmToken: [トークンA]
*** fcmTokenError: nothing
- トークンA はcurl コマンドより有効であることがわかる
- 実行可能
- 以下のログを得る
*** fcmToken: [トークンA]
*** fcmToken: [トークンA]
*** fcmToken: [トークンB]
*** fcmToken: [トークンB]
*** Messaging.messaging().token
*** fcmToken: [トークンB]
*** fcmTokenError: nothing
*** Messaging.messaging().token
*** fcmToken: [トークンB]
*** fcmTokenError: nothing
curl コマンドによる追加検証で以下が判明している
- 上記ログ2行目時点ではトークンAは有効
- 上記ログ3行目時点ではトークンAは無効
- トークンBは有効
検証結果まとめ
- アプリを再インストールした際に
didReceiveRegistrationTokenでアンインストール前に利用していたトークンが返却される -
Messaging.messaging().fcmTokenではdidReveiveRegistrationTokenで得られるトークンと同じものを取得する -
didReceiveRegistrationToken発火後何らかの原因でアンインストール前に利用していたトークンが無効化される - 無効化された後アプリを再起動することで正常に通信可能なトークンを
didReceiveRegistrationToken、Messaging.messaging().fcmTokenで利用可能となる -
Messaging.messaging().token(completion:)メソッドを用いることで再インストール後にも有効なFCMトークンを得ることができる - その際
Messaging.messaging().token(completion:)メソッドにはエラーが入ってこない -
Messaging.messaging().token(completion:)メソッドを呼び出すとdidReveiveRegistrationTokenが発火し、内部で有効なFCMトークンを得ることができる -
Messaging.messaging().token(completion:)メソッドを呼び出した後にはMessaging.messaging().fcmTokenプロパティで有効なFCMトークンを得ることができる