.alignmentGuide modifier causes consistent app crash when Swift 6 language mode is enabled

I've been experiencing random crashes in my app and today, I could finally narrow it down enough to start building an example project that consistently crashes. The error is this:

"com.apple.SwiftUI.AsyncRenderer (19): EXC_BREAKPOINT (code=1, subcode=0x102f103f8)"

It's a very strange bug because the SwiftUI view hierarchy has to be in a very specific shape to cause the crash, but if it is, then it crashes 100% of the time:

It's a button inside a List and a simple "@State var number: Int?" that is being animated when the view appears. You can simply paste the following code into a project and run it (device, simulator or even in a preview):

// Must be in Swift 6 Language Mode!
// iOS 18 Beta 4
struct ContentView: View {
  @State var number: Int?

  var body: some View {
    List { // Must be in a List
      // It also crashes if button is here inside the List
    }
    .overlay {
      button
    }
    .task {  // Has to be in a task modifier (or .onAppear + Task {}, so it has to be async)
      number = 0
    }
  }

  // Has to be in a separate view property
  private var button: some View {
    Button {} label: {  // Has to be this initializer and not Button(number == nil ? "" : "") {}
      if number == nil { // There has to be a conditional with "number" in here
        Color.clear
      }
    }
    .animation(.default, value: number) // Animation must be inside the List
    .alignmentGuide(VerticalAlignment.center) { dimension in
      dimension[VerticalAlignment.center]
    }
  }
}

The crash only happens under the following conditions, as far as I can tell:

  • This only happens in the Swift 6 language mode (which I have enabled in the example project)
  • It's related to an .alignmentGuide modifier attached to a button that also has an .animation modifier
  • The button has to have a conditional view inside its label that checks for the same variable that is being animated
  • The button must be inside a separate property that is put either inside a List or inside an overlay of a List
  • The crash is then triggered when the animated value is asynchronously changed (in a .task modifier for example)

The crash happens at least with Beta 3 and 4 of Xcode 16.0

I have also opened a feedback: FB14510236 but I thought I'd post it here as well because this crash was driving me crazy and maybe others might find it helpful for their debugging. Took me hours to find out what's been causing it 😅

Edit:

Interestingly, when I run this project on an iOS 17 device, it does not crash, but rather print a runtime warning:

"warning: data race detected: @MainActor function at AlignmentGuideCrash/ContentView.swift:27 was not called on the main thread"

Line 27 is: .alignmentGuide(VerticalAlignment.center) { dimension in

Answered by DTS Engineer in 797942022

@Quantm Thanks for flagging that and filling the bug report.

Interesting enough the crash only happens when explicitly checking for nil.

if number == nil { 
  Color.clear
}

could you give this a trial instead:

private var button: some View {
    Button {
        
    } label: { 
      if let number {
          Color.red
      }
    }
    .animation(.default, value: number)
    .alignmentGuide(VerticalAlignment.center) { dimension in
      dimension[VerticalAlignment.center]
    }
  }

@Quantm Thanks for flagging that and filling the bug report.

Interesting enough the crash only happens when explicitly checking for nil.

if number == nil { 
  Color.clear
}

could you give this a trial instead:

private var button: some View {
    Button {
        
    } label: { 
      if let number {
          Color.red
      }
    }
    .animation(.default, value: number)
    .alignmentGuide(VerticalAlignment.center) { dimension in
      dimension[VerticalAlignment.center]
    }
  }

That does seem to work around the bug 🤔

I think I might be able to change the code to avoid the crash for now with this.

Thanks 😃

I've had the same problem with the 16.0 RC but while using my own custom AlignmentID. Unfortunately, removing explicit checks for == nil didn't work, but I did eventually figure out I could make the Alignment nonisolated and that appears to have fixed the problem.

extension HorizontalAlignment {
	private enum MyAlignment: AlignmentID {
		static func defaultValue(in context: ViewDimensions) -> CGFloat {
			context[HorizontalAlignment.center]
		}
	}
	
	nonisolated // Crashes without this
	static let myAlignment = HorizontalAlignment(MyAlignment.self)
}

Can confirm that this is happening on iOS 18.0 RC with Xcode 16 RC when in Swift 6.0 mode and optimisations enabled. When optimisations are not enabled, or if we're in Swift 5 mode, the crash does not occur. From what I see the crash is with an assert - so the issue may be an assert in iOS that assumes the asynchronous renderer is being tied to a specific dispatch queue, and the optimised build possibly does not tie the renderer into a specific dispatch queue, as it possibly uses an actor. I hope this additional input helps. It would be a shame if we can't compile for Swift 6.0 for iOS 18.0, I hope this gets fixed...

Edit: Found the exception text too:

EXC_BREAKPOINT: BUG IN CLIENT OF LIBDISPATCH: Assertion failed: Block was expected to execute on queue [com.apple.main-thread (0x1eee64a80)] > com.apple.main-thread

I was able to workaround the crash by adding @Sendable in the closure

view.alignmentGuide(VerticalAlignment.center) { @Sendable dimension in
      dimension[VerticalAlignment.center]
    }

you can also create an extension directly with @Sendable

view.alignmentGuideFixed(VerticalAlignment.center) { dimension in
      dimension[VerticalAlignment.center]
    }

extension View {
    nonisolated func alignmentGuideFixed(
        _ alignment: HorizontalAlignment,
        computeValue: @escaping @Sendable (ViewDimensions) -> CGFloat
    ) -> some View {
        self.alignmentGuide(alignment, computeValue: computeValue)
    }

    nonisolated func alignmentGuideFixed(
        _ alignment: VerticalAlignment,
        computeValue: @escaping @Sendable (ViewDimensions) -> CGFloat
    ) -> some View {
        self.alignmentGuide(alignment, computeValue: computeValue)
    }
}

I was able to workaround the crash by adding @Sendable in the closure

view.alignmentGuide(VerticalAlignment.center) { @Sendable dimension in
      dimension[VerticalAlignment.center]
    }

you can also create an extension directly with @Sendable

view.alignmentGuideFixed(VerticalAlignment.center) { dimension in
      dimension[VerticalAlignment.center]
    }

extension View {
    nonisolated func alignmentGuideFixed(
        _ alignment: HorizontalAlignment,
        computeValue: @escaping @Sendable (ViewDimensions) -> CGFloat
    ) -> some View {
        self.alignmentGuide(alignment, computeValue: computeValue)
    }

    nonisolated func alignmentGuideFixed(
        _ alignment: VerticalAlignment,
        computeValue: @escaping @Sendable (ViewDimensions) -> CGFloat
    ) -> some View {
        self.alignmentGuide(alignment, computeValue: computeValue)
    }
}
.alignmentGuide modifier causes consistent app crash when Swift 6 language mode is enabled
 
 
Q