SwiftUI中FirebaseAuth适配iOS17 @Observable模式报错求助
问题重现
将原有基于ObservableObject/EnvironmentObject的FirebaseAuth代码迁移至iOS17新的@Observable模式时,出现以下运行时错误:
Thread 1: "The default FirebaseApp instance must be configured before the default Authinstance can be initialized. One way to ensure this is to call FirebaseApp.configure() in the App Delegate's application(_:didFinishLaunchingWithOptions:) (or the @main struct's initializer in SwiftUI)."
旧代码(正常运行):
class Authentication : ObservableObject { ... } class AppDelegate: NSObject, UIApplicationDelegate { func application(_: UIApplication, didFinishLaunchingWithOptions _: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool { FirebaseApp.configure() return true } } @main struct RApp: App { @UIApplicationDelegateAdaptor(AppDelegate.self) var delegate @StateObject var authentication = Authentication() var body: some Scene { WindowGroup { RootView() .environmentObject(authentication) .preferredColorScheme(.dark) } } }
新代码(报错):
@Observable class Authentication { ... } class AppDelegate: NSObject, UIApplicationDelegate { func application(_: UIApplication, didFinishLaunchingWithOptions _: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool { FirebaseApp.configure() return true } } @main struct RApp: App { @UIApplicationDelegateAdaptor(AppDelegate.self) var delegate @State private var authentication: Authentication = Authentication() var body: some Scene { WindowGroup { RootView() .environment(authentication) .preferredColorScheme(.dark) } } }
原因分析
旧代码中@StateObject的初始化是懒加载的,会在首次需要使用时才创建实例,此时AppDelegate已经完成FirebaseApp.configure()的调用。而新代码中使用@State直接初始化Authentication时,实例会在RApp结构体初始化时就被创建——这个时机早于AppDelegate的didFinishLaunchingWithOptions方法执行,导致Firebase还未配置就初始化了Auth相关逻辑,触发错误。
解决方案
方案1:将Firebase配置移至App结构体的初始化器
直接在@main的App结构体初始化时调用FirebaseApp.configure(),确保在Authentication实例化前完成配置:
@Observable class Authentication { ... } @main struct RApp: App { private var authentication: Authentication init() { FirebaseApp.configure() self.authentication = Authentication() } var body: some Scene { WindowGroup { RootView() .environment(authentication) .preferredColorScheme(.dark) } } }
注:此方案可以移除AppDelegate,简化代码结构。
方案2:用lazy延迟初始化Authentication
保持AppDelegate的配置逻辑,通过lazy让Authentication在首次使用时才初始化,此时Firebase已经完成配置:
@Observable class Authentication { ... } class AppDelegate: NSObject, UIApplicationDelegate { func application(_: UIApplication, didFinishLaunchingWithOptions _: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool { FirebaseApp.configure() return true } } @main struct RApp: App { @UIApplicationDelegateAdaptor(AppDelegate.self) var delegate @State private lazy var authentication = Authentication() var body: some Scene { WindowGroup { RootView() .environment(authentication) .preferredColorScheme(.dark) } } }
方案3:通过@State的初始化闭包延迟创建实例
利用@State的闭包初始化特性,确保Firebase配置完成后再创建Authentication:
@Observable class Authentication { ... } class AppDelegate: NSObject, UIApplicationDelegate { func application(_: UIApplication, didFinishLaunchingWithOptions _: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool { FirebaseApp.configure() return true } } @main struct RApp: App { @UIApplicationDelegateAdaptor(AppDelegate.self) var delegate @State private var authentication: Authentication = { return Authentication() }() var body: some Scene { WindowGroup { RootView() .environment(authentication) .preferredColorScheme(.dark) } } }
注:此方案依赖AppDelegate的执行顺序,若后续iOS版本调整启动流程可能存在风险,优先推荐方案1或2。
内容的提问来源于stack exchange,提问作者ArHero

