SwiftUI @Published变量未更新视图,Firebase数据未加载完渲染致崩溃如何解决
核心问题
你创建了两个完全独立的DatabaseDelegate实例,状态无法互通:
- AppDelegate中调用
DatabaseDelegate().getUserInfo时创建了一个临时实例,数据加载完成后更新的是这个临时实例的userDataLoaded属性 - SwiftUI视图中初始化的
@ObservedObject var databaseDelegate = DatabaseDelegate()是另一个全新实例,它的userDataLoaded永远保持初始值false,自然不会触发UI更新
修复方案
1. 给DatabaseDelegate添加全局单例
确保整个App共用同一个实例,同时补充错误处理、主线程更新逻辑:
class DatabaseDelegate: ObservableObject { // 全局单例 static let shared = DatabaseDelegate() @Published var userDataLoaded = false // 也可以把用户数据直接存到这里,更符合SwiftUI数据流 @Published var currentUser: User? // 私有化初始化方法,禁止外部创建新实例 private init() {} func getUserInfo(UID: String, withCompletionHandler completionHandler: @escaping () -> Void) { database.collection("Users").document(UID).getDocument { [weak self] (document, error) in // 切到主线程更新UI相关的Published属性 DispatchQueue.main.async { guard let self = self else { return } // 处理错误情况 if let error = error { print("获取用户信息失败: \(error)") completionHandler() return } guard let document = document, document.exists, let data = document.data() else { print("用户文档不存在") completionHandler() return } guard let UID = data["UUID"] as? String, let Name = data["Name"] as? String, let PhoneNumber = data["PhoneNumber"] as? String, let StripeID = data["StripeID"] as? String else { print("用户字段解析失败") completionHandler() return } self.currentUser = User(UID: UID, Name: Name, PhoneNumber: PhoneNumber, StripeID: StripeID, PurchasedContent: ["TEMP": true]) self.userDataLoaded = true completionHandler() } } } }
2. 修改AppDelegate中调用方式
使用单例而不是新建临时实例:
// 把原来的DatabaseDelegate().getUserInfo替换成单例调用 DatabaseDelegate.shared.getUserInfo(UID: user!.uid, withCompletionHandler: { print("got user data") })
3. 修改SwiftUI视图的数据源
使用@StateObject(比@ObservedObject更稳定,适合持有ObservableObject)绑定全局单例:
struct MainViewDelegate: View { //MARK: VARIABLES @State var showAnimation = true @State var locationHandler = LocationHandler() // 绑定全局单例,不要自己创建新实例 @StateObject var databaseDelegate = DatabaseDelegate.shared init(){ //Check and enable user location locationHandler.requestAuthorisation() } var body: some View { VStack { 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") } } } }
额外优化建议
- 可以删除全局的
UserData存储,直接从DatabaseDelegate.shared.currentUser读取用户数据,避免多数据源不一致 - 可以在加载状态添加失败分支,比如
userDataLoaded保持false超过5秒提示用户网络异常,避免一直卡在加载页 - 如果需要支持未登录状态,可在
DatabaseDelegate中添加isUserLoggedIn状态,用来切换登录页和主视图
内容的提问来源于stack exchange,提问作者Omar
相关产品推荐
相关产品推荐

