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
相关产品推荐
相关产品推荐

