From 23d5ca9ade8f0701388fa8a52d4e935f7f38e50c Mon Sep 17 00:00:00 2001 From: Fernando Olivares Date: Thu, 4 Jul 2024 15:16:14 -0600 Subject: [PATCH 1/2] Update refresh behavior --- .../TurboNavigationHierarchyController.swift | 15 +++-- ...avigationHierarchyControllerDelegate.swift | 22 +++++-- Source/Turbo Navigator/TurboNavigator.swift | 9 ++- .../Turbo Navigator/TurboNavigatorTests.swift | 57 ++++++++++++++++++- 4 files changed, 91 insertions(+), 12 deletions(-) diff --git a/Source/Turbo Navigator/TurboNavigationHierarchyController.swift b/Source/Turbo Navigator/TurboNavigationHierarchyController.swift index 2d855b1..0a1995e 100644 --- a/Source/Turbo Navigator/TurboNavigationHierarchyController.swift +++ b/Source/Turbo Navigator/TurboNavigationHierarchyController.swift @@ -168,21 +168,28 @@ class TurboNavigationHierarchyController { if navigationController.presentedViewController != nil { if modalNavigationController.viewControllers.count == 1 { navigationController.dismiss(animated: proposal.animated) - delegate.refresh(navigationStack: .main) + refreshIfTopViewControllerIsVisitable(from: .main) } else { modalNavigationController.popViewController(animated: proposal.animated) - delegate.refresh(navigationStack: .modal) + refreshIfTopViewControllerIsVisitable(from: .modal) } } else { navigationController.popViewController(animated: proposal.animated) - delegate.refresh(navigationStack: .main) + refreshIfTopViewControllerIsVisitable(from: .main) + } + } + + private func refreshIfTopViewControllerIsVisitable(from stack: NavigationStackType) { + if let navControllerTopmostVisitable = navController(for: stack).topViewController as? Visitable { + delegate.refreshVisitable(navigationStack: stack, + newTopmostVisitable: navControllerTopmostVisitable) } } private func clearAll(via proposal: VisitProposal) { navigationController.dismiss(animated: proposal.animated) navigationController.popToRootViewController(animated: proposal.animated) - delegate.refresh(navigationStack: .main) + refreshIfTopViewControllerIsVisitable(from: .main) } private func replaceRoot(with controller: UIViewController, via proposal: VisitProposal) { diff --git a/Source/Turbo Navigator/TurboNavigationHierarchyControllerDelegate.swift b/Source/Turbo Navigator/TurboNavigationHierarchyControllerDelegate.swift index 32fccb9..a49093d 100644 --- a/Source/Turbo Navigator/TurboNavigationHierarchyControllerDelegate.swift +++ b/Source/Turbo Navigator/TurboNavigationHierarchyControllerDelegate.swift @@ -1,9 +1,23 @@ import SafariServices import WebKit -/// Implement to be notified when certain navigations are performed -/// or to render a native controller instead of a Turbo web visit. protocol TurboNavigationHierarchyControllerDelegate: AnyObject { - func visit(_ : Visitable, on: TurboNavigationHierarchyController.NavigationStackType, with: VisitOptions) - func refresh(navigationStack: TurboNavigationHierarchyController.NavigationStackType) + + /// Once the navigation hierarchy is modified, begin a visit on a navigation controller. + /// + /// - Parameters: + /// - _: the Visitable destination + /// - on: the navigation controller that was modified + /// - with: the visit options + func visit(_ : Visitable, + on: TurboNavigationHierarchyController.NavigationStackType, + with: VisitOptions) + + /// A refresh will pop (or dismiss) then ask the session to refresh the previous (or underlying) Visitable. + /// + /// - Parameters: + /// - navigationStack: the stack where the refresh is happening + /// - newTopmostVisitable: the visitable to be refreshed + func refreshVisitable(navigationStack: TurboNavigationHierarchyController.NavigationStackType, + newTopmostVisitable: Visitable) } diff --git a/Source/Turbo Navigator/TurboNavigator.swift b/Source/Turbo Navigator/TurboNavigator.swift index 7f6db64..24cdbe5 100644 --- a/Source/Turbo Navigator/TurboNavigator.swift +++ b/Source/Turbo Navigator/TurboNavigator.swift @@ -215,10 +215,13 @@ extension TurboNavigator: TurboNavigationHierarchyControllerDelegate { } } - func refresh(navigationStack: TurboNavigationHierarchyController.NavigationStackType) { + func refreshVisitable(navigationStack: TurboNavigationHierarchyController.NavigationStackType, + newTopmostVisitable: any Visitable) { switch navigationStack { - case .main: session.reload() - case .modal: modalSession.reload() + case .main: + session.visit(newTopmostVisitable, reload: true) + case .modal: + modalSession.visit(newTopmostVisitable, reload: true) } } } diff --git a/Tests/Turbo Navigator/TurboNavigatorTests.swift b/Tests/Turbo Navigator/TurboNavigatorTests.swift index aa26088..cba3e6f 100644 --- a/Tests/Turbo Navigator/TurboNavigatorTests.swift +++ b/Tests/Turbo Navigator/TurboNavigatorTests.swift @@ -82,6 +82,61 @@ final class TurboNavigationHierarchyControllerTests: XCTestCase { XCTAssert(navigationController.viewControllers.last is VisitableViewController) assertVisited(url: proposal.url, on: .main) } + + func test_default_default_refresh_refreshesPreviousController() { + navigator.route(oneURL) + XCTAssertEqual(navigationController.viewControllers.count, 1) + + navigator.route(twoURL) + XCTAssertEqual(navigator.rootViewController.viewControllers.count, 2) + + /// Refreshing should pop the view controller and refresh the underlying controller. + let proposal = VisitProposal(presentation: .refresh) + navigator.route(proposal) + + let visitable = navigator.session.activeVisitable as! VisitableViewController + XCTAssertEqual(visitable.visitableURL, oneURL) + XCTAssertEqual(navigator.rootViewController.viewControllers.count, 1) + } + + func test_default_modal_refresh_refreshesPreviousController() { + navigationController.pushViewController(UIViewController(), animated: false) + XCTAssertEqual(navigationController.viewControllers.count, 1) + + let oneURLProposal = VisitProposal(path: "/one", context: .modal) + navigator.route(oneURLProposal) + + let twoURLProposal = VisitProposal(path: "/two", context: .modal) + navigator.route(twoURLProposal) + XCTAssertEqual(modalNavigationController.viewControllers.count, 2) + + /// Refreshing should pop the view controller and refresh the underlying controller. + let proposal = VisitProposal(presentation: .refresh) + navigator.route(proposal) + + let visitable = navigator.modalSession.activeVisitable as! VisitableViewController + XCTAssertEqual(visitable.visitableURL, oneURL) + XCTAssertEqual(modalNavigationController.viewControllers.count, 1) + } + + func test_default_modal_refresh_dismissesAndRefreshesMainStackTopViewController() { + navigator.route(oneURL) + XCTAssertEqual(navigationController.viewControllers.count, 1) + + let twoURLProposal = VisitProposal(path: "/two", context: .modal) + navigator.route(twoURLProposal) + XCTAssertEqual(modalNavigationController.viewControllers.count, 1) + + /// Refreshing should dismiss the view controller and refresh the underlying controller. + let proposal = VisitProposal(context: .modal, presentation: .refresh) + navigator.route(proposal) + + let visitable = navigator.session.activeVisitable as! VisitableViewController + XCTAssertEqual(visitable.visitableURL, oneURL) + + XCTAssertNil(navigationController.presentedViewController) + XCTAssertEqual(navigator.rootViewController.viewControllers.count, 1) + } func test_default_modal_default_presentsModal() { navigationController.pushViewController(UIViewController(), animated: false) @@ -309,7 +364,7 @@ final class TurboNavigationHierarchyControllerTests: XCTestCase { private class EmptyNavigationDelegate: TurboNavigationHierarchyControllerDelegate { func visit(_: Visitable, on: TurboNavigationHierarchyController.NavigationStackType, with: VisitOptions) {} - func refresh(navigationStack: TurboNavigationHierarchyController.NavigationStackType) {} + func refreshVisitable(navigationStack: TurboNavigationHierarchyController.NavigationStackType, newTopmostVisitable: any Visitable) { } } // MARK: - VisitProposal extension From 84f0eca6ce904aca499c34ec2bbfd2ac9f703ec2 Mon Sep 17 00:00:00 2001 From: Fernando Olivares Date: Thu, 4 Jul 2024 18:20:03 -0600 Subject: [PATCH 2/2] Restore instead of reloading --- Source/Turbo Navigator/TurboNavigator.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Turbo Navigator/TurboNavigator.swift b/Source/Turbo Navigator/TurboNavigator.swift index 24cdbe5..bf27a0b 100644 --- a/Source/Turbo Navigator/TurboNavigator.swift +++ b/Source/Turbo Navigator/TurboNavigator.swift @@ -219,9 +219,9 @@ extension TurboNavigator: TurboNavigationHierarchyControllerDelegate { newTopmostVisitable: any Visitable) { switch navigationStack { case .main: - session.visit(newTopmostVisitable, reload: true) + session.visit(newTopmostVisitable, action: .restore) case .modal: - modalSession.visit(newTopmostVisitable, reload: true) + modalSession.visit(newTopmostVisitable, action: .restore) } } }