Wait until swift for loop with asynchronous network requests finishes executing
You can use dispatch groups to fire an asynchronous callback when all your requests finish.
Here's an example using dispatch groups to execute a callback asynchronously when multiple networking requests have all finished.
override func viewDidLoad() {
super.viewDidLoad()
let myGroup = DispatchGroup()
for i in 0 ..< 5 {
myGroup.enter()
Alamofire.request("https://httpbin.org/get", parameters: ["foo": "bar"]).responseJSON { response in
print("Finished request \(i)")
myGroup.leave()
}
}
myGroup.notify(queue: .main) {
print("Finished all requests.")
}
}
Output
Finished request 1
Finished request 0
Finished request 2
Finished request 3
Finished request 4
Finished all requests.
Swift async/await in for loop
The await id
means geting an id
element from the staticImageIDsURL.lines
is an asynchronous operation in itself.
for await id in staticImageIDsURL.lines
This operation has to complete before we enter for loop's body for that iteration. You should read AsyncSequence
docs to know more on this OR can watch Meet AsyncSequence WWDC 2021 session.
For each iteration, you are waiting for current operation to complete when you say this.
let thumbnail = await fetchThumbnail(for: id)
This line will suspend the function each time a new fetch call is initiated, so these thumbnails calls are guaranteed to be completed sequentially. These calls NEVER happen in parallel, first has to complete before second one is initiated.
Swift Make For Loop wait for Asynchronous function to end
Can't help you much because your code is such a pain to read. Use ObjectMapper
to reduce the level (and the effort) to map these fields. Anyhow, you can use dispatch_group
to synchronize the various async calls:
let group = dispatch_group_create()
for i in 1..<bids+1 {
// ...
dispatch_group_enter(group)
LOGOS_REF.child("\(phoneNumber)").dataWithMaxSize(3 * 1024 * 1024) {
// ...
dispatch_group_leave(group)
}
}
// Wait for all async calls to complete before proceeding
dispatch_group_wait(group, DISPATCH_TIME_FOREVER)
Each dispatch_group_enter()
should be balanced with a dispatch_group_leave()
when the async task is complete.
Swift: For Loop Wait Until Response And Return Value According To Response
It's possible to make this synchronous using a DispatchGroup, but this must never be called on the main queue.
// Blocking function. Must not be called on main queue!
func returnFirstKidIsGamer(kidsIds: [String]) -> String? {
let group = DispatchGroup()
var result: String? = nil
for kidID in kidsIds {
// Wait for previous request to finish before trying again.
group.enter()
DataService.instance.isKidAGamer(kidID: kidID) { (isGamer) in
if isGamer {
result = kidId
}
group.leave()
}
group.wait()
guard result == nil else { break }
}
return result
}
This calls group.enter()
before entering each loop, and group.leave()
when each step completes. It then waits for the step to complete before moving on.
This function is synchronous. It blocks the queue and so must never be called on the main queue. You have to move it to the background with something like this:
DispatchQueue.global(qos: .userInitiated).async {
let kidId = returnFirstKidIsGamer(kidsIds: [kids])
DispatchQueue.main.async {
doSomethingInTheUIWithValue(kidId)
}
}
Note that this returns String?
, not String
, since no id may be found.
As a rule, you shouldn't do this. You should use a query. But this is how you make asynchronous functions into synchronous functions when needed.
Swift: Asynchronous call in a for loop
How about using this structure?
let workGroup = dispatch_group_create()
for i in 0..<addressArray.count {
dispatch_group_enter(workGroup)
performGeoCoding({ successCallback :
dispatch_group_leave(workGroup)
})
}
dispatch_group_notify(workGroup, dispatch_get_main_queue()){
successCallback()
printAddressList()
}
There is very nice tutorial about dispatch_group here.
Execute for loop with optional asynchronous calls in order
You need to use DispatchSemaphore
to execute them in order
//MARK: getWishes
static func getWishes(dataSourceArray: [Wishlist], completion: @escaping (_ success: Bool, _ dataArray: [Wishlist]) -> Void){
var dataSourceArrayWithWishes = dataSourceArray
let db = Firestore.firestore()
let userID = Auth.auth().currentUser!.uid
let group = DispatchGroup()
let dispatchSemaphore = DispatchSemaphore(value: 0)
for list in dataSourceArray {
group.enter()
db.collection("users").document(userID).collection("wishlists").document(list.name).collection("wünsche").order(by: "wishCounter").getDocuments() { ( querySnapshot, error) in
defer {
print("leaving scope:\(String(describing: querySnapshot?.count))")
group.leave()
}
if let error = error {
print(error.localizedDescription)
completion(false, dataSourceArrayWithWishes)
} else {
// dispatch group to make sure completion only fires when for loop is finished
// append every Wish to array at wishIDX
let dispatchQueue = DispatchQueue(label: "taskQueue")
dispatchQueue.async {
for document in querySnapshot!.documents {
group.enter()
let documentData = document.data()
let name = documentData["name"] as? String ?? ""
let link = documentData["link"] as? String ?? ""
let price = documentData["price"] as? String ?? ""
let note = documentData["note"] as? String ?? ""
let imageUrlString = document["imageUrl"] as? String ?? ""
let wishIDX = documentData["wishlistIDX"] as? Int ?? 0
if let imageUrl = URL(string: imageUrlString) {
KingfisherManager.shared.retrieveImage(with: imageUrl, options: nil, progressBlock: nil, completionHandler: { result in
var image = UIImage()
switch result {
case .success(let abc):
image = abc.image
case .failure(let error):
print(error)
break
}
dataSourceArrayWithWishes[wishIDX].wishes.append(Wish(name: name, link: link, price: price, note: note, image: image, checkedStatus: false))
print("Signal for next one")
dispatchSemaphore.signal()
group.leave()
})
print("wait for next one")
dispatchSemaphore.wait()
} else {
dataSourceArrayWithWishes[wishIDX].wishes.append(Wish(name: name, link: link, price: price, note: note, image: nil, checkedStatus: false))
}
}
}
// for loop is finished -> fire completion
}
}
}
group.notify(queue: DispatchQueue.main) {
print("notify")
completion(true, dataSourceArrayWithWishes)
}
}
Swift: Async method into while loop
You can do this with a recursive function. I haven't tested this code but I think it could look a bit like this
func asyncRepeater(userId:Int, foundIdCompletion: (userId:Int)->()){
checkId(userId, completionHandler: { (success:Bool) -> () in
if success {
foundIdCompletion(userId:userId)
} else {
asyncRepeater(userId:Int.random(1...1000), completionHandler: completionHandler)
}
})
}
Swift Async let with loop
You can use a task group. See Tasks and Task Groups section of the The Swift Programming Language: Concurrency (which would appear to be where you got your example).
One can use withTaskGroup(of:returning:body:)
to create a task group to run tasks in parallel, but then collate all the results together at the end.
E.g. here is an example that creates child tasks that return a tuple of “name” and ”image”, and the group returns a combined dictionary of those name strings with their associated image values:
func downloadImages(names: [String]) async -> [String: UIImage] {
await withTaskGroup(
of: (String, UIImage).self,
returning: [String: UIImage].self
) { [self] group in
for name in names {
group.addTask { await (name, downloadPhoto(named: name)) }
}
var images: [String: UIImage] = [:]
for await result in group {
images[result.0] = result.1
}
return images
}
}
Or, more concisely:
func downloadImages(names: [String]) async -> [String: UIImage] {
await withTaskGroup(of: (String, UIImage).self) { [self] group in
for name in names {
group.addTask { await (name, downloadPhoto(named: name)) }
}
return await group.reduce(into: [:]) { $0[$1.0] = $1.1 }
}
}
They run in parallel:
But you can extract them from the dictionary of results:
let stooges = ["moe", "larry", "curly"]
let images = await downloadImages(names: stooges)
imageView1.image = images["moe"]
imageView2.image = images["larry"]
imageView3.image = images["curly"]
Or if you want an array sorted in the original order, just build an array from the dictionary:
func downloadImages(names: [String]) async -> [UIImage] {
await withTaskGroup(of: (String, UIImage).self) { [self] group in
for name in names {
group.addTask { await (name, downloadPhoto(named: name)) }
}
let dictionary = await group.reduce(into: [:]) { $0[$1.0] = $1.1 }
return names.compactMap { dictionary[$0] }
}
}
How to use Dispatch Group in a loop with asynchronous Function Swift?
Leave within the async block not outside of it ... and enter should be equal to leave so put it in defer statement to always execute before leaving the scope
func Promise_searchedDataFromDB(stringArray:[String]) {
for id in stringArray {
myGroup.enter()
collectionRef.getDocuments { (querySnapshot, error) in
defer{ myGroup.leave() }
if error != nil {
return
}
else {
guard let snapshot = querySnapshot else {return}
for document in snapshot.documents {
let myData = document.data()
if StaticVariable == true {
self.typeOfListing = myData["Type"] as? String ?? "Not Found"
self.charges = Int(myData["Charges"] as? String ?? "Not Found") ?? 0
self.nameOfListing = myData["Title"] as? String ?? "Not Found"
self.currency = myData["Currency"] as? String ?? "Not Found"
self.days = myData["Days"] as? String ?? "Not Found"
self.details = myData["Description"] as? String ?? "Not Found"
self.cityName = myData["City"] as? String ?? "Ghost"
let dataArray = CellComponents(image: UIImage(named: "b")!, typeOfListing: self.typeOfListing , charges: self.charges, rating: 4.1, nameOfListing: self.nameOfListing , cityName: self.cityName, detail: self.details, currency: self.currency, days: self.days)
self.ArrayToHoldSearchedListing.append(dataArray)
self.tableView.reloadData()
}
}
}
}
}
myGroup.notify(queue: .main) {
print("All done")
}
}
Related Topics
Linking Error When Building Parse in Xcode 7
How to Change the Appearance of the Datepicker in the Swiftui Framework to Only Months and Years
How to Unpack Multiple Levels of Nested JSON in Firebase Database
Swift Format Text Field When User Is Typing
Unexpectedly Found Nil While Unwrapping an Optional Value While Reading from Ds with Fromcstring
Value' Is Inaccessible Due to 'Internal' Protection Level
Can't Change Uiimageview Image in Function (Swift)
Structuring Data for Chat App in Firebase
How to Get All Text from a PDF in Swift
What Does the Underscore in a Function Declaration Do
Cannot Preview This File, App May Have Crashed -- Occurs When Inputting Specific Line of Code
Deleterowsatindexpaths Crashing
Swift: Nil Error When Using Self.Moc.Save() to Save in Core Data
Firestore Security Rules Breaking with Update Rule
How to Execute Multiplications And/Or Divisions in the Right Order