【问题标题】:SwiftUI A View.environmentObject(_:) for may be missing as an ancestor of this view.: fileSwiftUI A View.environmentObject(_:) for 可能作为此视图的祖先丢失。:文件
【发布时间】:2020-05-11 13:51:16
【问题描述】:

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

对于登录,我使用默认的 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/ 的代码,它使用的是旧式 Bindable 协议

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
        }
    }
}

环境对象出现在我的内容视图和场景代理中

场景委托

class SceneDelegate: UIResponder, UIWindowSceneDelegate, FUIAuthDelegate{

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

内容视图

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 中 de environmentObject 丢失的事实有关,但我如何防止这种情况发生?我需要以某种方式扩展 MyFUIAuthPickerViewController 以允许传递 environmentObject 但如何?

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

【问题讨论】:

  • 我不确定发生了什么,但您使用environmentObject 窗口的根视图创建了ContentView,因此您的MyFUI...VC 不会妨碍您。几个方面:@EnvironmentObjectSceneDelegate 上没有做任何事情。它仅适用于视图。此外,如果您使用guard let user = user ...,那么您不必在整个函数中继续使用!
  • 你能解决这个问题吗?我正在经历类似的事情

标签: ios swift firebase observable


【解决方案1】:

您在 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 中提及

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-07-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-24
    • 2021-05-22
    • 2020-05-11
    • 2021-12-30
    相关资源
    最近更新 更多