I've got an ObservableObject
class that I am calling to via @StateObject
from my view. Inside that class is a function and the entire class is marked @MainActor
. In my view there is a TextField
with an onChange
modifier that has a perform calling to that function via viewModel.funcname
.
Xcode is complaining with an error of:
Converting function value of type '@MainActor (String) -> Void' to '(String) -> Void' loses global actor 'MainActor'
I've been researching this for hours now and have found little to nothing. Even Apple's own docs say to simply use await but that doesn't work, at least not the many ways I've tried it.
This is the code within my view:
TextField("", text: $viewModel.username)
.onChange(of: viewModel.username, perform: viewModel.editingChanged) // This is where the error occurs
This is the function in my class (remember that the entire class is marked @MainActor
):
func editingChanged(_ value: String) {
let jsonFetchUserExistsURL = URL(string: "https://blah.com")
let jsonFetchUserExistsTask = jsonFetch(jsonFetchUserExistsURL, defaultValue: [UserExists]())
guard isNetworkActive else { loadingAlert = true; return }
Task {
jsonFetchUserExistsTask.sink (receiveCompletion: { completion in
switch completion {
case .failure:
self.loadingState = .failed
case .finished:
self.checkUser()
}
},
receiveValue: { loadedUserExists in
self.userExists = loadedUserExists
}).store(in: &requests)
}
}
}
I have tried modifying the onChange
to read as follows:
.onChange(of: viewModel.username, perform: await viewModel.editingChanged)
.onChange(of: viewModel.username, perform: Task { await viewModel.editingChanged })
.onChange(of: viewModel.username, perform: Task.detached { await viewModel.editingChanged })
.onChange(of: viewModel.username, perform: DispatchQueue.main.async { viewModel.editingChanged })
The entire reason I marked the class @MainActor
is because Xcode complained that the function wasn't running on the main thread. It compiled but froze after it complained a few times in the console.
Nothing I've tried seems to change anything. Hoping someone can shed some light on this.
@MainActor
is because Xcode complained that the function wasn't running on the main thread" IfjsonFetchUserExistsTask
is an asynchronous operation (which I'm guessing it is) and isn't returning on the main thread, it seems like rather than going through the dance of trying to annotate with@MainActor
and then launch aTask
directly withineditingChanged
, which then callsjsonFetch
on another thread, you should just use.receive(on: RunLoop.main)
before yoursink
... – Intermigration