Thank you so much Nick, your videos are great and you are the best programming language teacher I've ever seen (I have MS degree in AI😂). you always explain every single point that is vague for me during my learning😍 I am eagerly waiting for more videos of you, though I know that takes so much time for you to prepare them. I really appreciate you and take my hats off to you😍😍😍😍
Amazing, thank you so much Nick!!! I love your way of teaching, the repetition that you provide and how every video builds upon the other. With all that repetition we can become experts on these topics without even realizing it. You are a great teacher 👍🏻
Nick, you are amazing. Can't stop watching your bootcamps. I must say sometimes it is weird to spend half a video doing exactly the same stuff I was doing 5 minutes ago in the last video. I can't recall content to be repeated so often in previous courses. If I'd want to rewind Async Let I'd open previous video and do it again. I'm not saying you should stop doing stuff the way you do, but I decided to drop a piece of a honest feedback here 😇
I just wanted to say that your video series is awesome! The content is top-notch and the quality is Amazon-level good! I really appreciate you taking the time to create this. Thank you so much!
Thank you so much Nick for this wonderful series of videos ! Your teaching style is awesome. Even the most complex topics (like actor vs class vs struct, etc…), become cristal clear with your explanations and sample codes. There is a lot of videos about Swift and SwiftUI on TH-cam, but yours are by far my favorites one. Moreover I wish to mention that your diction, your speech are excellent. As a French, English is not my native language, therefore some voices like the one of the great Paul Hudson (Hacking with Swift) require me to listen to very carefully in order to understand what is said. But with you, I can understand effortlessly every single word and so, I can focus all my attention only on the subject of the video. I also have a question: > Suppose I have the following code to process the data associated with a bunch of URLs: class DataProcessor { // … func processData(for urls: [URL]) async throws { let dataManager = DataManager() try await withThrowingTaskGroup(of: Data.self) { group in for url in urls { group.addTask { return try await dataManager.getInfo(from: url) } for try await data in group { // Do something with data } } } } } class DataManager { // ... func getInfo(from url: URL) async throws -> Data { // return some Data } } If I pass to processData(for urls: [URL]) an array of a few URLs, for instance 16, it works fine. But with an array of 800 URLs it fails because too much tasks will be spawned simultaneously. To limit the number of tasks added to the TaskGroup over time, I end up with the code below. It consists of 3 parts: 1. It starts by adding 8 tasks to the TaskGroup (8 is the maximum allowed in this example). 2. Then, while there is more URL to process, as soon as a task ends, the data is collected and a new task is added to the TaskGroup. 3. Finally, collects the last data (waits until all tasks has finished) This ensure that no more than 8 tasks are run simultaneously. I wonder if there is a better approach. class DataProcessor { // … func processData(for urls: [URL]) async throws { let dataManager = DataManager() let maxConcurentTaskCount = 8 try await withThrowingTaskGroup(of: Data.self) { group in func addTask(urlIndex: Int) async throws { let url = urls[urlIndex] group.addTask { return try await dataManager.getInfo(from: url) } } func process(_ data: Data) { // Do something with data } var i = 0 // Starts the first maxConcurentTaskCount tasks for _ in 0..
Thank you for your overall engagement. Is it stunning. But one problem in your sample could be the random order of the return. In most real live apps, you would like to have the exact Image for the given url. Returning a tuple (image, used url) could help with that. But anyway > great stuff
Yea that’s a great point. We could always sort the final array to the same order as they were requested, before returning out of the function. Fetching UIImage is probably not a good example to do that with though 😭
Two notes: 1. It is worth it to mention that TaskGroup may return nothing, and in this case childtask result, i.e. 'of:' will be Void.self. 2. Not explained why do we need second loop? Could we just add content from the second loop into the first one - in the closure of group.addTask? And next, does the task start running when we add it with group.addTask?
Awesome video. Need to clarify and check on one aspect. This works, but tasks are spawned concurrently, _all_ of them finish downloading, then only parent View is updated. Parent view is the one that would use the "TaskGroupBootcamp()" in its "var body: some View". This would be wrong if set of images is larger, say 200, isn't it!? It should act somewhat similar to - as user is scrolling (in say LazyVStack), network calls are made to fetch more images and populate parent View. How would that be accomplished - what would be solution scheme. What is needed is: as downloads are happening and finishing, it should update the parent View. Downloaded images should show up in LazyVStack, and those which are not yet downloaded should show with '_downloading_' or say '?' mark.
You kept the strong self reference. I tried to turn it into a [weak self], but then Swift complains that calling self in an async context is not allowed in Swift 6. How can this be resolved?
Kindly asking why you are using classes for service layer ? Is it just you dont care or there is some value in it ? I didnt get any issue with structs since its not holding a variable only contains functions.
If you don’t need a class / instance for your service layer than you can definitely use a struct. I’m doing it here because later in this course we will use this same pattern but with more complex classes, which we will convert to actors to make them thread safe!
Did you change the Root View of the App struct to the TaskGroupBootccampView? Would be same exact result but wouldn't technically be using the code from this video if AsyncLetView was forgotten to be switched.
It is kind of strange how different async let vs taskgroup syntax is for basically the same thing. One would expect one thing to be like a syntactical extension of another. Or maybe I just don’t understand at all what’s going on there.
@@SwiftfulThinking Interesting, I thought maybe async let (or rather awaiting multiple things declared as async let) may create a TaskGroup under the hood. But maybe I’m wrong, if TaskGroup is more powerful/requires more resources…
you just write "try await" for all the lets. Like this ... `let (image1, image2, image3, image4) = try await ( fetchImage1, fetchImage2, fetchImage3, fetchImage4)`
You are literally THE BEST TEACHER of Swift and SwiftUI out there! 🤙
Thank you so much Nick, your videos are great and you are the best programming language teacher I've ever seen (I have MS degree in AI😂). you always explain every single point that is vague for me during my learning😍 I am eagerly waiting for more videos of you, though I know that takes so much time for you to prepare them. I really appreciate you and take my hats off to you😍😍😍😍
Haha thanks for the comment! I’m hoping to start making vids again soon 🤓
@@SwiftfulThinking Yeah! We miss you Nick!☺
Amazing, thank you so much Nick!!! I love your way of teaching, the repetition that you provide and how every video builds upon the other. With all that repetition we can become experts on these topics without even realizing it. You are a great teacher 👍🏻
Good stuff as always Nick. You're at 11K subscribers now... it's just gonna keep growing
Nick, you are amazing. Can't stop watching your bootcamps.
I must say sometimes it is weird to spend half a video doing exactly the same stuff I was doing 5 minutes ago in the last video. I can't recall content to be repeated so often in previous courses.
If I'd want to rewind Async Let I'd open previous video and do it again.
I'm not saying you should stop doing stuff the way you do, but I decided to drop a piece of a honest feedback here 😇
its in case someone isnt following the video in order and just clicked here to learn about TaskGroups
I just wanted to say that your video series is awesome! The content is top-notch and the quality is Amazon-level good! I really appreciate you taking the time to create this. Thank you so much!
As always, simply amazing! Thank you. I really need more coding playtime. I really want to set the cement on these concepts.
No doubt he is best.
These videos have been really useful. Plugging all the gaps in Apple's WWDC vids and documentation. Thank you.
Thank you so much Nick for this wonderful series of videos !
Your teaching style is awesome. Even the most complex topics (like actor vs class vs struct, etc…), become cristal clear with your explanations and sample codes.
There is a lot of videos about Swift and SwiftUI on TH-cam, but yours are by far my favorites one.
Moreover I wish to mention that your diction, your speech are excellent. As a French, English is not my native language, therefore some voices like the one of the great Paul Hudson (Hacking with Swift) require me to listen to very carefully in order to understand what is said. But with you, I can understand effortlessly every single word and so, I can focus all my attention only on the subject of the video.
I also have a question:
>
Suppose I have the following code to process the data associated with a bunch of URLs:
class DataProcessor {
// …
func processData(for urls: [URL]) async throws {
let dataManager = DataManager()
try await withThrowingTaskGroup(of: Data.self) { group in
for url in urls {
group.addTask {
return try await dataManager.getInfo(from: url)
}
for try await data in group {
// Do something with data
}
}
}
}
}
class DataManager {
// ...
func getInfo(from url: URL) async throws -> Data {
// return some Data
}
}
If I pass to processData(for urls: [URL]) an array of a few URLs, for instance 16, it works fine. But with an array of 800 URLs it fails because too much tasks will be spawned simultaneously.
To limit the number of tasks added to the TaskGroup over time, I end up with the code below.
It consists of 3 parts:
1. It starts by adding 8 tasks to the TaskGroup (8 is the maximum allowed in this example).
2. Then, while there is more URL to process, as soon as a task ends, the data is collected and a new task is added to the TaskGroup.
3. Finally, collects the last data (waits until all tasks has finished)
This ensure that no more than 8 tasks are run simultaneously.
I wonder if there is a better approach.
class DataProcessor {
// …
func processData(for urls: [URL]) async throws {
let dataManager = DataManager()
let maxConcurentTaskCount = 8
try await withThrowingTaskGroup(of: Data.self) { group in
func addTask(urlIndex: Int) async throws {
let url = urls[urlIndex]
group.addTask {
return try await dataManager.getInfo(from: url)
}
}
func process(_ data: Data) {
// Do something with data
}
var i = 0
// Starts the first maxConcurentTaskCount tasks
for _ in 0..
Thanks Nick! Great job teaching as always!
Thank you, Nick, for this wonderful playlist.
Fantastic explanation!
Wonderful video as always~~
It's hard to understand from official video to me....
But you explain so clear!!
Thanks !!
Awesome video, I have learnt a lot from your videos. Thanks
All of your videos are great! Thank you so much. 🎉
Thanks!
Nick, could you tell me please, how to turn on displaying error for capture self? thank you
Declare your TaskGroupBootcampDataManager that way:
class TaskGroupBootcampDataManager: @unchecked Sendable {
Thank you! Understandable, easy explanation. Saved my day. :)
Amazing, Keep posting
Thank you for your overall engagement. Is it stunning. But one problem in your sample could be the random order of the return. In most real live apps, you would like to have the exact Image for the given url. Returning a tuple (image, used url) could help with that. But anyway > great stuff
Yea that’s a great point. We could always sort the final array to the same order as they were requested, before returning out of the function. Fetching UIImage is probably not a good example to do that with though 😭
one question: for try await image in group, does it run in parallel or sequentially if we're inside the group? I didn't have time to test it thanks
They run in parallel
Always great explanation and contents ❤👍🏿👏🏿🔥
Two notes: 1. It is worth it to mention that TaskGroup may return nothing, and in this case childtask result, i.e. 'of:' will be Void.self. 2. Not explained why do we need second loop? Could we just add content from the second loop into the first one - in the closure of group.addTask? And next, does the task start running when we add it with group.addTask?
Awesome video. Need to clarify and check on one aspect. This works, but tasks are spawned concurrently, _all_ of them finish downloading, then only parent View is updated. Parent view is the one that would use the "TaskGroupBootcamp()" in its "var body: some View". This would be wrong if set of images is larger, say 200, isn't it!? It should act somewhat similar to - as user is scrolling (in say LazyVStack), network calls are made to fetch more images and populate parent View. How would that be accomplished - what would be solution scheme. What is needed is: as downloads are happening and finishing, it should update the parent View. Downloaded images should show up in LazyVStack, and those which are not yet downloaded should show with '_downloading_' or say '?' mark.
Can the task results be ordered after the TaskGroup finishes? Is there any kind of identifier/sort index?
No, but you can manually sort the array before returning (ie. Use the parameter input to sort the output).
Btw, can we have heterogeneous (with different return types) tasks in a task group?
You kept the strong self reference. I tried to turn it into a [weak self], but then Swift complains that calling self in an async context is not allowed in Swift 6. How can this be resolved?
Awesome video ✅🎉 Thanks!
Kindly asking why you are using classes for service layer ? Is it just you dont care or there is some value in it ? I didnt get any issue with structs since its not holding a variable only contains functions.
If you don’t need a class / instance for your service layer than you can definitely use a struct. I’m doing it here because later in this course we will use this same pattern but with more complex classes, which we will convert to actors to make them thread safe!
@@SwiftfulThinking As usual perfect video perfect answer! Cant wait to see rest of it Nick.
Did you change the Root View of the App struct to the TaskGroupBootccampView? Would be same exact result but wouldn't technically be using the code from this video if AsyncLetView was forgotten to be switched.
What tool or keyboard shortcut do you use to copy and paste so quickly, thanks
Thank you! 🙌
The reserve capacity method is good practice for space complexity
It is kind of strange how different async let vs taskgroup syntax is for basically the same thing. One would expect one thing to be like a syntactical extension of another. Or maybe I just don’t understand at all what’s going on there.
I agree, but Async Let is multiple concurrent actions within 1 Task, while TaskGroup is multiple concurrent Tasks
@@SwiftfulThinking Interesting, I thought maybe async let (or rather awaiting multiple things declared as async let) may create a TaskGroup under the hood. But maybe I’m wrong, if TaskGroup is more powerful/requires more resources…
Please make video of payment subscription & submitting app on play store!
Yes we all are waiting for it .
Good stuff, however it doesn't update the view on the main thread when running in simulator, just FYI
Amazing! Can the groupTask type be void?
Yes, you should have it as (of: Void.self)
Nick, you're not on the ground floor. Bit Higher, like 2-4. Let me know If I'm correct.
Is there any thing that revolves the first await and discard the rest. Something like Promise.any in JavaScript.
I know I'm jumping ahead here a bit but should the data store be an actor?
It definitely can be
Thanksss
you just write "try await" for all the lets. Like this ... `let (image1, image2, image3, image4) = try await ( fetchImage1, fetchImage2, fetchImage3, fetchImage4)`