Repository navigation
Conversation
SwiftUI calls a presentation's onDismiss for every close, also the one PHP asked for by setting `visible` to false. Both renderers sent each call to PHP as `@dismiss`, so PHP's own close came back as a dismissal. The event is asynchronous: when a handler closed a sheet and the next tap opened it again, the late dismissal closed the sheet that was just opened. Android sends only from onDismissRequest, so it has no such echo. The bottom sheet now marks the closes that present(false) starts and skips them in onDismiss. The binding can't tell the two apart: SwiftUI writes it for PHP's closes too. onAppear clears the mark, so a close that never reached onDismiss can't swallow the next swipe-down. The modal's full-screen cover is bound to a constant, so it can't be swiped away and only goes away when PHP sets `visible` to false. Its onDismiss is gone; the close button still sends `@dismiss`, as the header comment already said. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Nov 4, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
On iOS, a
<bottom-sheet>and a<modal>send@dismissto PHP when PHP itself closes them. SwiftUI callsonDismissfor every close of a presentation, also the one PHP asked for by settingvisibleto false, and both renderers send each call as@dismiss. Android sends only fromonDismissRequest, so it has no such echo.The event is asynchronous. If a handler closes a sheet and the next tap opens it again, the late dismissal arrives after the reopen and closes the sheet that was just opened. You see the sheet open, close and open again.
We found it in our app: a score sheet that saves and closes in one tap closed itself when you opened it for the next match. We had worked around it in nine screens with timers that drop any
@dismissarriving within 0.5s of opening.Steps to reproduce
closeSheet()is logged, although nobody dismissed the sheet.@dismisscloses the sheet you just opened.On Android,
closeSheet()runs only for a swipe-down or back.The fix
Bottom sheet.
present(false)marks the closes that PHP starts, andonDismissskips them. The binding can't tell the two kinds of close apart, because SwiftUI writes it for PHP's closes too. We checked this with a log line on the simulator: a binding-based version still reported PHP's close. The sheet content'sonAppearclears the mark, so a close that never reachedonDismisscan't swallow the next swipe-down.Modal. The full-screen cover is bound to
.constant(visible). It can't be swiped away, so it only goes away when PHP setsvisibleto false, and itsonDismisscould only echo that close. TheonDismissis gone. The close button still sends@dismiss, which is what the header comment already describes.Testing
tests/BottomSheetRendererTest.phpandtests/ModalRendererTest.phpeach get a test, and both fail without the change.composer test(487 passed) andcomposer lint:testpass.onDismiss:🤖 Generated with Claude Code