使用 Swift 在 iOS 应用中使用 DispatchQueue 等待数据从 Cloud Firestore 发送到 return 时遇到问题
Having trouble using DispatchQueue to wait for data to return from Cloud Firestore in iOS app using Swift
我正在尝试使用 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)")
}
也许这可以帮到你。我有很多用户,它附加到我的模型,并且可以检查我何时拥有所有数据并继续我的代码:
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")
}
}
从问题来看,似乎不需要任何 DispatchQueue。 Firestore 是异步的,因此数据仅在 firebase 函数之后的闭包内有效。此外,UI 调用在主线程上处理,而网络在后台线程上处理。
如果您想等待所有数据从 Firestore 加载,这将在 Firestore 调用之后的闭包内完成。例如,假设我们有一个区域集合,其中包含存储开始和停止指标的文档
zones
zone_0
start: 1
stop: 3
zone_1
start: 7
stop: 9
对于此示例,我们将每个区域的开始和停止存储在 class 元组数组中
var tupleArray = [(Int, Int)]()
和要在区域中读取的代码,填充 tupleArray,然后执行 'next step' - 在这种情况下打印它们。
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( [=12=].0, [=12=].1) }
})
}
和输出
1 3
7 9
由于 Firebase 的异步特性,您不能从闭包中 'return',但如果需要,您可以利用完成处理程序从闭包中传递数据 'back'。
我正在尝试使用 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)")
}
也许这可以帮到你。我有很多用户,它附加到我的模型,并且可以检查我何时拥有所有数据并继续我的代码:
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")
}
}
从问题来看,似乎不需要任何 DispatchQueue。 Firestore 是异步的,因此数据仅在 firebase 函数之后的闭包内有效。此外,UI 调用在主线程上处理,而网络在后台线程上处理。
如果您想等待所有数据从 Firestore 加载,这将在 Firestore 调用之后的闭包内完成。例如,假设我们有一个区域集合,其中包含存储开始和停止指标的文档
zones
zone_0
start: 1
stop: 3
zone_1
start: 7
stop: 9
对于此示例,我们将每个区域的开始和停止存储在 class 元组数组中
var tupleArray = [(Int, Int)]()
和要在区域中读取的代码,填充 tupleArray,然后执行 'next step' - 在这种情况下打印它们。
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( [=12=].0, [=12=].1) }
})
}
和输出
1 3
7 9
由于 Firebase 的异步特性,您不能从闭包中 'return',但如果需要,您可以利用完成处理程序从闭包中传递数据 'back'。