[英]Waiting for loop to finish with DispatchGroup getting data from firebase using Swift
我正在嘗試獲取一系列員工並等待它完成。
嘗試使用DispatchGroup
但似乎什么也沒發生。
有二維數組,第一個是在員工上方,第二個是在他成功收到的文件上。 然后,它應該將ShiftConst
對象附加到請求的數組中。
我希望它通過使用前面的完成塊來通知它何時完成。
我得到的結果是 0 並且“沒有完成”。
這是我的代碼:
@Published var employeesConst = [ShiftConst]()
func getAllShiftConsts(employees: [Employee], completion: @escaping (Bool) -> () = {_ in}){
let group = DispatchGroup()
var finish = false
for employee in employees{
let ref = self.session.db.collection(CollectionRef.users.rawValue).document(employee.uid!).collection(CollectionRef.shiftConsts.rawValue)
ref.getDocuments { (qSnapshot, error) in
guard let qSnapshot = qSnapshot, !qSnapshot.isEmpty else {return}
for document in qSnapshot.documents{
group.enter()
do{
let data = try document.decode(as: ShiftConst.self)
self.employeesConst.append(data)
finish = true
group.leave()
}catch let error{
finish = false
group.leave()
print(error)
}
}
}
}
group.notify(queue: .main) {
completion(finish)
}
}
用法:
getAllShiftConsts(employees: session.employeesList) { finish in
if finish {
print("finish")
print("Count: ", self.employeesConst.count)
}else{
print("didn't finish")
print("Count: ", self.employeesConst.count)
}
}
完成后我想用它來做這個:
for (index, symbol) in self.date.weekDaySymbols.enumerated(){
let day = Day(date: self.date.nextWeek[index], isSelected: false, hasShifts: false, daySymbol: symbol, dayNumber: self.date.nextWeekNumbers[index])
day.setShifts(morning: self.session.employeesList, middle: self.session.employeesList, evening: self.session.employeesList)
for const in self.employeesConst{
if const.dateToString() == day.date{
for shift in day.shifts{
if shift.type == const.shiftType{
shift.filterEmployees(shiftConsts: const)
}
}
}
}
if index == 0 {day.isSelected = true}
self.week.append(day)
}
不要這樣做,因為您阻塞了主線程,使其對所有內容都沒有響應,而是在准備好時異步分派結果,例如
func getAllShiftConsts(employees: [Employee], completion: @escaping (Bool) -> () = {_ in}){
for employee in employees{
let ref = self.session.db.collection(CollectionRef.users.rawValue).document(employee.uid!).collection(CollectionRef.shiftConsts.rawValue)
ref.getDocuments { (qSnapshot, error) in
guard let qSnapshot = qSnapshot, !qSnapshot.isEmpty else {return}
for document in qSnapshot.documents{
do{
let data = try document.decode(as: ShiftConst.self)
DispatchQueue.main.async {
self.employeesConst.append(data)
}
}catch let error{
print(error)
}
}
}
}
}
問題在於, notify
確定到目前為止它遇到的所有enter
調用是否都被等量的leave
調用抵消了。 但是您的enter
調用在異步ref.getDocuments
調用中,因此無疑會在到達第一個enter
之前到達notify
。 並且,因此,它會立即觸發notify
塊,根本不等待。
一個更小的觀察結果是您還在該for
循環中使用了組,但這不是必需的,除非您在該循環內執行任何異步操作。 因此,除非您的for
循環觸發了額外的異步任務,否則我不會將它與組糾纏在一起。
因此,最重要的是,在異步調用之前調用enter
,並在調用完成后leave
(在這種情況下最好使用defer
實現,恕我直言)。
例如
func getAllShiftConsts(employees: [Employee], completion: ((Result<[ShiftConst], Error>) -> Void)? = nil) {
let group = DispatchGroup()
var employees: [ShiftConst] = []
var errors: [Error] = []
for employee in employees {
let ref = session.db.collection(CollectionRef.users.rawValue).document(employee.uid!).collection(CollectionRef.shiftConsts.rawValue)
group.enter()
ref.getDocuments { qSnapshot, error in
defer { group.leave() }
guard let qSnapshot = qSnapshot, !qSnapshot.isEmpty else { return }
for document in qSnapshot.documents {
do {
let data = try document.decode(as: ShiftConst.self)
employees.append(data)
} catch let error {
errors.append(error)
}
}
}
}
group.notify(queue: .main) {
if let error = errors.first {
completion?(.failure(error)) // I don't know what you want to do with all the errors, so I'll just grab the first one
} else {
completion?(.success(employees))
}
}
}
上面的其他一些小的修改:
我建議不要在這個for
循環中更新self.employeesConst
。 您通常不希望有一個異步過程更新模型對象。 您通常需要明確的職責分離,此函數負責檢索結果數組,但不應更新模型對象。 我會將數組構建為局部變量並將結果傳遞回閉包中。
如果閉包是可選的,而不是將其默認為{ _ in }
,您實際上會將其設為可選,並使用completion?(...)
語法調用它。 就個人而言,我不會將其設為可選(以便調用者負責更新模型),但我將其設為可選以匹配您的代碼示例。
我不知道在出現錯誤的情況下你想做什么,所以我只是構建了一個錯誤數組並在.failure
條件下提供了第一個錯誤。 再次,做你想做的。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.