【问题标题】:SwiftUI published variable not updating viewSwiftUI 发布的变量不更新视图
【发布时间】:2021-12-19 02:13:58
【问题描述】:

上下文:我正在尝试使用 firebase 身份验证和 firestore 来获取用户的数据。我遇到的问题是视图是在完全获取数据之前呈现的,这显然会导致应用程序崩溃。话虽如此,我在我的应用委托中使用了 Firebase 身份验证侦听器,以确保在获取用户数据之前对用户进行身份验证(这也在应用委托中完成,如下所示)

应用委托sn-p

class AppDelegate: NSObject, UIApplicationDelegate {
    var handle: AuthStateDidChangeListenerHandle?

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
        FirebaseApp.configure()

        self.handle = Auth.auth().addStateDidChangeListener { (auth, user) in
            if (user != nil){
                print("UserAuthentication User authenticated in delegate")
                
                DatabaseDelegate().getUserInfo(UID: user!.uid, withCompletionHandler: {
                    print("got user data")
                })
                
            } else {
                print(" UserAuthentication User not authenticated in delegate")
                try! Auth.auth().signOut()
            }
        }
        return true
    }

这是我正在查询并希望在数据加载完成时监听的数据库代码:

class DatabaseDelegate: ObservableObject {
    @Published var userDataLoaded = Bool()

    func getUserInfo(UID: String, withCompletionHandler completionHandler: @escaping () -> Void) {
        database.collection("Users").document(UID).getDocument { (document, error) in
            if let document = document, document.exists {
                let data = document.data()!
                guard let UID = data["UUID"] as? String else { return }
                guard let Name = data["Name"] as? String else { return }
                guard let PhoneNumber = data["PhoneNumber"] as? String else { return }
                guard let StripeID = data["StripeID"] as? String else { return }
                
                self.userDataLoaded = true
                
                UserData.append(User(UID: UID, Name: Name, PhoneNumber: PhoneNumber, StripeID: StripeID, PurchasedContent: ["TEMP": true]))
                
                completionHandler()
            }
        }
    }
}

这是我要根据上面的 userDataLoaded 更新的 SwiftUI 视图:

struct MainViewDelegate: View {
    //MARK: VARIABLES
    @State var showAnimation = true
    @State var locationHandler = LocationHandler()
    @ObservedObject var databaseDelegate = DatabaseDelegate()
    
    init(){
        //Check and enable user location
        locationHandler.requestAuthorisation()
    }

    var body: some View {
        VStack {
            //Check if data has finished loading, if not, show loading. Listen for changes when the data is finished loading and then present the tab view when it is.
            switch databaseDelegate.userDataLoaded {
                case true:
                    TabView {
                        HomeView()
                            .tabItem {
                                Label("Home", systemImage: "house")
                            }
                        CheckoutView()
                            .tabItem {
                                Label("Services", systemImage: "bag")
                            }
                        SettingsView()
                            .tabItem {
                                Label("Settings", systemImage: "gearshape")
                            }
                    }
                case false:
                    Text("Loading data")
            }
        }
    }
}

在此先感谢您。我是 swiftui 新手(从 uikit 过渡),我花了太多时间试图解决这个愚蠢的问题

【问题讨论】:

标签: swift firebase swiftui


【解决方案1】:
  1. 您正在使用DatabaseDelegate 的两个不同实例,一个在 AppDelegate 中,一个在 MainViewDelegate 中。布尔值仅在应用委托的实例中更新。

将您的身份验证侦听器移动到您的 DatabaseDelegate。

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

class DatabaseDelegate: ObservableObject {
    @Published var userDataLoaded = false

    private var handle: AuthStateDidChangeListenerHandle?
    
    init() {
        self.handle = Auth.auth().addStateDidChangeListener { (auth, user) in
           // .. etc
           self.getUserInfo(...)
        }
    }

    private func getUserInfo(UID: String, withCompletionHandler completionHandler: @escaping () -> Void) {
        database.collection("Users").document(UID).getDocument { (document, error) in
            // .. etc
            self.userDataLoaded = true
        }
    }
}
  1. 您需要使用StateObject 而不是ObservedObject,因为您是在视图内部对其进行初始化,而不是从外部源注入它。
struct MainViewDelegate: View {
    @StateObject private var databaseDelegate = DatabaseDelegate()
}

如果你想使用 ObservedObject,你可以在外部创建它并像这样注入到视图中:

var databaseDelegate = DatabaseDelegate()

MainViewDelegate(databaseDelegate: databaseDelegate)

struct MainViewDelegate: View {
    @ObservedObject var databaseDelegate: DatabaseDelegate
}

【讨论】:

  • 非常感谢!我非常感谢您的帮助
猜你喜欢
  • 2020-11-26
  • 2021-08-06
  • 2023-04-08
  • 2020-04-10
  • 2022-10-04
  • 2022-09-28
  • 1970-01-01
  • 2020-04-15
  • 2022-01-24
相关资源
最近更新 更多