You requested:
Can I nonetheless use
DispatchQueue.major.async
?
If you’re in an async
methodology and wish to dispatch one thing to the principle queue, probably the most literal equal could be:
MainActor.run { ... }
However it’s extra prudent to easily mark the tactic (or its class) with @MainActor
. Not solely will this be certain that it runs it on the principle thread, however you get compile-time warnings in the event you try to name it from the incorrect actor.
So, in case your view mannequin is marked with @MainActor
, the guide working of the duty on the MainActor
turns into pointless. That is very true when coping with printed properties of an noticed object.
For instance, think about:
@MainActor
class ViewModel: ObservableObject {
@Printed var values: [Int] = []
func fetchData() async {
let foo = await ...
values = foo.values
}
}
After which
struct ContentView: View {
@ObservedObject var viewModel = ViewModel()
var physique: some View {
Record {
...
}
.refreshable {
await viewModel.fetchData()
}
}
}
(Observe, I made fetchData
an async
methodology and await
it inside refreshable
in order that the spinner precisely displays when the async
course of is working.)
See WWDC 2021 video Swift concurrency: Replace a pattern app. That’s admittedly illustrating the transition of a UIKit app, however contains examples of @MainActor
and MainActor.run
.
Observe, whereas @MainActor
, largely eliminates the necessity for MainActor.run { … }
, there are nonetheless some situations the place you would possibly use this run
sample. Particularly, if you’re on another actor and wish to run, for instance, three separate @MainActor
features in succession on the principle thread, you possibly can wrap the collection of them inside a single MainActor.run { … }
block, thereby working all three with a single dispatch to the principle actor, moderately than three separate calls.
Above, I centered on the salient parts, however right here is my full MCVE:
struct ContentView: View {
@ObservedObject var viewModel = ViewModel()
var physique: some View {
Record {
ForEach(viewModel.values, id: .self) { worth in
Textual content("(worth)")
}
}
.refreshable {
await viewModel.fetchData()
}
}
}
struct Foo: Decodable{
let json: [Int]
}
@MainActor
class ViewModel: ObservableObject {
@Printed var values: [Int] = []
func fetchData() async {
do {
let foo = attempt await object(Foo.self, for: request)
values = foo.json
} catch {
print(error)
}
}
func object<T: Decodable>(_ kind: T.Sort, for request: URLRequest) async throws -> T {
let (knowledge, response) = attempt await URLSession.shared.knowledge(for: request)
guard let response = response as? HTTPURLResponse else {
throw URLError(.badServerResponse)
}
guard 200 ... 299 ~= response.statusCode else {
throw ApiError.failure(response.statusCode, knowledge)
}
return attempt JSONDecoder().decode(T.self, from: knowledge)
}
var request: URLRequest = {
let url = URL(string: "https://httpbin.org/something")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.httpBody = "[1,2,3,4,5]".knowledge(utilizing: .utf8)
request.addValue("software/json", forHTTPHeaderField: "Content material-Sort")
request.addValue("software/json", forHTTPHeaderField: "Settle for")
return request
}()
}
enum ApiError: Error {
case failure(Int, Information)
}