Skip to content

Commit 3ce4f97

Browse files
authored
Merge pull request #220 from hotwired/fix-refresh
Fix refresh behavior
2 parents 97d0219 + 84f0eca commit 3ce4f97

4 files changed

Lines changed: 91 additions & 12 deletions

File tree

Source/Turbo Navigator/TurboNavigationHierarchyController.swift

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -168,21 +168,28 @@ class TurboNavigationHierarchyController {
168168
if navigationController.presentedViewController != nil {
169169
if modalNavigationController.viewControllers.count == 1 {
170170
navigationController.dismiss(animated: proposal.animated)
171-
delegate.refresh(navigationStack: .main)
171+
refreshIfTopViewControllerIsVisitable(from: .main)
172172
} else {
173173
modalNavigationController.popViewController(animated: proposal.animated)
174-
delegate.refresh(navigationStack: .modal)
174+
refreshIfTopViewControllerIsVisitable(from: .modal)
175175
}
176176
} else {
177177
navigationController.popViewController(animated: proposal.animated)
178-
delegate.refresh(navigationStack: .main)
178+
refreshIfTopViewControllerIsVisitable(from: .main)
179+
}
180+
}
181+
182+
private func refreshIfTopViewControllerIsVisitable(from stack: NavigationStackType) {
183+
if let navControllerTopmostVisitable = navController(for: stack).topViewController as? Visitable {
184+
delegate.refreshVisitable(navigationStack: stack,
185+
newTopmostVisitable: navControllerTopmostVisitable)
179186
}
180187
}
181188

182189
private func clearAll(via proposal: VisitProposal) {
183190
navigationController.dismiss(animated: proposal.animated)
184191
navigationController.popToRootViewController(animated: proposal.animated)
185-
delegate.refresh(navigationStack: .main)
192+
refreshIfTopViewControllerIsVisitable(from: .main)
186193
}
187194

188195
private func replaceRoot(with controller: UIViewController, via proposal: VisitProposal) {
Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,23 @@
11
import SafariServices
22
import WebKit
33

4-
/// Implement to be notified when certain navigations are performed
5-
/// or to render a native controller instead of a Turbo web visit.
64
protocol TurboNavigationHierarchyControllerDelegate: AnyObject {
7-
func visit(_ : Visitable, on: TurboNavigationHierarchyController.NavigationStackType, with: VisitOptions)
8-
func refresh(navigationStack: TurboNavigationHierarchyController.NavigationStackType)
5+
6+
/// Once the navigation hierarchy is modified, begin a visit on a navigation controller.
7+
///
8+
/// - Parameters:
9+
/// - _: the Visitable destination
10+
/// - on: the navigation controller that was modified
11+
/// - with: the visit options
12+
func visit(_ : Visitable,
13+
on: TurboNavigationHierarchyController.NavigationStackType,
14+
with: VisitOptions)
15+
16+
/// A refresh will pop (or dismiss) then ask the session to refresh the previous (or underlying) Visitable.
17+
///
18+
/// - Parameters:
19+
/// - navigationStack: the stack where the refresh is happening
20+
/// - newTopmostVisitable: the visitable to be refreshed
21+
func refreshVisitable(navigationStack: TurboNavigationHierarchyController.NavigationStackType,
22+
newTopmostVisitable: Visitable)
923
}

Source/Turbo Navigator/TurboNavigator.swift

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -215,10 +215,13 @@ extension TurboNavigator: TurboNavigationHierarchyControllerDelegate {
215215
}
216216
}
217217

218-
func refresh(navigationStack: TurboNavigationHierarchyController.NavigationStackType) {
218+
func refreshVisitable(navigationStack: TurboNavigationHierarchyController.NavigationStackType,
219+
newTopmostVisitable: any Visitable) {
219220
switch navigationStack {
220-
case .main: session.reload()
221-
case .modal: modalSession.reload()
221+
case .main:
222+
session.visit(newTopmostVisitable, action: .restore)
223+
case .modal:
224+
modalSession.visit(newTopmostVisitable, action: .restore)
222225
}
223226
}
224227
}

