SwiftUI视图未更新为EnvironmentObject更改 [英] SwiftUI view is not updating to EnvironmentObject change

查看:100
本文介绍了SwiftUI视图未更新为EnvironmentObject更改的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm creating a SwiftUI app that includes Firebase to enable logging into an account, extremely simple, just a ui form with password and email fields, then a button to submit. Once the user signs in I store the firebase user object in an EnvironmentObject so the rest of the views will have access to it. The problem with the app currently is that once the user logs in and the user data is stored in the EnvironmentObject, the view is supposed to update to the changed state of this to show a different screen, but it seems the view still thinks the EnvironmentObject is equal to nil. Do views not automatically change to updates in an EnvironmentObject like they do for state variables perhaps?

我已确保正确设置EnvironmentObject并将其传递给预览和SceneDelegate

I've made sure the EnvironmentObject is setup properly and passed to both the preview and SceneDelegate

通过在登录时将帐户信息打印到控制台来确保应用程序确实能够成功登录用户,但是视图本身仅显示nil作为帐户信息,看来它不会使用用户信息访问更新的EnvironmentObject .

Made sure that the app is indeed successfully logging in the user by printing account information to the console upon sign in, yet the view itself will only display nil for account information, it seems it wont access the updated EnvironmentObject with the user info.

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
    }

}

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

As you can see in the view, it is supposed to render login fields when user is not logged in, and when the user is logged in the view should display another view. That other view is not displaying.

推荐答案

尝试使用@Published属性. 尝试实现这样的东西:

Try to make use of the @Published property. Try to implement something like this:

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
    }

}

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

This should update your view when a change was made in the User object, like the email or displayname because they're Published. Hope this will help, gl

已更新:

由于SwiftUI还不支持嵌套的Observable,因此您需要自己通知主模型.

Because SwiftUI doesn't support nested Observables yet, you need to notify your main model by yourself.

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

See this snippet how to work with a nested ObservableObject inside a 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()
        }
    }
}

当子模型中的数据更改时,主模型将通知自己.这将导致视图上的更新.

When data inside a submodel changes, the main Model will notify itself. This will result in a update on the view.

让我知道这是否对您有帮助..祝你好运!

Let me know if this helped you out.. Goodluck!

这篇关于SwiftUI视图未更新为EnvironmentObject更改的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