I am trying to understand what the rule is when calling Task {... }
and, in that task, calling await
in terms of threads.
This example works:
struct TaskTestView: View {
let url = URL(string: "https://www.google.com")!
@State private var message = "Loading..."
var body: some View {
Text(message)
.task {
/// on MAIN THREAD
do {
var receivedLines = [String]()
for try await line in url.lines {
/// on MAIN THREAD
receivedLines.append(line)
message = "Received \(receivedLines.count) lines"
}
} catch {
message = "Failed to load"
}
}
}
}
This does not:
struct TaskTestView: View {
@StateObject var model = TaskTestViewModel()
var body: some View {
Text(model.message)
.task {
/// - here it is on main thread
await model.refresh()
/// - here it is NOT on main thread
print("after refresh: on main?")
}
}
}
class TaskTestViewModel:ObservableObject {
let url = URL(string: "https://www.google.com")!
@Published private(set) var message = "Loading..."
func refresh() async {
do {
var receivedLines = [String]() // on main thread
for try await line in url.lines {
receivedLines.append(line) // NOT on main thread
message = "Received \(receivedLines.count) lines"
}
} catch {
message = "Failed to load"
}
}
}
for try await line in url.lines {
in the first example?How would I know the answer to those questions without running the code and putting a breakpoint to inspect the thread I am on?
Obviously the main issue here is that I want to make sure I update @State variables on main so the view works properly, but, not having this concept clear makes it hard to design proper patterns.
There are not clear rules regarding on which thread the “continuation” will run. WWDC 2021 video Swift concurrency: Behind the scenes talks about continuations and how the thread after the suspension point might be different than the one before.
Trying to diagnose this with traditional debugging statements or breakpoints can be a frustrating exercise. I have encountered situations where the insertion of a few completely unrelated lines of debugging code changed the continuation threading behavior. Bottom line, unless you tell it otherwise, it is free to decide on which thread the continuation will run and it can sometimes defy expectations.
But, if you want to make sure that your observable object updates on the main thread, you can use the @MainActor
qualifier:
@MainActor
class TaskTestViewModel: ObservableObject {
...
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.