调整FirebaseApp.configure()位置触发SIGABRT,需优先注册远程通知
为什么原来在init()里调用FirebaseApp.configure()?
原来的代码在类的override init()中调用FirebaseApp.configure(),大概率是因为项目中存在依赖Firebase的组件(比如FIRAuth)在application:didFinishLaunchingWithOptions:之前就已经初始化了。举个例子,如果某个单例类的属性直接声明了let auth = Auth.auth(),这个属性会在该单例被创建时(可能早于didFinishLaunching)初始化,而Auth.auth()要求默认的FIRApp已经配置完成,所以必须提前在init()中完成Firebase配置,避免崩溃。
移到didFinishLaunching开头后崩溃的原因
你遇到的错误信息已经说得很明确:
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'The default FIRApp instance must be configured before the default FIRAuthinstance can be initialized. One way to ensure that is to call
[FIRApp configure];is called inapplication:didFinishLaunchingWithOptions:.'
这说明你的项目里有代码在FirebaseApp.configure()之前就尝试初始化了FIRAuth实例。当你把FirebaseApp.configure()移到didFinishLaunching开头时,这个配置操作的时机还是晚于某个FIRAuth的初始化操作(比如某个单例的属性、全局变量等),所以触发了崩溃。
满足「先注册远程通知,再配置Firebase」的解决方案
要同时满足两个要求——registerForRemoteNotifications()在FirebaseApp.configure()之前执行,且FirebaseApp.configure()在所有依赖它的组件(比如FIRAuth)之前执行,你可以按照以下步骤调整代码:
1. 先处理远程通知注册(在Firebase配置之前)
修改application:didFinishLaunchingWithOptions:方法,先完成远程通知的注册逻辑,再调用Firebase配置:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { // 第一步:先处理远程通知注册 if #available(iOS 10, *) { UNUserNotificationCenter.current().delegate = self as? UNUserNotificationCenterDelegate Messaging.messaging().delegate = self // 先请求通知权限,完成后注册远程通知 UNUserNotificationCenter.current().requestAuthorization(options: [.badge, .sound, .alert]) { [weak self] granted, error in guard let self = self else { return } DispatchQueue.main.async { application.registerForRemoteNotifications() } } // 即使权限还没请求完成,也可以先注册(iOS会在授权通过后返回token) application.registerForRemoteNotifications() } else { let notificationSettings = UIUserNotificationSettings(types: [.badge, .alert, .sound], categories: nil) UIApplication.shared.registerUserNotificationSettings(notificationSettings) UIApplication.shared.registerForRemoteNotifications() } // 第二步:配置Firebase(现在在远程通知注册之后) FirebaseApp.configure() // 第三步:处理其他初始化逻辑(依赖Firebase的代码必须在这之后) GMSPlacesClient.provideAPIKey("AIzaSyCWMU53OSR4zO28i9e2BsASnda3X1TAS2Y") GMSServices.provideAPIKey("AIzaSyCWMU53OSR4zO28i9e2BsASnda3X1TAS2Y") UIApplication.shared.setStatusBarStyle(UIStatusBarStyle.lightContent, animated: true) UITabBarItem.appearance().setTitleTextAttributes([NSAttributedStringKey.foregroundColor:UIColor.clear], for: .normal) UITabBarItem.appearance().setTitleTextAttributes([NSAttributedStringKey.foregroundColor:UIColor.clear], for: .selected) // 注意:Messaging.messaging().fcmToken要在Firebase配置之后获取,否则会是nil DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { print("Token is ", Messaging.messaging().fcmToken) } return true }
2. 修复FIRAuth初始化过早的问题
检查项目中所有使用Auth.auth()的地方:
- 如果是类属性/全局变量直接声明
let auth = Auth.auth(),改成懒加载:lazy var auth: Auth = { return Auth.auth() }() - 如果是单例类在初始化时就创建了FIRAuth实例,把这部分逻辑延迟到
FirebaseApp.configure()之后执行,比如在didFinishLaunching中调用单例的初始化方法,而不是让单例自动提前初始化。
3. 移除原来init()中的Firebase配置
把override init()里的FirebaseApp.configure()删掉,避免重复配置:
override init() { super.init() // 移除 FirebaseApp.configure() }
额外注意点
Messaging.messaging().fcmToken必须在FirebaseApp.configure()之后才能获取到有效值,所以要调整打印时机,不要在Firebase配置之前打印。- iOS10+的通知授权是异步操作,所以
registerForRemoteNotifications()可以在授权请求发起后立即调用,iOS会在用户授权通过后自动返回设备token。
内容的提问来源于stack exchange,提问作者bolt

