SwiftUI 视图未更新为 EnvironmentObject 更改

2023-12-31

我正在创建一个 SwiftUI 应用程序,其中包含 Firebase 来启用帐户登录,非常简单,只是一个带有密码和电子邮件字段的 ui 表单,然后是一个提交按钮。用户登录后,我将 firebase 用户对象存储在 EnvironmentObject 中,以便其余视图可以访问它。目前应用程序的问题是,一旦用户登录并且用户数据存储在EnvironmentObject中,视图应该更新到更改后的状态以显示不同的屏幕,但似乎视图仍然认为EnvironmentObject等于零。视图是否不会像状态变量那样自动更改为环境对象中的更新?

我已确保环境对象设置正确并传递给预览和 SceneDelegate

确保应用程序确实通过在登录时将帐户信息打印到控制台来成功登录用户,但视图本身仅显示帐户信息为零,似乎它不会使用用户信息访问更新的环境对象。

import SwiftUI
import Firebase
import Combine

struct ContentView: View {

    @EnvironmentObject var session: SessionStore

    @State var emailTextField: String = ""
    @State var passwordTextField: String = ""

    @State var loading = false
    @State var error = false

    var body: some View {
        VStack {
            if (session.session != nil) {
                Home()
            } else {
                Form {
                    TextField("Email", text: $emailTextField)
                    SecureField("Password", text: $passwordTextField)
                    Button(action: signIn) {
                        Text("Sign in")
                    }
                }

                Text("Session: \(session.session?.email ?? "no user")")
            }
        }.onAppear(perform: getUser)
    }

    func getUser () {
        session.listen()
    }

    func signIn () {
        loading = true
        error = false
        session.signIn(email: emailTextField, password: passwordTextField) { (result, error) in
            self.loading = false
            if error != nil {
                self.error = true
            } else {
                self.emailTextField = ""
                self.passwordTextField = ""
            }
        }
    }
}

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



class SessionStore : ObservableObject {

    var didChange = PassthroughSubject<SessionStore, Never>()
    var session: User? { didSet { self.didChange.send(self) }}
    var handle: AuthStateDidChangeListenerHandle?

    func listen () {
        // monitor authentication changes using firebase
        handle = Auth.auth().addStateDidChangeListener { (auth, user) in
            if let account = user {
                // if we have a user, create a new user model
                print("Got user: \(account)")
                self.session = User(
                    uid: account.uid,
                    displayName: account.displayName,
                    email: account.email
                )
                print("Session: \(self.session?.email ?? "no user")")
            } else {
                // if we don't have a user, set our session to nil
                self.session = nil
            }
        }
    }

    func signUp(
        email: String,
        password: String,
        handler: @escaping AuthDataResultCallback
        ) {
        Auth.auth().createUser(withEmail: email, password: password, completion: handler)
    }

    func signIn(
        email: String,
        password: String,
        handler: @escaping AuthDataResultCallback
        ) {
        Auth.auth().signIn(withEmail: email, password: password, completion: handler)
    }

    func signOut () -> Bool {
        do {
            try Auth.auth().signOut()
            self.session = nil
            return true
        } catch {
            return false
        }
    }

    func unbind () {
        if let handle = handle {
            Auth.auth().removeStateDidChangeListener(handle)
        }
    }
}

class User {
    var uid: String
    var email: String?
    var displayName: String?

    init(uid: String, displayName: String?, email: String?) {
        self.uid = uid
        self.email = email
        self.displayName = displayName
    }

}

正如您在视图中看到的,当用户未登录时,它应该呈现登录字段,而当用户登录时,视图应该显示另一个视图。其他视图未显示。


尝试使用@Published 属性。 尝试实现这样的事情:

class SessionStore : ObservableObject {
    @Published var session: User
}

class User: ObservableObject {
    @Published var uid: String
    @Published var email: String?
    @Published var displayName: String?

    init(uid: String, displayName: String?, email: String?) {
        self.uid = uid
        self.email = email
        self.displayName = displayName
    }

}

当用户对象(例如电子邮件或显示名称)发生更改时,这应该会更新您的视图,因为它们已发布。 希望这会有所帮助,GL

UPDATED:

因为 SwiftUI 还不支持嵌套 Observables,所以你需要自己通知你的主模型。

请参阅以下代码片段,了解如何在 ObservableObject 中使用嵌套的 ObservableObject:

class Submodel1: ObservableObject {
  @Published var count = 0
}

class Submodel2: ObservableObject {
  @Published var count = 0
}

class Model: ObservableObject {
  @Published var submodel1: Submodel1 = Submodel1()
  @Published var submodel2: Submodel2 = Submodel2()
    
    var anyCancellable: AnyCancellable? = nil
    var anyCancellable2: AnyCancellable? = nil

    init() {
        
        anyCancellable = submodel1.objectWillChange.sink { (_) in
            self.objectWillChange.send()
        }
        
        anyCancellable2 = submodel2.objectWillChange.sink { (_) in
            self.objectWillChange.send()
        }
    }
}

当子模型内的数据发生变化时,主模型会通知自己。这将导致视图更新。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

SwiftUI 视图未更新为 EnvironmentObject 更改 的相关文章

随机推荐