Firestore异步查询未完成致后续代码报错问题求助
问题解决:Firestore查询未完成就执行后续代码导致数组越界
核心问题分析
你的代码存在两个关键问题:
- Async/Await版本:你在
getUser()里用的是闭包回调版的getDocuments(),没有使用Firestore提供的可await的异步API,导致async函数并没有真正等待查询完成就返回了。 - Completion Handler版本:
completion(true)被放在了for document循环内部,会在处理第一个文档时就触发回调,而非等所有文档处理完成后;如果集合为空,回调甚至不会被调用,后续代码永远得不到执行信号。
修正方案
方案1:正确使用Async/Await
Firestore从iOS 15+(配合Firebase SDK 8.10+)开始支持原生Async/Await,直接使用getDocuments()的异步版本即可:
// 修正后的getUser方法 func getUser() async throws { let querySnapshot = try await self.db.collection("userSetting").getDocuments() for document in querySnapshot.documents { // 用可选绑定避免强制解包崩溃 guard let userTrust = document.data()["userTrust"] as? String, let userGrade = document.data()["userGrade"] as? String, let userDisclaimer = document.data()["userDisclaimer"] as? String else { print("文档字段缺失或类型错误") continue } let row = [userTrust, userGrade, userDisclaimer] self.userArray.append(row) self.userTrustInfo = userTrust self.userGradeInfo = userGrade } }
调用方式(在didFinishLaunchingWithOptions中)
注意:didFinishLaunchingWithOptions必须立即返回true,所以需要在Task内部等待查询完成后,再执行依赖userArray的代码:
internal func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { FirebaseApp.configure() db = Firestore.firestore() Database.database().isPersistenceEnabled = true Task { do { // 等待用户数据加载完成 try await getUser() // 这里执行依赖userArray的代码,比如初始化UI、跳转页面等 print("用户数据加载完成:\(userArray)") setupMainUI() // 示例:调用依赖数据的方法 } catch { print("加载用户数据失败:\(error)") // 处理错误,比如显示错误页面 } } return true }
方案2:正确使用Completion Handler
把completion移到循环外部,确保所有文档处理完成后再触发回调;同时处理错误场景,避免回调丢失:
// 修正后的getUser方法 func getUser(completion: @escaping (Bool, Error?) -> Void) { self.db.collection("userSetting").getDocuments() { (querySnapshot, err) in if let err = err { print("Error getting documents: \(err)") completion(false, err) return } guard let documents = querySnapshot?.documents else { print("没有查询到文档") completion(true, nil) return } for document in documents { guard let userTrust = document.data()["userTrust"] as? String, let userGrade = document.data()["userGrade"] as? String, let userDisclaimer = document.data()["userDisclaimer"] as? String else { print("文档字段异常") continue } let row = [userTrust, userGrade, userDisclaimer] self.userArray.append(row) self.userTrustInfo = userTrust self.userGradeInfo = userGrade } // 所有文档处理完成后再调用回调 completion(true, nil) } }
调用方式
internal func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { FirebaseApp.configure() db = Firestore.firestore() Database.database().isPersistenceEnabled = true getUser { [weak self] success, error in guard let self = self else { return } if success { // 执行依赖userArray的代码 print("用户数据加载完成:\(self.userArray)") self.setupMainUI() } else { print("加载失败:\(error?.localizedDescription ?? "未知错误")") // 错误处理 } } return true }
额外建议
- 避免使用
as!强制解包,改用as?配合可选绑定,防止字段缺失或类型错误导致崩溃。 - 如果
userSetting集合只有一个用户文档,可以直接用document()获取单文档,而非查询整个集合,提升效率。 - 若启动时必须等待数据加载完成才能进入主界面,可以添加启动屏(Launch Screen)或加载指示器,避免用户看到空白或异常界面。
内容的提问来源于stack exchange,提问作者Nicholas Farmer
相关产品推荐
相关产品推荐

