Hit three related compile failures when running previewsmcp snapshot against #Preview blocks that compile cleanly under swift build against the same package. In all three the error is in PreviewsMCP's generated bridge module (<module>_N.swift in /var/folders/.../T/previewsmcp/<uuid>/), not in the user's source. All three look like the same root cause: the bridge file generates @_cdecl C-bridge functions (createPreviewView, previewBodyKind) that are nonisolated, and the user's #Preview body inherits that nonisolated context — even though SwiftUI previews always execute on the main actor at runtime (UIHostingController, UIWindow, didFinishLaunching).
Failure 1 — @Previewable @State causes detect probe overload ambiguity
Repro
#Preview("Recording") {
@Previewable @State var state: MyObservableState = {
let s = MyObservableState()
s.someBool = true
return s
}()
ZStack {
Color(.systemBackground).ignoresSafeArea()
SomeView(state: state)
}
}
Error
error: ambiguous use of 'detect'
return __PreviewBodyKindProbe.detect {
@Previewable @State var state: MyObservableState = {
^- error: ambiguous use of 'detect'
The generated previewBodyKind() function wraps the user's #Preview body in __PreviewBodyKindProbe.detect { ... } to determine whether the body returns a View / UIView / UIViewController. The @Previewable @State var ... declaration at the top of the closure prevents the Swift compiler from inferring the closure's return type, so all three detect overloads remain candidates and resolution fails.
Workaround
Replace @Previewable @State with a regular let and put return on the view expression. (Loses Xcode Canvas interactivity, but PreviewsMCP only needs a static render.)
#Preview("Recording") {
let state = MyObservableState()
state.someBool = true
return ZStack { ... }
}
Failure 2 — IIFE state-init pattern loses MainActor isolation in the bridge module
Repro
After applying Workaround 1 with the IIFE pattern still in place:
#Preview("Recording") {
let state: MyObservableState = {
let s = MyObservableState()
s.someBool = true
return s
}()
return ZStack { ... }
}
Error
error: main actor-isolated property 'someBool' can not be mutated from a nonisolated context
s.someBool = true
^- error: main actor-isolated property 'someBool' can not be mutated from a nonisolated context
The state type is @MainActor-isolated (typically via @Observable). The IIFE creates a nested non-MainActor closure that can't mutate main-actor properties.
Workaround
Inline the mutations directly in the #Preview closure body (no IIFE):
#Preview("Recording") {
let state = MyObservableState()
state.someBool = true
return ZStack { ... }
}
Failure 3 — Even inlined mutations are nonisolated in the generated bridge
Repro
After applying Workaround 2 (inline, no IIFE):
#Preview("Recording") {
let state = MyObservableState()
state.someBool = true
return ZStack { ... }
}
Error
error: call to main actor-isolated initializer 'init()' in a synchronous nonisolated context
error: main actor-isolated property 'someBool' can not be mutated from a nonisolated context
The generated createPreviewView is @_cdecl and lacks @MainActor:
@_cdecl("createPreviewView")
public func createPreviewView() -> UnsafeMutableRawPointer {
let view = {
return SwiftUI.AnyView(__PreviewBridge.wrap {
let state = MyObservableState() // nonisolated
state.someBool = true // nonisolated mutation
return ZStack { ... }
})
}()
let hostingController = UIHostingController(rootView: view)
return Unmanaged.passRetained(hostingController).toOpaque()
}
The user's preview body lives inside __PreviewBridge.wrap { ... } inside a let view = { ... }() inside a nonisolated @_cdecl function. The body inherits nonisolated context all the way down.
Workaround
Wrap the entire preview body in MainActor.assumeIsolated — asserts the runtime-true fact that previews execute on the main actor:
#Preview("Recording") {
return MainActor.assumeIsolated {
let state = MyObservableState()
state.someBool = true
return ZStack { ... }
}
}
This is awkward for the user; the cleaner fix is upstream.
Single suggested upstream fix that covers Failure 2 + Failure 3
Annotate the generated createPreviewView (and the closure passed to __PreviewBridge.wrap) as @MainActor. SwiftUI/UIKit view construction is required to be on the main actor anyway, and UIHostingController(rootView:) later in the same function already implicitly requires main-actor context. Making the wrapped user closure @MainActor () -> some View allows main-actor-isolated state types to be initialized and mutated naturally inside the user's #Preview body.
Concrete shape (suggested only):
@_cdecl("createPreviewView")
public func createPreviewView() -> UnsafeMutableRawPointer {
// Preview is always presented via UIHostingController on the main actor.
MainActor.assumeIsolated {
let view = SwiftUI.AnyView(__PreviewBridge.wrap { @MainActor in
// user's #Preview body
})
let hostingController = UIHostingController(rootView: view)
return Unmanaged.passRetained(hostingController).toOpaque()
}
}
Same shape for previewBodyKind — the __PreviewBodyKindProbe.detect closure should also be @MainActor.
Suggested upstream fix for Failure 1
Have __PreviewBodyKindProbe.detect use @_disfavoredOverload on the UIKit-typed overloads, OR restructure to take an autoclosure that doesn't need return-type inference at the call site. Either eliminates the ambiguity when leading variable declarations prevent the compiler from inferring the closure's return type.
Environment
- macOS 26.x
- Xcode-beta 26.4 RC (Swift 6.3)
- PreviewsMCP
main (commit current as of 2026-05-11)
- iOS Simulator: iPhone 17 Pro / iOS 26.4
- Source package: an iOS 17+ SPM library with
#Preview blocks that each triggered one of the failure modes.
The user's library compiles cleanly with swift build against both the iOS-sim and macOS targets in all three cases — only PreviewsMCP's snapshot pipeline fails.
I have working workarounds for my own use case, so this isn't blocking — filing for future contributors who'll hit the same wall. Happy to provide a minimal-repro package if useful.
Hit three related compile failures when running
previewsmcp snapshotagainst#Previewblocks that compile cleanly underswift buildagainst the same package. In all three the error is in PreviewsMCP's generated bridge module (<module>_N.swiftin/var/folders/.../T/previewsmcp/<uuid>/), not in the user's source. All three look like the same root cause: the bridge file generates@_cdeclC-bridge functions (createPreviewView,previewBodyKind) that are nonisolated, and the user's#Previewbody inherits that nonisolated context — even though SwiftUI previews always execute on the main actor at runtime (UIHostingController, UIWindow, didFinishLaunching).Failure 1 —
@Previewable @Statecausesdetectprobe overload ambiguityRepro
Error
The generated
previewBodyKind()function wraps the user's#Previewbody in__PreviewBodyKindProbe.detect { ... }to determine whether the body returns aView/UIView/UIViewController. The@Previewable @State var ...declaration at the top of the closure prevents the Swift compiler from inferring the closure's return type, so all threedetectoverloads remain candidates and resolution fails.Workaround
Replace
@Previewable @Statewith a regularletand putreturnon the view expression. (Loses Xcode Canvas interactivity, but PreviewsMCP only needs a static render.)Failure 2 — IIFE state-init pattern loses MainActor isolation in the bridge module
Repro
After applying Workaround 1 with the IIFE pattern still in place:
Error
The state type is
@MainActor-isolated (typically via@Observable). The IIFE creates a nested non-MainActor closure that can't mutate main-actor properties.Workaround
Inline the mutations directly in the
#Previewclosure body (no IIFE):Failure 3 — Even inlined mutations are nonisolated in the generated bridge
Repro
After applying Workaround 2 (inline, no IIFE):
Error
The generated
createPreviewViewis@_cdecland lacks@MainActor:The user's preview body lives inside
__PreviewBridge.wrap { ... }inside alet view = { ... }()inside a nonisolated@_cdeclfunction. The body inherits nonisolated context all the way down.Workaround
Wrap the entire preview body in
MainActor.assumeIsolated— asserts the runtime-true fact that previews execute on the main actor:This is awkward for the user; the cleaner fix is upstream.
Single suggested upstream fix that covers Failure 2 + Failure 3
Annotate the generated
createPreviewView(and the closure passed to__PreviewBridge.wrap) as@MainActor. SwiftUI/UIKit view construction is required to be on the main actor anyway, andUIHostingController(rootView:)later in the same function already implicitly requires main-actor context. Making the wrapped user closure@MainActor () -> some Viewallows main-actor-isolated state types to be initialized and mutated naturally inside the user's#Previewbody.Concrete shape (suggested only):
Same shape for
previewBodyKind— the__PreviewBodyKindProbe.detectclosure should also be@MainActor.Suggested upstream fix for Failure 1
Have
__PreviewBodyKindProbe.detectuse@_disfavoredOverloadon the UIKit-typed overloads, OR restructure to take an autoclosure that doesn't need return-type inference at the call site. Either eliminates the ambiguity when leading variable declarations prevent the compiler from inferring the closure's return type.Environment
main(commit current as of 2026-05-11)#Previewblocks that each triggered one of the failure modes.The user's library compiles cleanly with
swift buildagainst both the iOS-sim and macOS targets in all three cases — only PreviewsMCP's snapshot pipeline fails.I have working workarounds for my own use case, so this isn't blocking — filing for future contributors who'll hit the same wall. Happy to provide a minimal-repro package if useful.