I cannot find a clean way to resolve 2 issues related to the fact that SwiftUI's View.overlay() is declared as nonisolated and i need the content to access MainActor properties from the view model:
@MainActor class FooVM: NSObject, ObservableObject {
enum Bar {
case bar
}
@Published var bar: Bar = .bar
}
struct FooView: View {
@StateObject var vm: FooVM
var body: some View { foo }
@ViewBuilder var foo: some View {
AnotherView()
.overlay {
switch vm.bar { // 1. WARNING
case .bar:
ProgressView()
.progressViewStyle(.circular) // 2. WARNING
}
}
}
}
1. WARNING
The first warning is Main actor-isolated property 'bar' can not be referenced from a nonisolated context; this is an error in the Swift 6 language mode
where with nonisolated context it refers to the declaration of .overlay(): @inlinable nonisolated public func overlay<V>(alignment: Alignment = .center, @ViewBuilder content: () -> V) -> some View where V : View
2. WARNING
An similar error but of which i have even less freedom to solve is that progressViewStye is also nonisolated whille .circular is declared MainActor
@MainActor @preconcurrency public static var circular: CircularProgressViewStyle { get }
WHAT I TRIED
Beside finding it a very ugly solution, I cannot wrap the switch case in a MainActor.assumeIsolated since i get errors when i have to return the view from the various cases, for example Branches have mismatching types 'some View' (result of 'Self.foregroundColor') and 'some View' (result of 'Self.foregroundColor') and the Views hierarchy is complex.
Also these are just a few of the crazy amount of warning that have popped since i updated to xcode 16, despite having a perfectly clean build with strict concurrency complete enabled in xcode 15.5
Your code compiles for me in Xcode 16.0 in Swift 6 language mode without any @preconcurrency import, so long I add var body to the view. Also, virtually every view modifier is nonisolated, so this does not relate to overlay specifically.
It could be that the diagnostics are failing you in this way specifically because of the lack of var body: the View protocol is @MainActor, and the modifiers, being synchronous, will inherit that, but without the body property the compiler can't confirm that the struct is indeed a View and thus doesn't designate it to the main actor. When I remove the View conformance, that is, write simply
struct FooView {
// ...
}
I'm getting the errors you've mentioned.
Now, it's unfortunate that the diagnostics for concurrency are firing before the diagnostics for protocol conformance, as it's obviously getting super cryptic and points into a wrong direction — but this doesn't happen for me in Xcode 16.0 16A242d. What is your exact version?
I'm sorry, the var body is present, it's a big project, i tried to strip down as much as possible for clarity here. I add it to the question.
The project is published, and it did compile and with no warning in xcode15, swift5.9 with strict concurrency enabled; and then i get all this warnings with xcode16 (which become errors if i enable swift6).
It sometimes happens to me that the compiler reports irrelevant errors on SwiftUI views because the expressions are very complex, and it apparently just decides to not diagnose all of them.
In any case, your code looks alright and should work/compile, there's no need to do anything with concurrency specifically, but as I mentioned, it's likely that the compiler doesn't understand that this is a View to begin with, presumably because there's a bug in body. When this happens, I typically would start replacing complex expressions with EmptyView() and comment out large stacks of modifiers until I see which of them fails. You also can put @MainActor over FooView to reassure the compiler and make it typecheck deeper.
You can try to move out from the closure (note that this will require that all passed parties conform to Sendable, which seems to be not a problem, at least with the snippet). It is a tricker for progress style, I have only managed to fix this with nonisolated(unsafe):
@ViewBuilder var foo: some View {
let bar = vm.bar
nonisolated(unsafe) let pvs: CircularProgressViewStyle = .circular
PhotosPicker(...)
}
I think this in fact should be safe, because I have no idea how the styling passing can be unsafe.
It should work – it specifically disables checks. Check if everything is in place:
import PhotosUI
@MainActor
class FooVM: NSObject, ObservableObject {
enum Bar {
case bar
}
@Published var bar: Bar = .bar
}
struct ViewTest: View {
@StateObject
private var vm: FooVM
var body: some View { EmptyView() }
@ViewBuilder
var foo: some View {
let bar = vm.bar
nonisolated(unsafe) let pvs: CircularProgressViewStyle = .circular
PhotosPicker(selection: .constant(nil),
matching: .images,
photoLibrary: .shared()) {
EmptyView()
.overlay(alignment: .center) {
if case .bar = bar {
EmptyView()
ProgressView()
.progressViewStyle(pvs)
}
}
}
}
}