diff --git a/Sources/FlowStacks/Convenience methods/Array+convenienceMethods.swift b/Sources/FlowStacks/Convenience methods/Array+convenienceMethods.swift index 806188a..c4484c5 100644 --- a/Sources/FlowStacks/Convenience methods/Array+convenienceMethods.swift +++ b/Sources/FlowStacks/Convenience methods/Array+convenienceMethods.swift @@ -278,6 +278,18 @@ public extension Array where Element: RouteProtocol, Element.Screen: Identifiabl } } +// MARK: - Pop and push + +public extension Array where Element: RouteProtocol { + /// Pops the top pushed screen and pushes a new screen, resulting in a push transition to the new screen. + /// Only the top screen will be popped, and it must have been pushed (not presented). + /// - Parameter screen: The new screen to push. + mutating func popAndPush(_ screen: Element.Screen) { + pop() + push(screen) + } +} + // MARK: - Dismiss public extension Array where Element: RouteProtocol { diff --git a/Sources/FlowStacks/Convenience methods/FlowNavigator+convenienceMethods.swift b/Sources/FlowStacks/Convenience methods/FlowNavigator+convenienceMethods.swift index 29ea274..43c26c8 100644 --- a/Sources/FlowStacks/Convenience methods/FlowNavigator+convenienceMethods.swift +++ b/Sources/FlowStacks/Convenience methods/FlowNavigator+convenienceMethods.swift @@ -256,6 +256,17 @@ public extension FlowNavigator where Screen == AnyHashable { } } +// MARK: - Pop and push + +public extension FlowNavigator { + /// Pops the top pushed screen and pushes a new screen, resulting in a push transition to the new screen. + /// Only the top screen will be popped, and it must have been pushed (not presented). + /// - Parameter screen: The new screen to push. + func popAndPush(_ screen: Screen) { + routes.popAndPush(screen) + } +} + // MARK: - Dismiss public extension FlowNavigator { diff --git a/Sources/FlowStacks/Convenience methods/FlowPath+convenienceMethods.swift b/Sources/FlowStacks/Convenience methods/FlowPath+convenienceMethods.swift index 0b63acd..8eb14a7 100644 --- a/Sources/FlowStacks/Convenience methods/FlowPath+convenienceMethods.swift +++ b/Sources/FlowStacks/Convenience methods/FlowPath+convenienceMethods.swift @@ -180,6 +180,17 @@ public extension FlowPath { } } +// MARK: - Pop and push + +public extension FlowPath { + /// Pops the top pushed screen and pushes a new screen, resulting in a push transition to the new screen. + /// Only the top screen will be popped, and it must have been pushed (not presented). + /// - Parameter screen: The new screen to push. + mutating func popAndPush(_ screen: AnyHashable) { + routes.popAndPush(screen) + } +} + // MARK: - Dismiss public extension FlowPath { diff --git a/Tests/FlowStacksTests/ConvenienceMethodsTests.swift b/Tests/FlowStacksTests/ConvenienceMethodsTests.swift index e83f9a1..d7306c4 100644 --- a/Tests/FlowStacksTests/ConvenienceMethodsTests.swift +++ b/Tests/FlowStacksTests/ConvenienceMethodsTests.swift @@ -86,6 +86,24 @@ final class ConvenienceMethodsTests: XCTestCase { XCTAssertEqual(path.count, 2) } + func testPopAndPush() { + var path = FlowPath([.push(1), .push("two"), .push(true)]) + path.popAndPush("three") + XCTAssertEqual(path.count, 3) + // Last route should be a push of the new screen + XCTAssertEqual(path.routes.last?.style, .push) + XCTAssertEqual(path.routes.last?.screen as? String, "three") + // Previous screen should be unchanged + XCTAssertEqual(path.routes[1].screen as? String, "two") + } + + func testPopAndPushFromSingleScreen() { + var path = FlowPath([.push(1)]) + path.popAndPush("replacement") + XCTAssertEqual(path.count, 1) + XCTAssertEqual(path.routes.last?.screen as? String, "replacement") + } + func testDismissAllWhereFirstIsPushed() { var path = FlowPath([.push(1), .sheet("two"), .push(3), .cover("four"), .push(5), .cover("six"), .push(7)]) path.dismiss()