Swift闭包内使用async/await加载Firebase数据失效问题排查
问题原因分析
你的loadData函数虽然标记了async,但内部没有正确处理Firestore的异步操作:
- Firestore的
getDocuments是闭包回调风格的异步API,它会在后台线程执行,而你的函数直接走到return date语句,此时闭包还没执行,date还是初始的nil,自然返回未加载完成的结果。 - 仅仅标记
async只能让函数被await调用,但函数内部没有任何挂起等待逻辑,await根本等不到异步操作完成。
正确实现方式
需要用withCheckedThrowingContinuation将Firestore的闭包API转换为async/await风格,让函数挂起直到异步操作完成,再返回结果。同时修复代码中的不安全操作(比如强制解包)。
修改后的完整代码
struct LoadData { let db = Firestore.firestore() var delegate: LoadDataDelegate? func getData(phoneNumber: String) { Task { do { let dateUser = try await loadData(phoneNumber: phoneNumber) print("Делегат получил дату - ", dateUser) delegate?.loadData(date: dateUser) } catch { print("加载数据出错:\(error)") delegate?.loadData(date: nil) } } } func loadData(phoneNumber: String) async throws -> TimeInterval { return try await withCheckedThrowingContinuation { continuation in db.collection("Users").getDocuments { snapshot, error in // 处理请求错误 if let err = error { continuation.resume(throwing: err) return } guard let snapshot = snapshot else { let emptySnapshotError = NSError(domain: "LoadDataError", code: -1, userInfo: [NSLocalizedDescriptionKey: "获取用户快照为空"]) continuation.resume(throwing: emptySnapshotError) return } // 查找是否存在目标用户 if let userDoc = snapshot.documents.first(where: { $0.documentID == phoneNumber }) { // 用户存在,解析时间戳 guard let date = userDoc["dateFirstLaunch"] as? TimeInterval else { let invalidDateError = NSError(domain: "LoadDataError", code: -2, userInfo: [NSLocalizedDescriptionKey: "时间戳格式无效"]) continuation.resume(throwing: invalidDateError) return } continuation.resume(returning: date) } else { // 用户不存在,创建新用户并返回当前时间戳 let currentDate = Date().timeIntervalSince1970 db.collection("Users").document(phoneNumber).setData(["dateFirstLaunch": currentDate]) { writeError in if let err = writeError { continuation.resume(throwing: err) } else { continuation.resume(returning: currentDate) } } } } } } } protocol LoadDataDelegate { func loadData(date: TimeInterval?) }
关键改动说明
- 用
withCheckedThrowingContinuation包装异步操作:
这个API可以将闭包风格的异步代码转为async/await,当Firestore的请求完成(成功或失败)时,调用continuation.resume()来恢复函数执行,返回结果或抛出错误。 - 修复不安全操作:
- 去掉
QuerySnapshot!的强制解包,改用可选绑定+自定义错误处理 - 用
delegate?.替代delegate!.,避免delegate为nil时崩溃
- 去掉
- 完善新用户逻辑:
新用户创建时,直接返回当前时间戳,确保无论用户是否存在,都能返回有效数据。
内容的提问来源于stack exchange,提问作者Denim
相关产品推荐
相关产品推荐

