Repository navigation
Fix Quick Look preview deactivation crash - #4459
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
📝 WalkthroughWalkthroughThe PR separates Quick Look view lifecycles into close (soft retirement) and dismantle (hard teardown), tracks retired/dismantled view identities to avoid reconfiguring deactivated QLPreviewView instances, adds SwiftUI representable teardown, and adds tests exercising these transitions. ChangesQuick Look Preview Lifecycle Management
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Poem
Caution Pre-merge checks failedPlease resolve all errors before merging. Addressing warnings is optional.
❌ Failed checks (1 error, 1 warning)
✅ Passed checks (15 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Greptile SummaryFixes a crash where SwiftUI could call
Confidence Score: 5/5Safe to merge. The retirement guard and dismantle path are correct across all close/reopen/late-dismantle orderings, and the targeted regression tests validate each path. All changed code stays on the main actor, the ObjectIdentifier-based retirement sets are properly cleared when a view is re-acquired via view(), and the conditional item = nil in dismantle() correctly protects an active session from a late dismantle of a previously-retired view. The three new regression tests directly exercise the crash path, the active-item-preservation path, and the base session sequencing. No correctness issues were found. No files require special attention. Important Files Changed
Sequence DiagramsequenceDiagram
participant SwiftUI
participant QLPV as QuickLookPreviewView
participant FQLS as FilePreviewQuickLookSession
participant PONS as PanelOwnedNativeViewSession
participant QL as QLPreviewView
SwiftUI->>QLPV: makeNSView
QLPV->>FQLS: view(panel)
FQLS->>PONS: view(configure)
PONS->>QL: makeView
PONS-->>FQLS: view1
FQLS->>QL: configure previewItem
FQLS-->>QLPV: view1
Note over FQLS,PONS: Session closes
FQLS->>PONS: close()
PONS->>PONS: retire view1
PONS->>QL: "releaseView previewItem=nil"
FQLS->>FQLS: "item=nil"
Note over SwiftUI,QL: SwiftUI calls updateNSView before dismantling
SwiftUI->>QLPV: updateNSView(view1)
QLPV->>FQLS: update(view1)
FQLS->>PONS: update(view1 configure)
PONS->>PONS: retiredViews contains view1 - early return
Note over QL: setPreviewItem NOT called - no crash
Note over SwiftUI,QL: SwiftUI tears down representable
SwiftUI->>QLPV: dismantleNSView(view1 coordinator)
QLPV->>FQLS: dismantle(view1)
FQLS->>PONS: dismantle(view1)
PONS->>PONS: insert dismantledViews returns false
PONS->>QL: "releaseView previewItem=nil"
FQLS->>FQLS: false - item unchanged
Reviews (2): Last reviewed commit: "fix: avoid manual Quick Look close durin..." | Re-trigger Greptile |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 2985e74. Configure here.

Fixes #4453.
Repro
Per the user override, I built and launched the tagged dev app locally before fixing:
I reproduced the crash in that dev app with:
Before the fix, the tagged app aborted and wrote
~/Library/Logs/DiagnosticReports/cmux DEV-2026-05-20-172719.ipswith the Quick Look assertion:[QL] -[QLPreviewView setPreviewItem:blockingUntilLoading:timeoutDate:transition:]: item == nil || _reserved->internalState != QLPreviewDeactivatedInternalStateTest Approach
The first commit adds
FilePreviewReviewFeedbackTests.testQuickLookSessionCloseDoesNotDeactivateMountedRepresentableView. It creates a Quick Look file preview, calls the session close path while the representable view is still mounted, then updates that same view. On the un-fixed implementation this crashes through the sameQLPreviewView.setPreviewItempath; with the fix it leaves the retired view unconfigured.Additional regression coverage verifies stale SwiftUI dismantle callbacks do not reset the active preview item, and that
PanelOwnedNativeViewSessionstill performs one dismantle callback for a view that was previously retired byclose().Fix
PanelOwnedNativeViewSessionnow separates three native-view states:FilePreviewQuickLookSessionnow clearspreviewItemand removes the view from its superview for both close and dismantle. It deliberately does not callQLPreviewView.close(): that API permanently rejects future preview items and also asserts when Quick Look considers the view inactive. The session retirement guard prevents stale updates from reconfiguring old views without entering Quick Look's deactivated state.QuickLookPreviewViewstill routes SwiftUIdismantleNSViewinto the session so SwiftUI-owned teardown participates in the same lifecycle path.Verification
Crash: cmux DEV ... libsystem_c.dylib: abort() called.openreturned OK, the tagged socket stayed reachable, and no newer DiagnosticReports crash was created.No cloud-mac video was used.
Summary by CodeRabbit
Bug Fixes
Tests