diff --git a/Sources/PortalSplitDividerCacheInvalidator.swift b/Sources/PortalSplitDividerCacheInvalidator.swift index a9bf69a600f2..2fbdf43c1647 100644 --- a/Sources/PortalSplitDividerCacheInvalidator.swift +++ b/Sources/PortalSplitDividerCacheInvalidator.swift @@ -10,13 +10,12 @@ private typealias PortalSubviewComparator = @convention(c) ( private extension NSView { @objc(cmux_portalAddSubview:) func cmux_portalAddSubview(_ subview: NSView) { - let previousWindow = subview.window - cmux_portalAddSubview(subview) - PortalViewHierarchyMutationTracker.recordInsertion( + PortalViewHierarchyMutationTracker.prepareForInsertion( parentView: self, insertedView: subview, - previousWindow: previousWindow + previousWindow: subview.window ) + cmux_portalAddSubview(subview) } @objc(cmux_portalAddSubview:positioned:relativeTo:) @@ -25,13 +24,12 @@ private extension NSView { positioned place: NSWindow.OrderingMode, relativeTo otherView: NSView? ) { - let previousWindow = subview.window - cmux_portalAddSubview(subview, positioned: place, relativeTo: otherView) - PortalViewHierarchyMutationTracker.recordInsertion( + PortalViewHierarchyMutationTracker.prepareForInsertion( parentView: self, insertedView: subview, - previousWindow: previousWindow + previousWindow: subview.window ) + cmux_portalAddSubview(subview, positioned: place, relativeTo: otherView) } @objc(cmux_portalSetSubviews:) diff --git a/Sources/PortalViewHierarchyMutationTracker.swift b/Sources/PortalViewHierarchyMutationTracker.swift index c612d7d972ef..55185e1a9e82 100644 --- a/Sources/PortalViewHierarchyMutationTracker.swift +++ b/Sources/PortalViewHierarchyMutationTracker.swift @@ -63,10 +63,12 @@ final class PortalViewHierarchyMutationTracker: NSObject { return registration } - /// Records an insertion after AppKit has attached the child. Unknown - /// prebuilt subtrees fail closed, while a leaf under an indexed no-split - /// branch stays on the fast path without a full cache rebuild. - static func recordInsertion( + /// Updates the hierarchy proof before handing the borrowed child reference + /// to AppKit. SwiftUI may consume that reference during insertion, so the + /// swizzled boundary must not access it after the original implementation + /// returns. Unknown prebuilt subtrees fail closed, while a leaf under an + /// indexed no-split branch stays on the fast path. + static func prepareForInsertion( parentView: NSView, insertedView: NSView, previousWindow: NSWindow?