Skip to content

Navigator

ohitsdaniel edited this page Feb 12, 2021 · 8 revisions

Navigator

Facade type erasing the type of the underlying datasource

public struct Navigator

Nested Type Aliases

DismissSuccessorInvocation

Testing helper

typealias DismissSuccessorInvocation = DismissInvocation

Example

 var invocations = [Navigator.DismissInvocation]()
 let expectectedInvocations = [
   Navigator.DismissInvocation(id: .id(expectedID))
 ]

 let sut = Navigator.mock(
   path: { self.path },
   dismissSuccessor: { id in
     invocations.append(.init(id: id))
   }
 )

 sut.dismissSuccessor(of: expectedID) // invoke code that invokes dismissSuccessor(of:)

 XCTAssertEqual(expectectedInvocations, invocations)

Initializers

init(path:go:goToOnScreen:goToPath:goToPathOnScreen:goBack:goBackToID:replace:dismiss:dismissScreen:dismissSuccessor:dismissSuccessorOfScreen:didAppear:)

public init(path: @escaping () -> [IdentifiedScreen], go: @escaping (AnyScreen, ScreenID) -> Void, goToOnScreen: @escaping (AnyScreen, AnyScreen) -> Void, goToPath: @escaping ([AnyScreen], ScreenID) -> Void, goToPathOnScreen: @escaping ([AnyScreen], AnyScreen) -> Void, goBack: @escaping (AnyScreen) -> Void, goBackToID: @escaping (ScreenID) -> Void, replace: @escaping ([AnyScreen]) -> Void, dismiss: @escaping (ScreenID) -> Void, dismissScreen: @escaping (AnyScreen) -> Void, dismissSuccessor: @escaping (ScreenID) -> Void, dismissSuccessorOfScreen: @escaping (AnyScreen) -> Void, didAppear: @escaping (ScreenID) -> Void)

init(dataSource:)

Initialises a Navigator wrapping a Datasource object

init(dataSource: Navigator.Datasource)

Parameters

  • dataSource: The wrapped data source

Properties

stub

let stub

Methods

mock(path:go:goToOnScreen:goToPath:goToPathOnScreen:goBack:goBackToID:replace:dismiss:dismissScreen:dismissSuccessor:dismissSuccessorOfScreen:didAppear:)

static func mock(path: @escaping () -> [IdentifiedScreen] = {
      fatalError("path() unimplemented in stub. Make sure to wrap your application in a Root view or inject Navigator via .environment(\\.navigator, navigator) for testing purposes.")
    }, go: @escaping (AnyScreen, ScreenID) -> Void = { _, _ in
      fatalError("go(to:) unimplemented in stub. Make sure to wrap your application in a Root view or inject Navigator via .environment(\\.navigator, navigator) for testing purposes.")
    }, goToOnScreen: @escaping (AnyScreen, AnyScreen) -> Void = { _, _ in
      fatalError("go(to:, on screen:) unimplemented in stub. Make sure to wrap your application in a Root view or inject Navigator via .environment(\\.navigator, navigator) for testing purposes.")
    }, goToPath: @escaping ([AnyScreen], ScreenID) -> Void = { _, _ in
      fatalError("goTo(path:, to:) unimplemented in stub. Make sure to wrap your application in a Root view or inject Navigator via .environment(\\.navigator, navigator) for testing purposes.")
    }, goToPathOnScreen: @escaping ([AnyScreen], AnyScreen) -> Void = { _, _ in
      fatalError("goTo(path:, to:) unimplemented in stub. Make sure to wrap your application in a Root view or inject Navigator via .environment(\\.navigator, navigator) for testing purposes.")
    }, goBack: @escaping (AnyScreen) -> Void = { _ in
      fatalError("goBack(to:) unimplemented in stub. Make sure to wrap your application in a Root view or inject Navigator via .environment(\\.navigator, navigator) for testing purposes.")
    }, goBackToID: @escaping (ScreenID) -> Void = { _ in
      fatalError("goBack(to:) unimplemented in stub. Make sure to wrap your application in a Root view or inject Navigator via .environment(\\.navigator, navigator) for testing purposes.")
    }, replace: @escaping ([AnyScreen]) -> Void = { _ in
      fatalError("replace(path:) unimplemented in stub. Make sure to wrap your application in a Root view or inject Navigator via .environment(\\.navigator, navigator) for testing purposes.")
    }, dismiss: @escaping (ScreenID) -> Void = { _ in
      fatalError("dismiss(id:) unimplemented in stub. Make sure to wrap your application in a Root view or inject Navigator via .environment(\\.navigator, navigator) for testing purposes.")
    }, dismissScreen: @escaping (AnyScreen) -> Void = { _ in
      fatalError("dismiss(screen:) unimplemented in stub. Make sure to wrap your application in a Root view or inject Navigator via .environment(\\.navigator, navigator) for testing purposes.")
    }, dismissSuccessor: @escaping (ScreenID) -> Void = { _ in
      fatalError("dismissSuccessor(of:) unimplemented in stub. Make sure to wrap your application in a Root view or inject Navigator via .environment(\\.navigator, navigator) for testing purposes.")
    }, dismissSuccessorOfScreen: @escaping (AnyScreen) -> Void = { _ in
      fatalError("dismissSuccessor(of:) unimplemented in stub. Make sure to wrap your application in a Root view or inject Navigator via .environment(\\.navigator, navigator) for testing purposes.")
    }, didAppear: @escaping (ScreenID) -> Void = { _ in
      fatalError("didAppear(id:) unimplemented in stub. Make sure to wrap your application in a Root view or inject Navigator via .environment(\\.navigator, navigator) for testing purposes.")
    }) -> Navigator

