在 StateObject 初始化之前应用程序委托中的 Firebase 初始化

Posted

技术标签:

【中文标题】在 StateObject 初始化之前应用程序委托中的 Firebase 初始化【英文标题】:Firebase initialisation in application delegate before StateObject initialisation 【发布时间】:2021-01-04 15:51:37 【问题描述】:

我正在用 SwiftUI 编写一个应用程序并使用 Firebase 作为我的后端。

我有一个可用的首选项面板,它由应用程序本身内的一个模式组成,但我想利用 macOS 的一致首选项窗口,其中包括菜单选项以及this 文章中所写的快捷方式。

这是我的应用声明和委托。

@main
struct SchedulerApp: App 
    #if os(macOS)
    @NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
    #else
    @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
    #endif
    
    @StateObject var authState = AuthState()
    @StateObject var model = Model()
    
    var body: some Scene 
        WindowGroup 
            ContentView()
                .frame(minWidth: 800, minHeight: 450)
                .environmentObject(model)
                .environmentObject(authState)
            
        
        .commands 
            SidebarCommands()
        
        
        #if os(macOS)
        Settings 
            Preferences()
        
        #endif
    


#if os(macOS)
class AppDelegate: NSObject, NSApplicationDelegate 
    func applicationWillFinishLaunching(_ notification: Notification) 
        FirebaseApp.configure()
    

#else
class AppDelegate: NSObject, UIApplicationDelegate 
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool 
        FirebaseApp.configure()
        
        
        
        return true
    

#endif

您可以看到,我有一个委托和一个状态对象,一个对象监听身份验证状态,另一个对象保存用户数据和设置:

class AuthState: ObservableObject 
    var handle: AuthStateDidChangeListenerHandle?
    
    @Published var signedIn: Bool? = nil
    
    init() 
        listen()
    
    
    func listen() 
        Auth.auth().addStateDidChangeListener  [self] (auth, user) in
            switch user == nil 
            case true: signedIn = false
            case false: signedIn = true
            
        
    

但是,当我运行此代码时,出现以下错误:

尚未配置默认 Firebase 应用。将[FIRApp configure];(Swift 中的FirebaseApp.configure())添加到您的应用程序初始化中。阅读更多:(...)

必须先配置默认 FIRApp 实例,然后才能初始化默认 FIRAuthinstance。确保这一点的一种方法是在 App Delegate 的 application:didFinishLaunchingWithOptions:(Swift 中为 application(_:didFinishLaunchingWithOptions:))中调用 [FIRApp configure];(Swift 中为 FirebaseApp.configure())。

我需要应用程序在初始化步骤有一个单一的事实来源 - 但由于某种原因 applicationWillFinishLaunching 在 StateObject 调用之后调用。

我的应用目前在 ContentView 中声明了这些 StateObject 类,这可以避免这些错误,但正如我上面提到的,我需要偏好来共享单一的事实来源。

非常感谢任何帮助!

【问题讨论】:

【参考方案1】:

在 SchedulerApp 中:应用只需添加一个 init 来初始化 Firebase。您将不再需要 AppDelegate 类(除非您稍后要在其中做其他事情)。

 init()  
    FirebaseApp.configure()
 

【讨论】:

【参考方案2】:

如Where to configure Firebase in my ios app in the new SwiftUI App life cycle without AppDelegate and SceneDelegate? 中所述,您可以使用应用的初始化程序 (init()) 或 AppDelegate 来初始化 Firebase。

使用初始化程序适用于相当多的 Firebase 的 SDK,但也有一些使用方法调配,他们希望出现 AppDelegate

在The Ultimate Guide to the SwiftUI 2 Application Life Cycle - SwiftUI 2,我深入探讨了新应用生命周期的一些细节。

让我们看看我为那篇文章构建的sample app 的代码,以及生成的输出:

class AppDelegate: NSObject, UIApplicationDelegate 
  func application(_ application: UIApplication,
                   didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool 
    print("Colors application is starting up. ApplicationDelegate didFinishLaunchingWithOptions.")
    return true
  



@main
struct ColorsApp: App 
  @UIApplicationDelegateAdaptor(AppDelegate.self) var delegate
  
  @Environment(\.scenePhase) var scenePhase
  
  init() 
    print("Colors application is starting up. App initialiser.")
  
  
  var body: some Scene 
    WindowGroup 
      ContentView()
    
    .onChange(of: scenePhase)  newScenePhase in
      switch newScenePhase 
      case .active:
        print("App is active")
      case .inactive:
        print("App is inactive")
      case .background:
        print("App is in background")
      @unknown default:
        print("Oh - interesting: I received an unexpected new value.")
      
    
  

调试控制台输出:

Colors application is starting up. App initialiser.
Colors application is starting up. ApplicationDelegate didFinishLaunchingWithOptions.
App is active

所以应用初始化如下:

    应用初始化程序被调用 AppDelegatedidFinishLaunchingWithOptions 被调用 scenePhase 更改为 .active

对于您的应用,我建议如下:

    在应用的初始化程序中初始化 Firebase 在您应用的初始化程序中,初始化您的模型和其他事实来源,并将它们连接到相应的 Firebase 服务

另一种选择是:

    在应用的初始化程序中初始化 Firebase 通过提供默认值(如您所做的那样)来初始化您的模型和事实来源,但暂时不要连接到 Firebase 实现 ScenePhase 处理程序,并且 - 一旦应用进入 .active 状态,“激活”您的模型并告诉它们连接到 Firebase。

第一个选项听起来更清晰,更容易实现。对我来说:-)

【讨论】:

以上是关于在 StateObject 初始化之前应用程序委托中的 Firebase 初始化的主要内容,如果未能解决你的问题,请参考以下文章

SwiftUI:如何在父视图中初始化新的 StateObject?

如何防止 SwiftUI 像使用 @StateObject 一样重新初始化我的包装属性?

如何以 stateobject 作为参数初始化视图?

SwiftUI onChange(of: ...) 更新 StateObject

在init之前设置CustomView委托

如何使用@StateObject 修改布尔值?