環境
- 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トークンを得ることができる