Tests/Turbo Navigator/TurboNavigatorTests.swift

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,61 @@ final class TurboNavigationHierarchyControllerTests: XCTestCase {
8282
XCTAssert(navigationController.viewControllers.last is VisitableViewController)
8383
assertVisited(url: proposal.url, on: .main)
8484
}
85+
86+
func test_default_default_refresh_refreshesPreviousController() {
87+
navigator.route(oneURL)
88+
XCTAssertEqual(navigationController.viewControllers.count, 1)
89+
90+
navigator.route(twoURL)
91+
XCTAssertEqual(navigator.rootViewController.viewControllers.count, 2)
92+
93+
/// Refreshing should pop the view controller and refresh the underlying controller.
94+
let proposal = VisitProposal(presentation: .refresh)
95+
navigator.route(proposal)
96+
97+
let visitable = navigator.session.activeVisitable as! VisitableViewController
98+
XCTAssertEqual(visitable.visitableURL, oneURL)
99+
XCTAssertEqual(navigator.rootViewController.viewControllers.count, 1)
100+
}
101+
102+
func test_default_modal_refresh_refreshesPreviousController() {
103+
navigationController.pushViewController(UIViewController(), animated: false)
104+
XCTAssertEqual(navigationController.viewControllers.count, 1)
105+
106+
let oneURLProposal = VisitProposal(path: "/one", context: .modal)
107+
navigator.route(oneURLProposal)
108+
109+
let twoURLProposal = VisitProposal(path: "/two", context: .modal)
110+
navigator.route(twoURLProposal)
111+
XCTAssertEqual(modalNavigationController.viewControllers.count, 2)
112+
113+
/// Refreshing should pop the view controller and refresh the underlying controller.
114+
let proposal = VisitProposal(presentation: .refresh)
115+
navigator.route(proposal)
116+
117+
let visitable = navigator.modalSession.activeVisitable as! VisitableViewController
118+
XCTAssertEqual(visitable.visitableURL, oneURL)
119+
XCTAssertEqual(modalNavigationController.viewControllers.count, 1)
120+
}
121+
122+
func test_default_modal_refresh_dismissesAndRefreshesMainStackTopViewController() {
123+
navigator.route(oneURL)
124+
XCTAssertEqual(navigationController.viewControllers.count, 1)
125+
126+
let twoURLProposal = VisitProposal(path: "/two", context: .modal)
127+
navigator.route(twoURLProposal)
128+
XCTAssertEqual(modalNavigationController.viewControllers.count, 1)
129+
130+
/// Refreshing should dismiss the view controller and refresh the underlying controller.
131+
let proposal = VisitProposal(context: .modal, presentation: .refresh)
132+
navigator.route(proposal)
133+
134+
let visitable = navigator.session.activeVisitable as! VisitableViewController
135+
XCTAssertEqual(visitable.visitableURL, oneURL)
136+
137+
XCTAssertNil(navigationController.presentedViewController)
138+
XCTAssertEqual(navigator.rootViewController.viewControllers.count, 1)
139+
}
85140

86141
func test_default_modal_default_presentsModal() {
87142
navigationController.pushViewController(UIViewController(), animated: false)
@@ -309,7 +364,7 @@ final class TurboNavigationHierarchyControllerTests: XCTestCase {
309364

310365
private class EmptyNavigationDelegate: TurboNavigationHierarchyControllerDelegate {
311366
func visit(_: Visitable, on: TurboNavigationHierarchyController.NavigationStackType, with: VisitOptions) {}
312-
func refresh(navigationStack: TurboNavigationHierarchyController.NavigationStackType) {}
367+
func refreshVisitable(navigationStack: TurboNavigationHierarchyController.NavigationStackType, newTopmostVisitable: any Visitable) { }
313368
}
314369

315370
// MARK: - VisitProposal extension

0 commit comments

Comments
 (0)