Converting function value of type '@MainActor () -> ()' to '() -> Void' loses global actor 'MainActor'

Note:
This is not a SwiftUI question, I was more concerned about the error thrown.

Overview:

  • I am encountering the error Converting function value of type '@MainActor () -> ()' to '() -> Void' loses global actor 'MainActor'

Questions:

  1. Is this a bug in the SwiftUI API not accepting main actor closure or am I missing something?
  2. Is there a way to resolve the issue without having to place the contents of buttonAction inside the action closure (as shown in the second button)?

Environment

  • Build Setting: Strict Concurrency Checking > Complete
  • Xcode: Version 14.0 beta 3 (14A5270f)
  • macOS: 13.0 Beta (22A5295i)

Code:

@MainActor
class Model: ObservableObject {
    func f1() {}
}

struct ContentView: View {
    
    @StateObject private var model = Model()
    
    var body: some View {
        VStack {
            //Error: Converting function value of type '@MainActor () -> ()' to '() -> Void' loses global actor 'MainActor'
            Button(action: buttonAction) {
                Text("Tap me")
            }

            //Compiles fine
            Button {
                buttonAction()
            } label: {
                Text("Tap me")
            }
        }
    }
    
    func buttonAction() {
        model.f1()
    }
}
4 Likes

Submitted feedback FB10859400

What happens for this version?

Button(action: model.f1) {
	Text("Tap me")
}

Thanks @tera, the same error is thrown even for your code as well.

This happens only when the build setting Strict Concurrency Checking is set as Complete.

Based on Eliminate data races using Swift Concurrency - WWDC22 - Videos - Apple Developer (24:41 min) the build setting is mentioned.

Why does wrapping in another function fix the warning?

I'm seeing these warnings with Strict Concurrency Checking set to Minimal (and in Swift Packages).

1 Like

I've been seeing this issue as a warning with complete concurrency checking. The warning is pretty pervasive in SwiftUI Buttons. I think that SwiftUI needs to annotate the action parameter of the button initializer with @MainActor.

Filed FB12575405

5 Likes

Thanks a lot!!

I don't understand this, either. I filed FB13562256.

Even though Button.init does not take a @MainActor closure for the action parameter, the type of the closure is not Sendable and it's formed on the main actor (because body is @MainActor isolated), so it cannot escape the main actor. There's no potential for data races here.

This issue was resolved by [Concurrency] Do not allow actor isolation violations in function conversions to impact constraint solving. by hborla · Pull Request #68685 · apple/swift · GitHub which eliminated a bunch of false positives in the loss-of-global-actor function conversion diagnostics, and the fix is included in Swift 5.10.

3 Likes

Thanks a lot @hborla great to hear that is fixed in Swift 5.10, hopefully that would reduce a lot of the warnings helping to focus on the real warnings / issues.

Still seeing this in Xcode 15.3 Swift 5.10

            Button(action: addListTodo) {
                Label("", systemImage: EnvironmentVars.Buttons.addNote)
            }
    @MainActor
    private func addListTodo() {
        let createDataHandler = createDataHandler
        
        Task.detached {
...
        }
    }