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 {
...
}
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
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")
]
}
...
}