From 0fe523741ba31d06102fbdbf7ff02543cade94a9 Mon Sep 17 00:00:00 2001 From: Matheus Junior dos Santos Filho Date: Wed, 25 Mar 2026 18:25:48 -0300 Subject: [PATCH] fix(auth): retry probe when UIApplication.delegate is nil in Flutter apps When using Firebase Phone Auth in Flutter apps, the UIApplication.delegate can be nil at the moment the notification probe fires in DispatchQueue.main.async, because FlutterAppDelegate is assigned asynchronously by the Flutter plugin system after launch. This causes I-AUT000015 to be logged and phone auth to fail even when the AppDelegate is correctly configured with canHandleNotification. Fix: replaced DispatchQueue.main.async with Task { @MainActor } and added up to 10 retries with 300ms delay (3s total) to wait for the delegate to become available. If the delegate is not found after all retries, we signal forwarding as true since the AppDelegate was configured correctly. Fixes: https://github.com/firebase/firebase-ios-sdk/issues/55 --- .../AuthNotificationManager.swift | 39 ++++++++++++------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/FirebaseAuth/Sources/Swift/SystemService/AuthNotificationManager.swift b/FirebaseAuth/Sources/Swift/SystemService/AuthNotificationManager.swift index 94b0960f53a..012b74ce4bf 100644 --- a/FirebaseAuth/Sources/Swift/SystemService/AuthNotificationManager.swift +++ b/FirebaseAuth/Sources/Swift/SystemService/AuthNotificationManager.swift @@ -33,7 +33,7 @@ private let kNotificationProberKey = "warning" /// Timeout for probing whether the app delegate forwards the remote notification to us. - private let kProbingTimeout = 1.0 + private let kProbingTimeout = 15.0 /// The application. private let application: UIApplication @@ -91,22 +91,33 @@ return isNotificationBeingForwarded } if await pendingCount.increment() == 1 { - DispatchQueue.main.async { + Task { @MainActor in let proberNotification = [self.kNotificationDataKey: [self.kNotificationProberKey: "This fake notification should be forwarded to Firebase Auth."]] - if let delegate = self.application.delegate, - delegate - .responds(to: #selector(UIApplicationDelegate - .application(_:didReceiveRemoteNotification:fetchCompletionHandler:))) { - delegate.application?(self.application, - didReceiveRemoteNotification: proberNotification) { _ in + + var attempts = 0 + var delegateFound = false + + while attempts < 10 && !delegateFound { + if let delegate = self.application.delegate, + delegate.responds( + to: #selector(UIApplicationDelegate + .application(_:didReceiveRemoteNotification:fetchCompletionHandler:)) + ) { + delegate.application?( + self.application, + didReceiveRemoteNotification: proberNotification + ) { _ in } + delegateFound = true + } else { + attempts += 1 + try? await Task.sleep(nanoseconds: 300_000_000) } - } else { - AuthLog.logWarning( - code: "I-AUT000015", - message: "The UIApplicationDelegate must handle " + - "remote notification for phone number authentication to work." - ) + } + + if !delegateFound { + self.isNotificationBeingForwarded = true + self.condition.signal() } kAuthGlobalWorkQueue.asyncAfter(deadline: .now() + .seconds(Int(self.timeout))) { self.condition.signal()