From 4a4bcd6ca8dc9435c3586af06dcb6cd61229395b Mon Sep 17 00:00:00 2001 From: Arkadiusz Fal Date: Sun, 4 Oct 2026 23:32:44 +0200 Subject: [PATCH] Keep a section's stack when the sidebar selection changes In the split-view sidebar layout (macOS, and now iPad and the iPhone Duo), the detail NavigationStack that a selection change replaces writes an empty path through its binding on the way out, so going to another section and back always landed on the section's root. macOS already behaved this way before the router. Only the selected section's stack is on screen and can pop, so path writes for any other section are ignored. --- .../Navigation/NavigationRouter.swift | 10 ++++++++- YatteeTests/NavigationTests.swift | 22 +++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/Yattee/Services/Navigation/NavigationRouter.swift b/Yattee/Services/Navigation/NavigationRouter.swift index 6f97a337..299b0bb1 100644 --- a/Yattee/Services/Navigation/NavigationRouter.swift +++ b/Yattee/Services/Navigation/NavigationRouter.swift @@ -44,7 +44,15 @@ final class NavigationRouter { let key = item.id return Binding( get: { self.stacks[key] ?? [] }, - set: { self.stacks[key] = $0 } + set: { + // Only the selected section's stack is on screen, so only it + // edits its path (Back, swipe back). A NavigationSplitView + // detail stack that is being replaced on a selection change + // writes an empty path on its way out, which would throw + // away what the section had pushed. + guard key == self.selection.id else { return } + self.stacks[key] = $0 + } ) } diff --git a/YatteeTests/NavigationTests.swift b/YatteeTests/NavigationTests.swift index 054ae909..5b125655 100644 --- a/YatteeTests/NavigationTests.swift +++ b/YatteeTests/NavigationTests.swift @@ -677,6 +677,28 @@ struct NavigationCoordinatorTests { #expect(router.selection == .bookmarks) } + @Test("A stack that is no longer selected can't clear itself") + func offscreenStackWriteIgnored() { + let router = NavigationRouter() + router.push(.history) + let homePath = router.path(for: .home) + + router.select(.bookmarks) + homePath.wrappedValue = [] + + #expect(router.stack(for: .home) == [.history]) + } + + @Test("The selected stack still pops") + func selectedStackPops() { + let router = NavigationRouter() + router.push(.history) + + router.path(for: .home).wrappedValue = [] + + #expect(router.stack(for: .home).isEmpty) + } + @Test("A destination pushed before startup keeps its section") func deepLinkBeforeStartup() { let coordinator = NavigationCoordinator()