From bfe030e2e5a0d13926ffb7dd05f5b339311695e9 Mon Sep 17 00:00:00 2001 From: Cosmic Flamingo Date: Tue, 30 Sep 2025 20:47:29 -0500 Subject: [PATCH 1/7] Dismiss should be called on child being passed in onDismiss closure --- Sources/UIKitNavigation/Navigation/Presentation.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/UIKitNavigation/Navigation/Presentation.swift b/Sources/UIKitNavigation/Navigation/Presentation.swift index f08fe2144..fc6497abd 100644 --- a/Sources/UIKitNavigation/Navigation/Presentation.swift +++ b/Sources/UIKitNavigation/Navigation/Presentation.swift @@ -129,8 +129,8 @@ } else { self.present(child, animated: !transaction.uiKit.disablesAnimations) } - } dismiss: { [weak self] _, transaction in - self?.dismiss(animated: !transaction.uiKit.disablesAnimations) { + } dismiss: { [weak self] child, transaction in + child.dismiss(animated: !transaction.uiKit.disablesAnimations) { onDismiss?() } } From 3e53a6790409ad0e112c7ccceff425f082fc0422 Mon Sep 17 00:00:00 2001 From: Cosmic Flamingo Date: Tue, 30 Sep 2025 23:05:05 -0500 Subject: [PATCH 2/7] Dismiss 'self' when presentedByID's controller did not trigger dismissal --- Examples/CaseStudiesTests/PresentationTests.swift | 9 ++++++++- .../UIKitNavigation/Navigation/Presentation.swift | 13 +++++++++++-- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/Examples/CaseStudiesTests/PresentationTests.swift b/Examples/CaseStudiesTests/PresentationTests.swift index dbe9d01ed..0cd01c033 100644 --- a/Examples/CaseStudiesTests/PresentationTests.swift +++ b/Examples/CaseStudiesTests/PresentationTests.swift @@ -398,14 +398,21 @@ final class PresentationTests: XCTestCase { @MainActor func testDismissMiddlePresentation() async throws { class VC: ViewController { + static var count = 1 @UIBinding var isPresented = false override func viewDidLoad() { super.viewDidLoad() - present(isPresented: $isPresented) { VC() } + present(isPresented: $isPresented) { + let vc = VC() + vc.title = "\(VC.count)" + VC.count += 1 + return vc + } } } let vc = VC() + vc.title = "0" try await setUp(controller: vc) vc.isPresented = true diff --git a/Sources/UIKitNavigation/Navigation/Presentation.swift b/Sources/UIKitNavigation/Navigation/Presentation.swift index fc6497abd..fc8e35823 100644 --- a/Sources/UIKitNavigation/Navigation/Presentation.swift +++ b/Sources/UIKitNavigation/Navigation/Presentation.swift @@ -129,7 +129,7 @@ } else { self.present(child, animated: !transaction.uiKit.disablesAnimations) } - } dismiss: { [weak self] child, transaction in + } dismiss: { child, transaction in child.dismiss(animated: !transaction.uiKit.disablesAnimations) { onDismiss?() } @@ -380,7 +380,16 @@ } } else if let presented = presentedByID[key] { if let controller = presented.controller { - dismiss(controller, transaction) + // If inFlightController is nil at this point, + // it means the presented controller is not + // the one that triggered the dismissal, so + // it must be 'self' that needs to be dismissed + let controllerToDismiss = if inFlightController != nil { + controller + } else { + self + } + dismiss(controllerToDismiss, transaction) } self.presentedByID[key] = nil inFlightController = nil From 5864d6be18236dbd7148b6232e45b30b023cfccd Mon Sep 17 00:00:00 2001 From: Cosmic Flamingo Date: Tue, 30 Sep 2025 23:54:06 -0500 Subject: [PATCH 3/7] Dismiss presentedViewController instead of 'self' when not nil --- .../Navigation/Presentation.swift | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/Sources/UIKitNavigation/Navigation/Presentation.swift b/Sources/UIKitNavigation/Navigation/Presentation.swift index 3db5824b1..7ca5cbea0 100644 --- a/Sources/UIKitNavigation/Navigation/Presentation.swift +++ b/Sources/UIKitNavigation/Navigation/Presentation.swift @@ -121,8 +121,10 @@ content($item) } present: { [weak self] child, transaction in guard let self else { return } - if presentedViewController != nil { - self.dismiss(animated: !transaction.uiKit.disablesAnimations) { + if let presentedViewController { + presentedViewController.dismiss( + animated: !transaction.uiKit.disablesAnimations + ) { onDismiss?() self.present(child, animated: !transaction.uiKit.disablesAnimations) } @@ -350,13 +352,12 @@ } } let childController = content(unwrappedItem) - let onDismiss = { - [ - weak self, - presentationID = id(unwrappedItem.wrappedValue) - ] in + let onDismiss = { [ + weak self, + presentationID = id(unwrappedItem.wrappedValue) + ] in if let wrappedValue = item.wrappedValue, - presentationID == id(wrappedValue) + presentationID == id(wrappedValue) { inFlightController = self?.presentedByID[key]?.controller item.wrappedValue = nil From 8f65819cc4a88f44978cf5becf81bd8d0c7b676f Mon Sep 17 00:00:00 2001 From: Cosmic Flamingo Date: Wed, 1 Oct 2025 00:26:07 -0500 Subject: [PATCH 4/7] Always pass 'controller' to dismiss closure --- Sources/UIKitNavigation/Navigation/Presentation.swift | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/Sources/UIKitNavigation/Navigation/Presentation.swift b/Sources/UIKitNavigation/Navigation/Presentation.swift index 7ca5cbea0..8d27dc787 100644 --- a/Sources/UIKitNavigation/Navigation/Presentation.swift +++ b/Sources/UIKitNavigation/Navigation/Presentation.swift @@ -382,16 +382,7 @@ } } else if let presented = presentedByID[key] { if let controller = presented.controller { - // If inFlightController is nil at this point, - // it means the presented controller is not - // the one that triggered the dismissal, so - // it must be 'self' that needs to be dismissed - let controllerToDismiss = if inFlightController != nil { - controller - } else { - self - } - dismiss(controllerToDismiss, transaction) + dismiss(controller, transaction) } self.presentedByID[key] = nil inFlightController = nil From 59e4e83bb0f3dd51ef9699bace2936c91973c870 Mon Sep 17 00:00:00 2001 From: Cosmic Flamingo Date: Wed, 1 Oct 2025 10:32:53 -0500 Subject: [PATCH 5/7] Dismiss presentedViewController if non-nil or self in dismiss closure --- Examples/CaseStudiesTests/PresentationTests.swift | 9 +-------- Sources/UIKitNavigation/Navigation/Presentation.swift | 6 ++++-- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/Examples/CaseStudiesTests/PresentationTests.swift b/Examples/CaseStudiesTests/PresentationTests.swift index 0cd01c033..dbe9d01ed 100644 --- a/Examples/CaseStudiesTests/PresentationTests.swift +++ b/Examples/CaseStudiesTests/PresentationTests.swift @@ -398,21 +398,14 @@ final class PresentationTests: XCTestCase { @MainActor func testDismissMiddlePresentation() async throws { class VC: ViewController { - static var count = 1 @UIBinding var isPresented = false override func viewDidLoad() { super.viewDidLoad() - present(isPresented: $isPresented) { - let vc = VC() - vc.title = "\(VC.count)" - VC.count += 1 - return vc - } + present(isPresented: $isPresented) { VC() } } } let vc = VC() - vc.title = "0" try await setUp(controller: vc) vc.isPresented = true diff --git a/Sources/UIKitNavigation/Navigation/Presentation.swift b/Sources/UIKitNavigation/Navigation/Presentation.swift index 8d27dc787..b73ff0bc3 100644 --- a/Sources/UIKitNavigation/Navigation/Presentation.swift +++ b/Sources/UIKitNavigation/Navigation/Presentation.swift @@ -131,8 +131,10 @@ } else { self.present(child, animated: !transaction.uiKit.disablesAnimations) } - } dismiss: { child, transaction in - child.dismiss(animated: !transaction.uiKit.disablesAnimations) { + } dismiss: { [weak self] _, transaction in + guard let self else { return } + let controllerToDismiss = presentedViewController ?? self + controllerToDismiss.dismiss(animated: !transaction.uiKit.disablesAnimations) { onDismiss?() } } From 30b66134319b0561f9f084d9ad74e8035bd1b2f1 Mon Sep 17 00:00:00 2001 From: Cosmic Flamingo Date: Wed, 1 Oct 2025 11:33:56 -0500 Subject: [PATCH 6/7] wip --- .../Navigation/Presentation.swift | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/Sources/UIKitNavigation/Navigation/Presentation.swift b/Sources/UIKitNavigation/Navigation/Presentation.swift index b73ff0bc3..5187d5774 100644 --- a/Sources/UIKitNavigation/Navigation/Presentation.swift +++ b/Sources/UIKitNavigation/Navigation/Presentation.swift @@ -121,8 +121,8 @@ content($item) } present: { [weak self] child, transaction in guard let self else { return } - if let presentedViewController { - presentedViewController.dismiss( + if presentedViewController != nil { + self.dismiss( animated: !transaction.uiKit.disablesAnimations ) { onDismiss?() @@ -131,10 +131,8 @@ } else { self.present(child, animated: !transaction.uiKit.disablesAnimations) } - } dismiss: { [weak self] _, transaction in - guard let self else { return } - let controllerToDismiss = presentedViewController ?? self - controllerToDismiss.dismiss(animated: !transaction.uiKit.disablesAnimations) { + } dismiss: { child, transaction in + child.dismiss(animated: !transaction.uiKit.disablesAnimations) { onDismiss?() } } @@ -384,7 +382,14 @@ } } else if let presented = presentedByID[key] { if let controller = presented.controller { - dismiss(controller, transaction) + let controllerToDismiss = if let inFlightController { + inFlightController + } else if controller.presentedViewController != nil { + self + } else { + controller + } + dismiss(controllerToDismiss, transaction) } self.presentedByID[key] = nil inFlightController = nil From 4f3baaea4a22fb2bf2deea6af53111e819dccbfb Mon Sep 17 00:00:00 2001 From: Cosmic Flamingo Date: Wed, 1 Oct 2025 23:52:06 -0500 Subject: [PATCH 7/7] Try to fix iOS 26 crash --- .../UIKitNavigation/Navigation/Presentation.swift | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/Sources/UIKitNavigation/Navigation/Presentation.swift b/Sources/UIKitNavigation/Navigation/Presentation.swift index 5187d5774..913f98cf4 100644 --- a/Sources/UIKitNavigation/Navigation/Presentation.swift +++ b/Sources/UIKitNavigation/Navigation/Presentation.swift @@ -382,17 +382,20 @@ } } else if let presented = presentedByID[key] { if let controller = presented.controller { - let controllerToDismiss = if let inFlightController { - inFlightController + var controllerToDismiss: UIViewController? = nil + if let tmpController = inFlightController { + controllerToDismiss = inFlightController + inFlightController = nil } else if controller.presentedViewController != nil { - self + controllerToDismiss = self } else { - controller + controllerToDismiss = controller + } + if let controllerToDismiss { + dismiss(controllerToDismiss, transaction) } - dismiss(controllerToDismiss, transaction) } self.presentedByID[key] = nil - inFlightController = nil } } }