I'm new to Swift, coming from JS, and I've started to build an iOS app.
Initially I went down the road, using Promise Kit for the async stuff, as it seemed easier to me than other things I read about.
Regardless, in JS, I use the following pattern a lot:
async function doAyncFunction(item) {
try {
// do async call to fetch data using item
return Promise.resolve(data);
} catch (error) {
return Promise.reject(error);
}
}
const promises = items.map((item) => doAyncFunction(item));
const results = await Promise.all(promises);
And I ultimately got this working with Promise Kit with something like this:
func doManyAsyncRequests(userIds: [String], accessToken: String) -> Promise<Void> {
Promise { seal in
let promises = spotifyUserIds.map {
doSingleAsyncRequest(userId: $0.id, accessToken: accessToken) // this function returns a promise
}
when(fulfilled: promises).done { results in
print("Results: \(results)")
// process results
}.catch { error in
print("\(error)")
// handle error
}
}
}
Promise Kit's when
is similar to JavaScript's Promise.all()
in that once the promises are fulfilled, things are triggered to move along in the code.
As my learning curve is slow enough, I've decided to start coding for iOS 15 and use Swift async/await.
QUESTION: What Swift async/await pattern that will do the above, similar to Promise Kit's wait
and JavaScript's Promise.all()
?
Thanks.
UPDATE: Thanks to @workingdog, who helped me arrive at the solution below. I now gotta work on error handling, but that's a different topic for now.
func getAllThings(users: [User], accessToken: String) async -> [Thing] {
var allThings: [Thing] = []
await withTaskGroup(of: [Thing].self) { group in
for user in users {
group.async {
let userThings = await self.getUsersThings(
accessToken: accessToken,
displayName: user.displayName,
userId: user.id
)
return userThings
}
}
for await (userThings) in group {
allThings = allThings + userThings
}
}
return allThings
}
you are probably looking for withTaskGroup(...)
, such as:
func getAll() async {
await withTaskGroup(of: Void.self) { group in
await getPosts()
for post in posts {
group.async { await self.getCommentsFor(post: post) }
}
}
}
I have setup my own basic test to learn this, on github: https://github.com/workingDog/TestAsync
Edit:
This how I would return an array of posts with their comments. As you can see not as neat as getAll().
func getAllPosts() async -> [Post] {
// this is the tricky parameter bit, the tuple returned when you call group.async {...}
await withTaskGroup(of: (Int, [Comment]).self) { group in
// get all the posts
var thePosts: [Post] = await fetchThem()
// for each post get all the comments (concurrently)
for post in thePosts {
group.async {
let comments: [Comment] = await self.fetchThem(with: post)
return (post.id, comments)
}
}
// add the comments to their corresponding post (concurrently)
for await (postid, comnts) in group {
if let ndx = thePosts.firstIndex(where: {$0.id == postid}) {
thePosts[ndx].comments = comnts
}
}
// when all done, return all post with their comments all cooked up
return thePosts
}
}