mock(path:goToInvoked:goToPathInvoked:goBackToInvoked:replacePathInvoked:dismissInvoked:dismissSuccessorInvoked:didAppearInvoked:)

static func mock(path: @escaping () -> [IdentifiedScreen] = {
      fatalError("path() unimplemented in stub. Make sure to wrap your application in a Root view or inject Navigator via .environment(\\.navigator, navigator) for testing purposes.")
    }, goToInvoked: @escaping (Navigator.GoToInvocation) -> Void = { _ in
      fatalError("go(to screen:) unimplemented in stub. Make sure to wrap your application in a Root view or inject Navigator via .environment(\\.navigator, navigator) for testing purposes.")
    }, goToPathInvoked: @escaping (Navigator.GoToPathInvocation) -> Void = { _ in
      fatalError("go(to path:) unimplemented in stub. Make sure to wrap your application in a Root view or inject Navigator via .environment(\\.navigator, navigator) for testing purposes.")
    }, goBackToInvoked: @escaping (Navigator.GoBackToInvocation) -> Void = { _ in
      fatalError("goBack(to:) unimplemented in stub. Make sure to wrap your application in a Root view or inject Navigator via .environment(\\.navigator, navigator) for testing purposes.")
    }, replacePathInvoked: @escaping (Navigator.ReplacePathInvocation) -> Void = { _ in
      fatalError("replace(path:) unimplemented in stub. Make sure to wrap your application in a Root view or inject Navigator via .environment(\\.navigator, navigator) for testing purposes.")
    }, dismissInvoked: @escaping (Navigator.DismissInvocation) -> Void = { _ in
      fatalError("dismiss(id:) unimplemented in stub. Make sure to wrap your application in a Root view or inject Navigator via .environment(\\.navigator, navigator) for testing purposes.")
    }, dismissSuccessorInvoked: @escaping (Navigator.DismissSuccessorInvocation) -> Void = { _ in
      fatalError("dismissSuccessor(of:) unimplemented in stub. Make sure to wrap your application in a Root view or inject Navigator via .environment(\\.navigator, navigator) for testing purposes.")
    }, didAppearInvoked: @escaping (Navigator.DidAppearInvocation) -> Void = { _ in
      fatalError("didAppear(id:) unimplemented in stub. Make sure to wrap your application in a Root view or inject Navigator via .environment(\\.navigator, navigator) for testing purposes.")
    }) -> Navigator

go(to:on:)

Append a screen after a given ScreenID.

public func go<S: Screen>(to screen: S, on id: ScreenID)

go(to:, on:) appends the given screen after the screen associated with the passed ScreenID. If you call go(to:, on:) for a ScreenID that is not associated with the last screen in the current routing path, the routing path after the ScreenID is replaced with [screen] and therefore cut off.

Example

// Curent path [(Content, ID)]
// [(A, 0), (B, 1)]
navigator.go(to: C(), on: 1)
// New path
// [(A, 0), (B, 1)], (C, 2)]

Parameters

  • screen: Destination
  • id: ScreenID used to identify where the destination should be appended

go(to:on:)

Append a screen after a given Screen.

public func go<S: Screen, Parent: Screen>(to screen: S, on parent: Parent)

