You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.28 00:45:01