在使用 Swift 的 iOS 应用程序中使用 DispatchQueue 等待数据从 Cloud Firestore 返回时遇到问题
Posted
技术标签:
【中文标题】在使用 Swift 的 iOS 应用程序中使用 DispatchQueue 等待数据从 Cloud Firestore 返回时遇到问题【英文标题】:Having trouble using DispatchQueue to wait for data to return from Cloud Firestore in iOS app using Swift 【发布时间】:2020-02-10 22:31:20 【问题描述】:我正在尝试使用 DispatchQueue 让我的代码等待查询从 Cloud Firestore 检索到我需要的结果,然后再继续执行,但只是无法让它工作。在下面的代码中,我试图让它等到数据被检索并存储在 zoneMarkerArray 中,然后打印出结果。
我已经按照我希望它发生的顺序对它打印的每一行进行了编号,并且正如您在输出中看到的那样,它不会在继续之前等待 Firestore 结果。
这是我的代码:
let zones = self.db.collection("zones")
let zonesQuery = zones.whereField("start", isGreaterThan: lowerLimit).whereField("start", isLessThan: upperLimit)
print("1. zones Query has been defined")
//pass zonesQuery query to getZoneMarkers function to retrieve the zone markers from Firestore
getZoneMarkers(zonesQuery)
print("6. Now returned from getZoneMarkers")
func getZoneMarkers(_ zonesQuery: Query) -> ([Double])
print("2. Entered getZoneMarkers function")
DispatchQueue.global(qos: .userInteractive).async
zonesQuery.getDocuments() (snapshot, error) in
if let error = error
print("Error getting zone markers: \(error)")
else
print("3. Successfully Retrieved the zone markers")
var result: Double = 0.0
for document in snapshot!.documents
print("Retrieved zone marker is \(document["start"]!)")
self.zoneMarkerArray.append(document["start"]! as! Double)
print("4. Looping over zone marker results")
DispatchQueue.main.async
//I want this the printCompleted function to print the result AFTER the results have been retrieved
self.printCompleted()
return self.zoneMarkerArray
func printCompleted()
print("5. Looping now completed. Result was \(zoneMarkerArray)")
这是打印出来的输出:
-
区域查询已定义
进入 getZoneMarkers 函数
现在从 getZoneMarkers 返回
循环现已完成。结果为 [0.0]
成功检索区域标记
循环遍历区域标记结果
循环遍历区域标记结果
检索到的区域标记为 12.0
循环遍历区域标记结果
感谢您的帮助!
编辑:如果其他人也为此苦苦挣扎,这是我最后根据收到的反馈汇总的工作代码。如果您看到如何进一步改进,请随时批评:
let zones = self.db.collection("zones")
let zonesQuery = zones.whereField("start", isGreaterThan: lowerLimit).whereField("start", isLessThan: upperLimit)
print("1. zones Query has been defined")
//pass zonesQuery query to getZoneMarkers function to retrieve the zone markers from Firestore
getZoneMarkers(zonesQuery)
func getZoneMarkers(_ zonesQuery: (Query))
print("2. Entered getZoneMarkers function")
zoneMarkerArray.removeAll()
zonesQuery.getDocuments(completion: (snapshot, error) in
if let error = error
print("Error getting zone markers: \(error)")
return
guard let docs = snapshot?.documents else return
print("3. Successfully Retrieved the zone markers")
for document in docs
self.zoneMarkerArray.append(document["start"]! as! Double)
print("4. Looping over zone marker results")
self.completion(zoneMarkerArray: self.zoneMarkerArray)
)
func completion(zoneMarkerArray: [Double])
print("5. Looping now completed. Result was \(zoneMarkerArray)")
【问题讨论】:
DispatchQueue.main.async
应该在 zonesQuery.getDocuments()
闭包内
你不能通过异步方法return
。即使您确实正确地实现了等待,也很有可能最终会阻塞主队列。您应该将完成闭包传递给您的 getZoneMarkers
函数,然后使用检索到的标记传递然后调用该闭包。见***.com/questions/25203556/…
您真的需要花时间了解异步方法的工作原理:firebase.googleblog.com/2018/07/…
你为什么使用 QOS : userInteractive?.
感谢所有建议!我查看了你们所说的所有内容以及下面提出的答案,并决定对我来说最好的方法是放弃 DispatchQueue 并使用完成处理程序。不得不阅读一堆关于它们的不同博客,因为我发现它完全令人困惑,但终于让它工作了。 @Paresh.P - QOS:userinteractive 只是我在玩,看看更改优先级是否对执行有任何影响。
【参考方案1】:
从问题来看,似乎不需要任何 DispatchQueue。 Firestore 异步,因此数据仅在 firebase 函数之后的闭包内有效。此外,UI 调用在主线程上处理,而网络在后台线程上处理。
如果您想等待从 Firestore 加载所有数据,这将在 Firestore 调用之后的闭包内完成。例如,假设我们有一个区域集合,其中包含存储开始和停止指示符的文档
zones
zone_0
start: 1
stop: 3
zone_1
start: 7
stop: 9
对于这个例子,我们将把每个区域的开始和结束存储在一个元组类数组中
var tupleArray = [(Int, Int)]()
以及要在区域中读取的代码,填充 tupleArray,然后执行“下一步” - 在这种情况下打印它们。
func readZoneMarkers()
let zonesQuery = self.db.collection("zones")
zonesQuery.getDocuments(completion: documentSnapshot, error in
if let err = error
print(err.localizedDescription)
return
guard let docs = documentSnapshot?.documents else return
for doc in docs
let start = doc.get("start") as? Int ?? 0
let end = doc.get("end") as? Int ?? 0
let t = (start, end)
self.tupleArray.append(t)
//reload your tableView or collectionView here,
// or proceed to whatever the next step is
self.tupleArray.forEach print( $0.0, $0.1)
)
和输出
1 3
7 9
由于 Firebase 的异步特性,您无法从闭包中“返回”,但如果需要,您可以利用完成处理程序从闭包中“返回”数据。
【讨论】:
感谢@Jay,这很有帮助。根据每个人的输入,我使用完成处理程序让它工作 - 我将编辑我的原始问题以包括我最终做了什么。【参考方案2】:也许这可以帮助你。我有很多用户,它附加到我的模型中,并且可以检查我何时拥有所有数据并继续使用我的代码:
func allUser (completion: @escaping ([UserModel]) -> Void)
let dispatchGroup = DispatchGroup()
var model = [UserModel]()
let db = Firestore.firestore()
let docRef = db.collection("users")
dispatchGroup.enter()
docRef.getDocuments (querySnapshot, err) in
for document in querySnapshot!.documents
print("disp enter")
let dic = document.data()
model.append(UserModel(dictionary: dic))
dispatchGroup.leave()
dispatchGroup.notify(queue: .main)
completion(model)
print("completion")
【讨论】:
以上是关于在使用 Swift 的 iOS 应用程序中使用 DispatchQueue 等待数据从 Cloud Firestore 返回时遇到问题的主要内容,如果未能解决你的问题,请参考以下文章
如何在iOS swift的tableview中使用json响应中的键和值?
在 iOS (Swift) 中使用后台位置更新的最佳方式 [关闭]