Reputation: 330
func getAll(completion: (([Foods]?, Error?) -> Void)?){
var foodWithEname: [Foods]?
let food: Food? = Food()
var sortedFood: [Foods]?
food?.getFoods(){ (foodsArray, error) in
sortedFood = foodsArray?.sorted{ (left, right) in
if let lh = left.hits {
if let rh = right.hits {
return left.hits! > right.hits!
}
return true
} else {
if let rh = right.hits {
return false
}
return false
}
}
for fa in foodsArray! {
var f = Foods(fid: fa.fid, fname: fa.fname, hits: fa.hits, addr: fa.addr, ename: nil)
food?.getEffectsById(fa.fid){ (effectsArray, error) in
//foodWithEname?.ename = effectsArray
print ("11111")
DispatchQueue.global().async {
f.ename = effectsArray
print ("2222")
foodWithEname?.append(f)
}
}
DispatchQueue.global().async (execute: {
print ("00000")
})
}
print("3333")
completion?(foodWithEname, nil)
}
print ("4444")
}
I need to return foodWithEname Value to the main view, but it returned without value because of thread flow...
I don't know how to control flow of closures.
I called the function in MainView:
let food: Food? = Food()
food?.getAll(){ (foodsArray, error) in
}
And results: 4444 3333 00000 00000 00000 00000 00000 00000 00000 00000 00000 00000 00000 00000 00000 00000 00000 00000 00000 00000 00000 00000 00000 00000 00000 00000 00000 00000 11111 2222 11111 2222 11111 2222 11111 2222 11111 2222 11111 2222 11111 2222 11111 2222 11111 2222 11111 2222 11111 2222 11111 2222 11111 2222 11111 2222 11111 2222 11111 2222 11111 2222 11111 2222 11111 2222 11111 2222 11111 2222 11111 2222 11111 2222 11111 2222 11111 2222 11111 2222
I want results like below:
2222 00000 11111 2222 00000 11111 2222 00000 ... 3333
And Array values should be in foodWithEname when completion?(foodWithEname, nil) is executed.
Please help to solve this problem, Thank you!
Upvotes: 0
Views: 694
Reputation: 2902
In your second loop you're sending multiple asynchronous calls in a loop, and while the result is waiting you called the completion handler so the result is empty.
You need to call these asynchronous calls in a dispatch group and when all the executions are completed return the result to completion handler.
Do something like this in second loop:
// Create dispatch group
let group = DispatchGroup()
for fa in foodsArray! {
var f = Foods(fid: fa.fid, fname: fa.fname, hits: fa.hits, addr: fa.addr, ename: nil)
// Enter in group just before the call
group.enter()
food?.getEffectsById(fa.fid){ (effectsArray, error) in
//foodWithEname?.ename = effectsArray
print ("11111")
DispatchQueue.global().async {
f.ename = effectsArray
print ("2222")
// Leave the group when task is completed
group.leave()
foodWithEname?.append(f)
}
}
DispatchQueue.global().async (execute: {
print ("00000")
})
}
print("3333")
// Wait for all tasks to complete, and call the completion handler
group.notify(queue: DispatchQueue.main) {
completion?(foodWithEname, nil)
}
Upvotes: 1
Reputation: 100523
You need DispatchGroup
to handle many asynchronous calls
func getAll(completion: (([Foods]?, Error?) -> Void)?){
var foodWithEname: [Foods]?
let food: Food? = Food()
var sortedFood: [Foods]?
food?.getFoods(){ (foodsArray, error) in
sortedFood = foodsArray?.sorted{ (left, right) in
if let lh = left.hits {
if let rh = right.hits {
return left.hits! > right.hits!
}
return true
} else {
if let rh = right.hits {
return false
}
return false
}
}
let dispatchGroup = DispatchGroup()
for fa in foodsArray! {
var f = Foods(fid: fa.fid, fname: fa.fname, hits: fa.hits, addr: fa.addr, ename: nil)
dispatchGroup.enter()
food?.getEffectsById(fa.fid){ (effectsArray, error) in
f.ename = effectsArray
foodWithEname?.append(f)
dispatchGroup.leave()
}
}
dispatchGroup.notify(queue: .main) {
completion?(foodWithEname, nil)
}
}
}
Upvotes: 1