在applicationDidFinishLaunchingWithOptions中基于Firestore数据设置根控制器
解决方案:重启APP时根据Firestore的openOrder值切换根视图控制器
我来帮你梳理下这个问题的核心解决思路——本质是要处理异步数据读取和UI更新的时序问题,因为Firestore的读取是异步操作,不能直接在启动时同步阻塞主线程来等待结果。下面是具体的实现方案和常见坑的规避:
核心步骤拆解
- 先确认用户认证状态:要读取用户的订单数据,必须先拿到当前已登录的用户,避免未登录状态下的权限错误。
- 异步读取Firestore中的订单数据:用回调或async/await等待读取完成,不要同步阻塞主线程。
- 主线程更新根视图控制器:所有UI操作必须在主线程执行,所以拿到openOrder值后要切回主线程设置rootVC。
代码示例(Swift + SceneDelegate,适配iOS 13+)
方式1:用async/await(Swift 5.5+ 推荐)
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { guard let windowScene = (scene as? UIWindowScene) else { return } let window = UIWindow(windowScene: windowScene) self.window = window // 第一步:检查用户是否已登录 if let currentUser = Auth.auth().currentUser { Task { do { // 第二步:异步读取用户的订单(这里假设订单集合是"orders",用userId关联用户) let orderQuery = try await Firestore.firestore() .collection("orders") .whereField("userId", isEqualTo: currentUser.uid) .getDocuments() // 处理订单数据 let openOrder: Bool if let orderDoc = orderQuery.documents.first { openOrder = orderDoc.data()["openOrder"] as? Bool ?? false } else { // 用户无订单时默认设为false,跳转到新建订单页 openOrder = false } // 第三步:回到主线程设置根视图控制器 DispatchQueue.main.async { let rootVC = openOrder ? OpenOrderViewController() : NewOrderViewController() window.rootViewController = UINavigationController(rootViewController: rootVC) window.makeKeyAndVisible() } } catch { // 读取失败的容错处理(比如网络问题) print("读取订单失败:\(error.localizedDescription)") DispatchQueue.main.async { window.rootViewController = UINavigationController(rootViewController: NewOrderViewController()) window.makeKeyAndVisible() } } } } else { // 用户未登录,跳转到登录页 window.rootViewController = UINavigationController(rootViewController: LoginViewController()) window.makeKeyAndVisible() } }
方式2:用完成回调(兼容旧版Swift)
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { guard let windowScene = (scene as? UIWindowScene) else { return } let window = UIWindow(windowScene: windowScene) self.window = window guard let currentUser = Auth.auth().currentUser else { window.rootViewController = UINavigationController(rootViewController: LoginViewController()) window.makeKeyAndVisible() return } // 异步读取订单数据 Firestore.firestore() .collection("orders") .whereField("userId", isEqualTo: currentUser.uid) .getDocuments { [weak self] querySnapshot, error in guard let self = self else { return } // 必须回到主线程更新UI DispatchQueue.main.async { if let error = error { print("读取订单出错:\(error)") window.rootViewController = UINavigationController(rootViewController: NewOrderViewController()) window.makeKeyAndVisible() return } let openOrder = querySnapshot?.documents.first?.data()["openOrder"] as? Bool ?? false let rootVC = openOrder ? OpenOrderViewController() : NewOrderViewController() window.rootViewController = UINavigationController(rootViewController: rootVC) window.makeKeyAndVisible() } } }
常见坑点提醒
- 必须在主线程更新UI:Firestore的回调默认在后台线程,直接设置rootVC会导致崩溃,一定要用
DispatchQueue.main.async包裹。 - 默认值处理:如果订单中
openOrder字段不存在,要给一个默认值(比如false),避免强制解包崩溃。 - 错误容错:网络异常、权限不足都可能导致读取失败,一定要处理error情况,给用户一个兜底页面。
- 订单查询逻辑:如果用户可能有多个订单,要根据你的业务调整查询条件(比如只查最新的订单,或者特定状态的订单)。
内容的提问来源于stack exchange,提问作者H.Epstein
相关产品推荐
相关产品推荐

