Arrays 等待循环完成,DispatchGroup使用Swift从firebase获取数据
我正试图招募一批员工,并等待它完成 尝试使用Arrays 等待循环完成,DispatchGroup使用Swift从firebase获取数据,arrays,swift,firebase,swiftui,Arrays,Swift,Firebase,Swiftui,我正试图招募一批员工,并等待它完成 尝试使用DispatchGroup,但似乎什么也没发生 有一个二维数组,第一个在员工上方,第二个在他成功收到的文件上。然后,它应该将ShiftConst对象附加到请求的数组中 我希望它在完成时使用前面的完成块进行通知 我得到的结果是0和“未完成” 这是我的密码: @Published var employeesConst = [ShiftConst]() func getAllShiftConsts(employees: [Employee
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
,然后在调用完成时离开(在本例中,最好使用defer
,IMHO)
例如
func getAllShiftConsts(员工:[员工],完成:((结果)->无效)?=nil){
let group=DispatchGroup()
var员工:[ShiftConst]=[]
变量错误:[错误]=[]
员工中的员工{
let ref=session.db.collection(CollectionRef.users.rawValue).document(employee.uid!).collection(CollectionRef.shiftConsts.rawValue)
group.enter()
ref.getDocuments{qSnapshot,错误在
延迟{group.leave()}
守卫让qSnapshot=qSnapshot,!qSnapshot.isEmpty else{return}
对于qSnapshot.documents{
做{
let data=try document.decode(as:ShiftConst.self)
employees.append(数据)
}捕捉错误{
错误。追加(错误)
}
}
}
}
通知组(队列:.main){
如果let error=errors.first{
完成?(.failure(error))//我不知道你想对所有错误做什么,所以我就抓住第一个
}否则{
完成?(.成功(员工))
}
}
}
上述内容中的一些其他小修改:
for
循环中更新self.employeesConst
。您通常不希望有一个异步进程来更新模型对象。您通常需要明确的职责分离,其中此函数负责检索结果数组,但不应更新模型对象。我将数组构建为局部变量,并将结果传递回闭包中
{in}
,那么实际上可以将其设置为可选的,使用完成(…)
语法调用它。就个人而言,我不会将其设置为可选的(以便调用方负责更新模型),但我将其设置为可选的,以匹配您的代码示例
.failure
条件下提供了第一个。再说一遍,你想做什么就做什么
但是这个方法不允许我在抓取完成时使用完成块。为什么在这个用例中需要完成?使用上面的方法,用户会在一些数据出现时立即在UI中得到一些更新,而不是在完成时的最后一刻。我已经添加了我想应用于这个问题的工作。我觉得这是完成这项任务所需的唯一途径。当然可能有更好的方法,但现在我想这就是我能做到的。非常感谢你的解决方案和解释!现在可以了!快速提问,是否不建议使用此调度组方法?因为它阻塞了主线程。有没有更好的方法来实现这个目标?@ElaiZuberman因为你使用的是
notify
,所以你没有阻止任何东西。当然,我们还可以提出其他改进建议,但我在上面的评论中谈到了我所认为的实质性问题。
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))
}
}
}