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

Swift中如何在Firestore异步获取用户数据后加载视图控制器?

解决Firestore异步加载导致视图加载前用户单例未初始化的问题

我太懂这种异步回调赶不上视图加载的坑了!你现在的问题核心是Firestore的getDocument是异步操作,而SceneDelegate里默认的根视图控制器(应该就是HomeVC)会在异步请求完成前就加载,导致User.current还没赋值就被调用。下面给你几个靠谱的解决思路:

方案一:先显示过渡加载页,等用户数据就绪后再切换视图

这个方案能给用户明确的加载反馈,同时确保视图加载时用户数据已经完全准备好。

修改你的SceneDelegate代码:

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
    guard let windowScene = (scene as? UIWindowScene) else { return }
    self.window = UIWindow(windowScene: windowScene)
    
    // 先显示一个加载页面(你可以自己做个带ActivityIndicator的LoadingVC)
    let loadingVC = LoadingViewController()
    self.window?.rootViewController = loadingVC
    self.window?.makeKeyAndVisible()
    
    if let currentUser = Auth.auth().currentUser {
        print("user is logged in, uid: \(currentUser.uid)")
        UserService.getUserData(uid: currentUser.uid) { [weak self] data in
            guard let self = self else { return }
            if let data = data {
                let myUser = User(uid: currentUser.uid, data: data)
                User.setCurrent(myUser)
                print("User was set")
                // 加载完成,切换到主页面
                let storyboard = UIStoryboard(name: "Main", bundle: .main)
                let homeVC = storyboard.instantiateInitialViewController()!
                self.window?.rootViewController = homeVC
            } else {
                print("Error retrieving user data")
                // 加载失败,跳转到登录页
                let loginVC = UIStoryboard.initalViewController(for: .login)
                self.window?.rootViewController = loginVC
            }
        }
    } else {
        // 未登录,直接跳登录页
        let loginVC = UIStoryboard.initalViewController(for: .login)
        self.window?.rootViewController = loginVC
    }
}

这里关键是先把window的根视图设为加载页,等异步请求完成后再替换成目标页面,这样HomeVC的viewDidLoad只会在用户数据准备好后才触发。

方案二:给User类添加状态监听,让HomeVC响应数据变化

如果不想加过渡页,可以让HomeVC监听User.current的变化,当数据就绪后再更新UI。

首先修改你的User类,添加一个静态回调:

class User {
    static var current: User! {
        didSet {
            // 当current被设置时触发回调
            didSetCurrentUser?(current)
        }
    }
    static var didSetCurrentUser: ((User) -> Void)?
    
    // 你的其他属性和方法...
    let uid: String
    let firstName: String
    
    init(uid: String, data: [String: Any]) {
        self.uid = uid
        self.firstName = data["firstName"] as? String ?? ""
    }
    
    static func setCurrent(_ user: User) {
        current = user
    }
}

然后在HomeVC里监听这个回调:

override func viewDidLoad() {
    super.viewDidLoad()
    availabilitySwitch.isOn = false
    print("Home view loaded")
    
    // 先检查current是否已存在
    if let currentUser = User.current {
        updateWelcomeLabel(with: currentUser)
    } else {
        // 显示加载状态,比如隐藏label或者显示ActivityIndicator
        welcomeLabel.text = "Loading..."
        // 监听User.current的变化
        User.didSetCurrentUser = { [weak self] user in
            self?.updateWelcomeLabel(with: user)
        }
    }
}

private func updateWelcomeLabel(with user: User) {
    welcomeLabel.text = "Welcome Back, \(user.firstName)!"
}

这个方案的好处是不需要修改SceneDelegate的视图切换逻辑,而是让HomeVC自己处理数据就绪后的UI更新。

为什么你的原代码会出问题?

你原代码里,当有登录用户时,会触发异步的getUserData,但此时如果你的storyboard默认的根视图控制器是HomeVC,那么HomeVC会立刻加载,而异步请求还在后台进行,这时候User.current还没被赋值,就会导致welcomeLabel.text设置时出错。

总结下来,方案一的用户体验最好,方案二更灵活,你可以根据自己的需求选择~

内容的提问来源于stack exchange,提问作者byfu34

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.08 07:07:48