Our app uses a UIView backed by a CATiledLayer that is embedded in a UIScrollView, to represent a large document viewer. (PDF data, actually.) It needs to be big - far too big to allocate a single layer, and it needs to be able to reveal more detail as you zoom in. This is the exact use case for a CATiledLayer.
CATiledLayer does its drawing on a background thread, as you know, and we've always taken care to make our draw method thread-safe. It has worked great for us, for over a decade now.
However, starting with iOS 26, we've been having some surprising crashes. It looks like our CATiledLayer (I think?) is trying to trigger a layout on the background thread as well. This is frustrating because it doesn't have any subviews or sublayers - there's no reason for it. I'm suspecting the CATiledLayer because it does its drawing on a thread, so maybe it would also do other things there, but honestly, I'm not sure - it's hard to tell.
Here's the crash. Normally with a crash like this, the solution is to bounce your layout call out to the main thread, but in our case, I'm not the one calling the layout function. The system (Core Animation) is doing it, and I can't figure out what I might be doing that is triggering it. Everything I am doing is on the main thread, and there's none of my code in this stack trace.
It's possible this may have something to do with the SwiftUI hosting view, as well? There's definitely one of those involved here - our custom PDF viewer is embedded in one. (That's another reason to suspect the CATiledLayer - whatever is crashing here is in a SwiftUI hosting view, and the PDF viewer is just about the only UIKit on the screen, here, embedded in a hosting view.)
I also have a test app that is pure UIKit, where I have not been able to reproduce the crash.
I've tried subclassing CATiledLayer and having an empty implementation of updateSublayers, and some other, similar hacks along those lines, but nothing I do seems to help.
I'm tempted to try moving from CATiledLayer to simple UIViews, and tiling those myself, but that's a lot of work. I want to be sure there's not some other solution before I try something like that. Is CATiledLayer still a supported mechanism or should I be moving away from it? If it is something entirely different that is crashing here, I don't want to do a bunch of rework of the PDF viewer only to end up with the same results.
It really smells like a UIKit bug to me, or at least UIKit making some concurrency assumptions that CATiledLayer is breaking. But all I can really do is guess.
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Modifications to the layout engine must not be performed from a background thread after it has been accessed from the main thread.'
*** First throw call stack:
(
0 CoreFoundation 0x00000001804f7348 __exceptionPreprocess + 172
1 libobjc.A.dylib 0x000000018009c094 objc_exception_throw + 72
2 CoreAutoLayout 0x000000022d3cc13c __36-[NSISEngine rebuildFromConstraints]_block_invoke + 0
3 CoreAutoLayout 0x000000022d3cbee0 -[NSISEngine _optimizeWithoutRebuilding] + 68
4 CoreAutoLayout 0x000000022d3cbe14 -[NSISEngine optimize] + 92
5 CoreAutoLayout 0x000000022d3c8744 -[NSISEngine performPendingChangeNotifications] + 100
6 UIKitCore 0x00000001869c7e7c -[UIView(Hierarchy) layoutSubviews] + 132
7 SwiftUI 0x00000001ddb40318 $s7SwiftUI14_UIHostingViewC14layoutSubviewsyyF + 68
8 SwiftUI 0x00000001ddb40358 $s7SwiftUI14_UIHostingViewC14layoutSubviewsyyFTo + 32
9 UIKitCore 0x0000000185642d34 block_destroy_helper.13 + 10136
10 UIKitCore 0x00000001856430c8 block_destroy_helper.13 + 11052
11 UIKitCore 0x00000001869d6ff4 -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 2656
12 QuartzCore 0x000000018c8fd194 _ZN2CA5Layer15perform_update_EPS0_P7CALayerjNS_17LayerUpdateReasonEPNS_11TransactionE + 452
13 QuartzCore 0x000000018c8fc9e4 _ZN2CA5Layer17update_if_needed_EPNS_11TransactionENS_17LayerUpdateReasonE + 600
14 QuartzCore 0x000000018c908674 _ZN2CA5Layer28layout_and_display_if_neededEPNS_11TransactionE + 152
15 QuartzCore 0x000000018c81d914 _ZN2CA7Context18commit_transactionEPNS_11TransactionEdPd + 544
16 QuartzCore 0x000000018c84da48 _ZN2CA11Transaction6commitEv + 636
17 QuartzCore 0x000000018c89ea8c _ZL21CAImageProviderThreadPjb + 1004
18 libdispatch.dylib 0x00000001033d59dc _dispatch_client_callout + 12
19 libdispatch.dylib 0x00000001033bf728 _dispatch_continuation_pop + 740
20 libdispatch.dylib 0x00000001033f3344 _dispatch_async_redirect_invoke + 700
21 libdispatch.dylib 0x00000001033cf470 _dispatch_root_queue_drain + 356
22 libdispatch.dylib 0x00000001033cffc4 _dispatch_worker_thread2 + 272
23 libsystem_pthread.dylib 0x0000000103276b50 _pthread_wqthread + 228
24 libsystem_pthread.dylib 0x000000010327598c start_wqthread + 8
)
libc++abi: terminating due to uncaught exception of type NSException