Swift中Firestore getDocuments查询快照内无法给变量赋值如何解决?
核心原因
getDocuments 是异步执行的接口,你写的return语句会在网络请求返回、回调触发前就执行完毕,所以拿到的永远是retVal的初始值。
解决方案
方案1:使用完成回调(全系统版本兼容)
把需要拿到判断结果后执行的逻辑,放到回调闭包中执行,示例代码如下:
首先封装判断逻辑的方法:
func checkUsernameUnique(username: String, completion: @escaping (Bool, Error?) -> Void) { Firestore.firestore().collection("users") .whereField("username", isEqualTo: username) .getDocuments { querySnapshot, error in if let error = error { print(error.localizedDescription) completion(false, error) return } let isUnique = querySnapshot?.isEmpty ?? false print(isUnique ? "QuerySnapshot is empty, this is unique" : "There's a bloody username in here, get a new one") completion(isUnique, nil) } }
调用方法示例:
guard let inputUsername = usernameTextField.text, !inputUsername.isEmpty else { // 处理输入为空的异常逻辑 return } checkUsernameUnique(username: inputUsername) { [weak self] isUnique, error in guard let self = self else { return } // 所有需要用到isUnique结果的逻辑都写在这个闭包里 if let error = error { // 弹出网络/服务错误提示 return } if isUnique { // 用户名唯一,执行后续注册、提交等逻辑 } else { // 提示用户用户名已存在,要求更换 } }
方案2:使用async/await(适配iOS 15+)
如果你的App最低支持版本在iOS 15及以上,可以用Swift Concurrency的异步写法,避免闭包嵌套,代码更简洁:
封装方法:
func checkUsernameUnique(username: String) async throws -> Bool { let snapshot = try await Firestore.firestore().collection("users") .whereField("username", isEqualTo: username) .getDocuments() return snapshot.isEmpty }
调用示例:
Task { guard let inputUsername = usernameTextField.text, !inputUsername.isEmpty else { return } do { let isUnique = try await checkUsernameUnique(username: inputUsername) // 拿到结果后执行后续逻辑 if isUnique { // 用户名可用 } else { // 用户名重复 } } catch { // 处理请求错误 print(error.localizedDescription) } }
注意事项
不要使用DispatchSemaphore等方式阻塞主线程等待异步结果,会导致UI无响应,严重时会被系统直接杀死应用,所有依赖异步结果的逻辑都应该放到异步回调的上下文中执行
内容的提问来源于stack exchange,提问作者EAO123
相关产品推荐
相关产品推荐

