multithreadingswiftuiconcurrencymainactor

SwiftUI @MainActor loses global actor


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.


Solution

  • See my comment on the question about whether or not the @MainActor strategy is the right way to address an underlying issue, but to directly address your compilation error, you can use this syntax, which compiles fine:

    .onChange(of: viewModel.username) { viewModel.editingChanged($0) }