How can I call in Swift 6 a normal async function from a MainActor function? I do not want to execute URLRequests
on the MainThread
I want to switch from the Main Thread to an async Thread but I have no idea how to do it…
This code doesn’t work and throws the following error: Sending 'self.service' risks causing data races
import SwiftUI
struct CustomView: View {
private let viewModel: CustomViewModel = CustomViewModel()
var body: some View {
Text("HELLO")
.task {
Task {
await viewModel.getDataFromBackend()
}
}
}
}
@Observable
final class CustomViewModel {
private let service: CustomService = DefaultCustomService()
@MainActor func getDataFromBackend() async {
// Updating UI on Main-Thread
// This shouldn't run on the Main Thread!
await service.provideBackendData()
}
}
protocol CustomService {
func provideBackendData() async
}
struct DefaultCustomService: CustomService {
func provideBackendData() async {
// Async Stuff which shouldn't run on Main-Thread
}
}
2
Answers
Currently your async func is an
@Observable
class which is for model data not for services, i.e. its designed to hold shared mutable state which is not usually something a service does. To fix your data races warning your async func can either be static (i.e. no risk of shared mutatble state) or in a sendable struct. To use async/await in SwiftUI with Swift 6 with a mockable service try something like this:You can just add
To remove the error.
In order to safely make something
Sendable
you should meet all of these official requirementshttps://developer.apple.com/documentation/swift/sendable
Unofficially any reference types should also be marked
final
so the user cant create unsafe mutating children.You can also use
actor
andglobalActor
to make somethingSendable
.