go(to:, on:) appends the given screen after the last occurrence of the passed Parent screen object. If you call go(to:, on:) for a Screen that is not associated with the last screen in the current routing path, the routing path after the Parent is replaced with [screen] and therefore cut off.

Example

// Curent path [(Content, ID)]
// [(A, 0), (B, 1)]
navigator.go(to: C(), on: B())
// New path
// [(A, 0), (B, 1)], (C, 2)]

Parameters

  • screen: Destination
  • parent: Parent screen object used to identify where the destination should be appended

go(to:on:)

Append a screen after a given ScreenID.

public func go<S: Screen>(to screen: S, on parent: AnyScreen)

go(to:, on:) appends the given screen after the last occurrence of the passed Parent screen object. If you call go(to:, on:) for a Screen that is not associated with the last screen in the current routing path, the routing path after the Parent is replaced with [screen] and therefore cut off. Most likely used in conjunction with @Environment(.currentScreen).

Example

// Curent path [(Content, ID)]
// [(A, 0), (B, 1)]
navigator.go(to: C(), on: B())
// New path
// [(A, 0), (B, 1)], (C, 2)]

Parameters

  • screen: Destination
  • parent: AnyScreen screen object used to identify where the destination should be appended

go(to:on:)

Replace the path after a given ScreenID with the passed path.

public func go(to path: [AnyScreen], on id: ScreenID)

go(to:, on:) appends the given path after the screen associated with the passed ScreenID. If you call go(to:, on:) for a ScreenID that is not associated with the last screen in the current routing path, the routing path after the ScreenID is replaced with path and potentially cut off.

Example

// Curent path [(Content, ID)]
// [(A, 0), (B, 1)]

navigator.go(
 to: [C().eraseToAnyScreen(), D().eraseToAnyScreen()],
 on: 0
)

// New path
// [(A, 0), (C, 2), (D, 3)]

Parameters

  • path: New path after id
  • id: ScreenID used to identify where the path should be appended

go(to:on:)

Replace the path after the last occurrence of a given Parent with the passed path.

public func go<Parent: Screen>(to path: [AnyScreen], on parent: Parent)

go(to:, on:) appends the given path after the last occurrence of the passed Parent Screen object. If you call go(to:, on:) for a Parent screen that is not associated with the last screen in the current routing path, the routing path after the ScreenID is replaced with path and potentially cut off.

Example

// Curent path [(Content, ID)]
// [(A, 0), (B, 1)]

navigator.go(
 to: [C().eraseToAnyScreen(), D().eraseToAnyScreen()],
 on: A()
)

// New path
// [(A, 0), (C, 2), (D, 3)]

Parameters

  • path: New path after Parent
  • parent: Screen used to identify where the path should be appended

go(to:on:)

Replace the path after the last occurrence of a given Parent with the passed path.

public func go(to path: [AnyScreen], on parent: AnyScreen)

go(to:, on:) appends the given path after the last occurrence of the passed Parent Screen object. If you call go(to:, on:) for a Parent screen that is not associated with the last screen in the current routing path, the routing path after the ScreenID is replaced with path and potentially cut off. Most likely used in conjunction with @Environment(.currentScreen).

Example

// Curent path [(Content, ID)]
// [(A, 0), (B, 1)]

navigator.go(
 to: [C().eraseToAnyScreen(), D().eraseToAnyScreen()],
 on: A()
)

// New path
// [(A, 0), (C, 2), (D, 3)]

Parameters

  • path: New path after Parent
  • parent: AnyScreen used to identify where the path should be appended

goBack(to:)

Go back to the last occurrence of the screen instance in the routing path.

public func goBack<S: Screen>(to screen: S)

goBack(to:) trims the routing path to up to the last occurrence of the passed screen.

Example

// Curent path [(Content, ID)]
// [(A, 0), (B, 1), (C, 2)]

navigator.goBack(to: A())

// New path
// [(A, 0)]

Parameters

  • screen: Destination

goBack(to:)

Go back to the last occurrence of the screen instance in the routing path.

public func goBack(to screen: AnyScreen)

goBack(to:) trims the routing path to up to the last occurrence of the passed screen. Most likely used in conjunction with @Environment(.currentScreen).

Example

// Curent path [(Content, ID)]
// [(A, 0), (B, 1), (C, 2)]

navigator.goBack(to: A())

// New path
// [(A, 0)]

Parameters

  • screen: Destination

goBack(to:)

Go back to the specified ScreenID in the routing path.

