You have to think about threading differently with async/await and the
.task
modifier. Although it begins on the main thread, each line of await code goes off and does something and when it completes it moves on to the next line. The actual thread that the awaited line runs on depends on its actor. If it is an async func inside the View struct then it is the main thread because that is marked with@MainActor
, thus the main thread will be blocked during the await line. If it is declared in a different struct or class then it should be a random background thread. You can also get a background thread within the.task
by using a child Task { } and even if the screen that is showing this View struct dissapears this child Task will still be cancelled same way.task
is, be careful not to useTask.detatched
because that will not be cancelled and could cause a crash. Another way to get a background thread in a View func is to mark it as nonisolated. Put a breakpoint inside this func to check it is on a background thread. Some classes or actors that do their async funcs in background threads useMainActor.run
orTask { @MainActor in ... }
to switch to the main thread, e.g. to update an@Published
. Now we don't need that any more with the new@Observable
fetcher classes in Xcode 15/iOS 17, e.g.
@Observable
class Test {
var counter = 0
func update() async {
do {
try await Task.sleep(for: .seconds(3))
counter+=1 // no longer need to wrap this in Task.mainActor { } to set this anymore
}
catch {
}
}
}
Source: malhal
on StackOverflow (link here)