'How do I fix "Expression requiring global actor 'MainActor' cannot appear in default-value expression of property '_audioPlaybackManager'"?

I have created a class called "AudioPlaybackManager" which is a MainActor class as a StateObject when the app starts up. Im getting this warning. How do I fix this?

@main
struct Pro_Music_2App: App {
    @StateObject var audioPlaybackManager = AudioPlaybackManager() //This line is giving warning to me
    
    @StateObject var dataController = DataController()
    
    var body: some Scene {
        WindowGroup {
            ContentView()
                .environmentObject(audioPlaybackManager.manager)
                .environmentObject(audioPlaybackManager)
                .environmentObject(audioPlaybackManager.manager.playlistManager)
                .environmentObject(audioPlaybackManager.musicPlayerViewManager)
                .environmentObject(audioPlaybackManager.scrubberViewModel)
                .environment(\.managedObjectContext, dataController.container.viewContext)
                
        }
    }
}

@MainActor class AudioPlaybackManager: ObservableObject {
    ...
}


Solution 1:[1]

Edit: As Michael Long mentioned in the comments and I already guessed, this probably won’t be the final solution. Instead, the problem will probably be resolved in the compiler and the existing syntax will work without warning.

Depending on how strict you are about warnings, you might also just accept the warning for now and not change your code. Or you apply the fix below and annotate it with a TODO: clean up syntax right away :)


The suggested solution is to provide no default value to the property and set its value in the initializer instead.

See in the Xcode release notes under Swift 5.6 -> Resolved Issues.

For your code the fix is like this:

@main
struct Pro_Music_2App: App {
    @StateObject var audioPlaybackManager: AudioPlaybackManager
    
    @StateObject var dataController = DataController()

    init() {
        self._audioPlaybackManager = StateObject(wrappedValue: AudioPlaybackManager())
    }    

    var body: some Scene {
        WindowGroup {
            ContentView()
                .environmentObject(audioPlaybackManager.manager)
                .environmentObject(audioPlaybackManager)
                .environmentObject(audioPlaybackManager.manager.playlistManager)
                .environmentObject(audioPlaybackManager.musicPlayerViewManager)
                .environmentObject(audioPlaybackManager.scrubberViewModel)
                .environment(\.managedObjectContext, dataController.container.viewContext)
                
        }
    }
}

@MainActor class AudioPlaybackManager: ObservableObject {
    ...
}

The underlying problem is that initializers are not async. Therefore, you cannot hop between different actors. And since the compiler synthesizes an initializer for default-values, this also affects default-values. That is, if e.g. your DataController was annotated with a different global actor than @MainActor, then the initializer had to hop between the @MainActor and whatever actor you're using on DataController. That, however, is impossible because init cannot be async.

I guess it would be difficult to implement this single-actor check for stored properties' default-values in the compiler, therefore they just decided to not allow actor protected default-values in this context. As a result, we have to write the initializer ourselves.

For reference see: https://github.com/apple/swift-evolution/blob/main/proposals/0327-actor-initializers.md#stored-property-isolation

Solution 2:[2]

I had the same issue and I have fixed it in this way:

@MainActor
class MultipleDownloadsViewModel: NSObject, ObservableObject {
    @Published var downloads: [DownloadModel]

    // Init of properties in actor: see https://stackoverflow.com/questions/71396296/how-do-i-fix-expression-requiring-global-actor-mainactor-cannot-appear-in-def/71412877#71412877
    @MainActor override init() {
        downloads = [
            DownloadModel(fileToDownload: "https://speed.hetzner.de/100MB.bin"),
            DownloadModel(fileToDownload: "https://speed.hetzner.de/1GB.bin"),
            DownloadModel(fileToDownload: "https://speed.hetzner.de/10GB.bin")
        ]
    }

    ...
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Damiano Curia