public func goBack(to id: ScreenID)

goBack(to:) trims the routing path to up to the last occurrence of the passed screen.

Example

// Curent path [(Content, ID)]
// [(A, 0), (B, 1), (C, 2)]

navigator.goBack(to: 0)

// New path
// [(A, 0)]

Parameters

  • id: Destination ID

replace(path:)

Replace the current routing path with a new routing path.

public func replace(path: AnyScreen)

Example

// Curent path [(Content, ID)]
// [(A, 0), (B, 1)]

navigator.replace(
  path: C().eraseToAnyScreen(), D().eraseToAnyScreen()
)

// New path
// [(C, 0), (D, 1)]

Parameters

  • path: The new routing path

replace(path:)

Replace the current routing path with a new routing path.

public func replace(path: [AnyScreen])

replace(path:) checks if a prefix of the new path was already part of the replaced routing path and makes sure to keep the IDs and hasAppeared state intact.

Example

// Curent path [(Content, ID)]
// [(A, 0), (B, 1)]

navigator.replace(
  path: [
    C().eraseToAnyScreen(),
    D().eraseToAnyScreen()
  ]
)

// New path
// [(C, 0), (D, 1)]

Parameters

  • path: The new routing path

dismiss(id:)

Removes the screen associated with the passed screenID from the routing path.

public func dismiss(id: ScreenID)

dismiss(id:) does not care take the screen's presentation style into account and cuts the routing path up to the passed id.

Example

// Curent path [(Content, ID)]
// [(A, 0), (B, 1), (C, 2), (D,3)]

navigator.dismiss(id: 2)

// New path
// [(A, 0), (B, 1)]

Parameters

  • id: The id identifying the screen that needs to be dismissed

dismiss(screen:)

Removes the last occurrence screen from the routing path.

public func dismiss<S: Screen>(screen: S)

dismiss(screen:) does not care take the screen's presentation style into account and cuts the routing path up to the passed id.

Example

// Curent path [(Content, ID)]
// [(A, 0), (B, 1), (C, 2), (D,3)]

navigator.dismiss(screen: C())

// New path
// [(A, 0), (B, 1)]

Parameters

  • screen: The screen that needs to be dismissed

dismiss(screen:)

Removes the last occurrence screen from the routing path.

public func dismiss(screen: AnyScreen)

dismiss(screen:) does not care take the screen's presentation style into account and cuts the routing path up to the passed id. Most likely used in conjunction with @Environment(.currentScreen).

Example

// Curent path [(Content, ID)]
// [(A, 0), (B, 1), (C, 2), (D,3)]

navigator.dismiss(screen: currentScreen)

// New path
// [(A, 0), (B, 1)]

Parameters

  • screen: The screen that needs to be dismissed

dismissSuccessor(of:)

Removes the screen successors from the routing path.

public func dismissSuccessor(of id: ScreenID)

dismissSuccessor(of id:) does not care take the screen's presentation style into account and cuts the routing path after the passed id.

Example

// Curent path [(Content, ID)]
// [(A, 0), (B, 1), (C, 2), (D,3)]

navigator.dismissSuccessor(of id: 2)

// New path
// [(A, 0), (B, 1), (C, 2)]

Parameters

  • id: The id identifying the screen that needs to be dismissed

dismissSuccessor(of:)

Removes successors of the last occurrence of the passed screen from the routing path.

public func dismissSuccessor<S: Screen>(of screen: S)

dismissSuccessor(of screen:) does not care take the screen's presentation style into account and cuts the routing path after the passed id.

Example

// Curent path [(Content, ID)]
// [(A, 0), (B, 1), (C, 2), (D,3)]

navigator.dismissSuccessor(of: C())

// New path
// [(A, 0), (B, 1), (C, 2)]

Parameters

  • screen: The screen that needs to be dismissed

dismissSuccessor(of:)

Removes successors of the last occurrence of the passed screen from the routing path.

public func dismissSuccessor(of screen: AnyScreen)

dismissSuccessor(of screen:) does not care take the screen's presentation style into account and cuts the routing path after the passed id.

Example

// Curent path [(Content, ID)]
// [(A, 0), (B, 1), (C, 2), (D,3)]

navigator.dismissSuccessor(of: C())

// New path
// [(A, 0), (B, 1), (C, 2)]

Parameters

  • screen: The screen that needs to be dismissed

debug()

Enable logging received function calls and path changes.

func debug() -> Navigator
Clone this wiki locally