Update for iOS 15+ (Swift 5.5)
Since this question is not specific to Firebase or Alamofire, I wanted to add a more modern solution for Swift 5.5 and iOS 15+.
The answer below makes use of async / await
and Structured Concurrency
. The outlined approach below is what Apple recommends for concurrent requests now.
This answer will help users who used to queue URLSession
requests and wait for these to all complete.
Task group example code
If we have a dynamic number of requests (variable-sized array), the right tool is a Task
Group.
func fetchThumbnails(for ids: [String]) async throws -> [String: UIImage] {
var thumbnails: [String: UIImage] = [:]
try await withThrowingTaskGroup(of: (String, UIImage).self) { group in
for id in ids {
group.async {
return (id, try await fetchOneThumbnail(withID: id))
}
}
for try await (id, thumbnail) in group {
thumbnails[id] = thumbnail
}
}
return thumbnails
}
This also uses the for await
loop (AsyncSequence
) to wait for tasks to complete. for try await
is an example of a throwing AsyncSequence
. The throwing syntax is because the new asynchronous URLSession.data(for:)
family of methods are throwing functions.
async let
example code
This syntax works for a fixed number of requests.
let reqOne = urlRequest(for: keyOne) // Function that returns a unique URLRequest object for this key. i.e. different URLs or format.
async let (dataOne, _) = URLSession.shared.data(for: reqOne)
let reqTwo = urlRequest(for: keyTwo)
async let (dataTwo, _) = URLSession.shared.data(for: reqTwo)
guard let parsedData = parseInformation(from: try? await dataOne) else {
// Call function to parse image, text or content from data.
continue
}
// Act on parsed data if needed.
guard let parsedDataTwo = parseInformation(from: try? await dataTwo) else {
// Call function to parse image, text or content from data.
continue
}
// Act on the second requests parsed data if needed.
// Here, we know that the queued requests have all completed.
The syntax where I don't await
for the request to finish immediately is called async let
.
This code example could be adapted with variable-sized arrays, but isn't recommended by Apple. This is because async let
doesn't always allow the requests to be processed as soon as they arrive.
In addition, the code is easier to write, safer and avoids deadlocks.
Note
The exact syntax of TaskGroup
and async let
may change in the future. Currently, async / await
, and Structured Concurrency is getting feedback during its beta phase.
However, Apple has made it clear that the underlying mechanics of grouped and asynchronous tasks are mostly finalised (approved in Swift Evolution). An example of some syntax changes already includes the replacement of async {
with Task {
.