SwiftUI的View.environmentObject(_ :)作为此视图的祖先可能会丢失。:file

时间:2020-01-25 13:59:51

标签: ios swift firebase observable

我正在使用SwiftUI和Firebase在IOS上构建我的第一个应用程序,以进行身份​​验证和存储

对于登录,我使用默认的Firebase UI,它通过FUIAuthPickerViewController的子类MyFUIAuthPickerViewController进行了自定义,详细信息https://firebase.google.com/docs/auth/ios/firebaseui

defaultUI已初始化,并显示在场景委托文件中。

// Create the SwiftUI view that provides the window contents.
        //let contentView = ContentView()
        
        self.authUI = _createAuthUI()
        
        guard self.authUI != nil else {
            print("No authUI")
            return
        }
        
        self.authUI?.delegate = self
        
        self.authUI?.shouldHideCancelButton = true
        
        // Use a UIHostingController as window root view controller.
        if let windowScene = scene as? UIWindowScene {
            let window = UIWindow(windowScene: windowScene)
            //window.rootViewController = UIHostingController(rootView: contentView)
            window.rootViewController = UINavigationController(rootViewController: MyFUIAuthPickerViewController(authUI: authUI!))
            self.window = window
            window.makeKeyAndVisible()
        }

MyFUIAuthPickerViewController子类目前包含的内容不多,但将用于向授权屏幕添加默认背景

import Foundation
import FirebaseUI
import Firebase

class MyFUIAuthPickerViewController: FUIAuthPickerViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
    }
}

为了跟踪用户是否登录,我使用了一个称为Sessionstore的Observable对象。我改编自https://benmcmahen.com/authentication-with-swiftui-and-firebase/的代码,该代码使用的是旧版的可绑定协议

import Foundation
import SwiftUI
import Firebase
import Combine

class SessionStore : ObservableObject
{
    
    @Published var user: AppUser?
    var handle: AuthStateDidChangeListenerHandle?

    func listen () {
           // monitor authentication changes using firebase
           handle = Auth.auth().addStateDidChangeListener { (auth, user) in
               if let user = user {
                   // if we have a user, create a new user model
                   print("Got user: \(user) \(user.displayName!)")
                self.user = AppUser(uid: user.uid,displayName: user.displayName, email: user.email)
               } else {
                   // if we don't have a user, set our session to nil
                   self.user = nil
               }
           }
       }
    
   func signOut () -> Bool {
        do {
            try Auth.auth().signOut()
            print("signed out")
            self.user = nil
            print("user object set to nil")
            return true
        } catch {
            print("Problem encountered signing the user out")
            return false
        }
    }
}

环境对象出现在我的contentview和scenedelegate中

scenedelegate

class SceneDelegate: UIResponder, UIWindowSceneDelegate, FUIAuthDelegate{

    var window: UIWindow?
    var authUI: FUIAuth?
    @EnvironmentObject var appSession: SessionStore

contentview

import SwiftUI
import FirebaseUI
struct ContentView: View {

    @EnvironmentObject var session: SessionStore
    
        var body: some View {
            Group{
                if session.user != nil {
                    Text("Welcome \(session.user!.displayName!)")
                } else {
                    Text("Please login")
                }
            }
        }
}

struct ContentView_Previews: PreviewProvider {
    let nav = UINavigationController()
    static var previews: some View {
        ContentView().environmentObject(SessionStore())
    }
}

在我的sceneDelegate的扩展中,我实现了所需的firebase函数。 成功登录后,我将创建一个新的appuser对象,该对象将放置在sessionStore中,然后将rootviewcontroller更改为通过environmentObject传递的contentview

Extension SceneDelegate {

    func authUI(_ authUI: FUIAuth, didSignInWith user: User?, error: Error?) {
        
        guard user != nil else {
            print("No User")
            return
        }
        
        print(user!.displayName!)
        
        let user = AppUser(uid: user!.uid,displayName: user?.email,email: user?.displayName)
        
        self.appSession.user = user
        let contentView = ContentView()
        self.window?.rootViewController = UIHostingController(rootView: contentView.environmentObject(SessionStore()))
        self.window?.makeKeyAndVisible()
        
    }
        
        
    func authPickerViewController(for authUI: FUIAuth) -> FUIAuthPickerViewController {
        return MyFUIAuthPickerViewController(authUI: authUI)
    }
}

现在,当我测试我的应用程序时,输入用户名和密码后出现以下错误

严重错误:找不到SessionStore类型的ObservableObject。 作为此视图的祖先,可能缺少SessionStore的View.environmentObject(_ :) 。:文件/BuildRoot/Library/Caches/com.apple.xbs/Sources/Monoceros_Sim/Monoceros-39.4.3/Core/EnvironmentObject.swift ,第55行

我怀疑这与从我的SceneDelegate到ContentView的流之间的MyFUIAuthPickerViewController中的environmentObject丢失有关,但是我如何防止这种情况发生呢?我需要以某种方式扩展MyFUIAuthPickerViewController,以允许环境对象的传递,但是如何传递?

希望我的问题很清楚,你们可以帮忙。

1 个答案:

答案 0 :(得分:0)

您在SceneDelegate中的代码为let contentView = ContentView(),我认为它应该类似于let contentView = ContentView().environmentObject(SessionStore())

似乎您的SessionStore也缺少var didChange = PassthroughSubject<SessionStore, Never>()

SessionStore的第一行应类似于:

import Foundation
import SwiftUI
import Firebase
import Combine

class SessionStore : ObservableObject
{

    @Published var user: AppUser? { didSet { self.didChange.send(self) }}
    var didChange = PassthroughSubject<SessionStore, Never>()
    var handle: AuthStateDidChangeListenerHandle?

您要确保更改正在传播到侦听器(订户)。

如果我是正确的,@EnvironmentObject var appSession: SessionStore不应在SceneDelegate中提及