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 TestApp: App {
@StateObject var audioPlaybackManager = AudioPlaybackManager() //This line is giving warning to me
@StateObject var dataController = DataController()
var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(audioPlaybackManager)
}
}
}
@MainActor class AudioPlaybackManager: ObservableObject {
...
}
Edit 2: This issue has been resolved with Swift 5.7 (release notes):
The diagnostic about non-isolated default-value expressions introduced for Swift 5.6 in the Xcode 13.3 release is no longer available. The proposed rule in SE-0327 wasn’t precise enough to avoid flagging an innocuous yet common pattern in SwiftUI code involving @StateObject properties and @MainActor. (88971160)
I assume a more precise rule will be introduced in Swift 6.0.
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
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")
]
}
...
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With