Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduces NavigationPathElement #70

Merged
merged 2 commits into from
May 10, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions Example/ExampleUITests/Screens/Detail.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ class Detail: Base {
let id: String
lazy var accessibilityIdentifiers = AccessibilityIdentifier.DetailScreen(id: id)

lazy var shortcutsButton = app
.buttons[accessibilityIdentifiers.shortcuts]
.await()
var shortcutsButton: XCUIElement {
app.buttons[accessibilityIdentifiers.shortcuts].await()
}

lazy var settingsButton = app
.buttons[accessibilityIdentifiers.settings]
.await()
var settingsButton: XCUIElement {
app.buttons[accessibilityIdentifiers.settings].await()
}

var shortcuts: NavigationShortcuts {
NavigationShortcuts(accessibilityPrefix: "detail.\(id)", app: app)
Expand Down
12 changes: 5 additions & 7 deletions Example/ExampleUITests/Screens/Home.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,16 @@
import XCTest

class Home: Base {
lazy var settingsButton = app
.buttons[AccessibilityIdentifier.HomeScreen.settingsNavigationBarItem]
.await()
var settingsButton: XCUIElement {
app.buttons[AccessibilityIdentifier.HomeScreen.settingsNavigationBarItem].await()
}

func detail(for id: String) -> XCUIElement {
app.buttons[AccessibilityIdentifier.HomeScreen.detail(for: id)]
.await()
app.buttons[AccessibilityIdentifier.HomeScreen.detail(for: id)].await()
}

func detailSettings(for id: String) -> XCUIElement {
app.buttons[AccessibilityIdentifier.HomeScreen.detailSettings(for: id)]
.await()
app.buttons[AccessibilityIdentifier.HomeScreen.detailSettings(for: id)].await()
}

@discardableResult
Expand Down
18 changes: 9 additions & 9 deletions Example/ExampleUITests/Screens/Settings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@ class Settings<Predecessor: Base>: Base {
AccessibilityIdentifier.SettingsScreen(prefix: prefix)
}

lazy var shortscutsSheetButton = app
.buttons[accessibilityIdentifiers.shortcutsSheet]
.await()
var shortcutsSheetButton: XCUIElement {
app.buttons[accessibilityIdentifiers.shortcutsSheet].await()
}

lazy var shortscutsPushButton = app
.buttons[accessibilityIdentifiers.shortcutsPush]
.await()
var shortcutsPushButton: XCUIElement {
app.buttons[accessibilityIdentifiers.shortcutsPush].await()
}

init(predecessor: Predecessor, prefix: String, app: XCUIApplication) {
self.predecessor = predecessor
Expand All @@ -28,19 +28,19 @@ class Settings<Predecessor: Base>: Base {

@discardableResult
func assertVisible() -> Self {
XCTAssertTrue(shortscutsSheetButton.exists)
XCTAssertTrue(shortcutsSheetButton.exists)
return self
}

@discardableResult
func goToShortcutsPush() -> NavigationShortcuts {
shortscutsPushButton.tap()
shortcutsPushButton.tap()
return NavigationShortcuts(accessibilityPrefix: "shortcuts", app: app)
}

@discardableResult
func goToShortcutsSheet() -> NavigationShortcuts {
shortscutsSheetButton.tap()
shortcutsSheetButton.tap()
return NavigationShortcuts(accessibilityPrefix: "shortcuts", app: app)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import XCTest

extension XCUIElement {
func await(_ timeout: TimeInterval = 6.0) -> XCUIElement {
_ = exists(after: timeout, pollInterval: 0.2)
return self
let exists = self.exists(after: timeout, pollInterval: 0.2)
return exists ? self: self
}

func exists(after timeout: TimeInterval, pollInterval: TimeInterval) -> Bool {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -358,7 +358,7 @@ public protocol NavigationTree: PathBuilder {
}

extension NavigationTree {
public func build(pathElement: AnyScreen) -> Builder.Content? {
public func build(pathElement: NavigationPathElement) -> Builder.Content? {
builder.build(pathElement: pathElement)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ public extension Navigator {
/// Enable logging received function calls and path changes.
func debug(
log: @escaping (String) -> Void = { print($0) },
dumpPath: @escaping (PathUpdate) -> Void = { dump($0) }
dumpPath: @escaping (NavigationPathUpdate) -> Void = { dump($0) }
) -> Navigator {
Navigator(
path: path,
Expand Down
4 changes: 2 additions & 2 deletions Sources/ComposableNavigator/Navigator/Navigator+Testing.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// MARK: - Stub
public extension Navigator {
static func mock(
path: @escaping () -> PathUpdate = {
path: @escaping () -> NavigationPathUpdate = {
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
Expand Down Expand Up @@ -67,7 +67,7 @@ public extension Navigator {
}

static func mock(
path: @escaping () -> PathUpdate = {
path: @escaping () -> NavigationPathUpdate = {
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
Expand Down
6 changes: 3 additions & 3 deletions Sources/ComposableNavigator/Navigator/Navigator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import Foundation

/// Facade type erasing the type of the underlying datasource
public struct Navigator {
private let _path: () -> PathUpdate
private let _path: () -> NavigationPathUpdate
private let _go: (AnyScreen, ScreenID) -> Void
private let _goToOnScreen: (AnyScreen, AnyScreen) -> Void
private let _goToPath: ([AnyScreen], ScreenID) -> Void
Expand All @@ -22,7 +22,7 @@ public struct Navigator {
/// Retrieve the current value of the navigation path
/// - Returns: The current navigation path
/// - SeeAlso: `Navigator.debug()`
func path() -> PathUpdate {
func path() -> NavigationPathUpdate {
_path()
}

Expand Down Expand Up @@ -346,7 +346,7 @@ public struct Navigator {
}

public init(
path: @escaping () -> PathUpdate,
path: @escaping () -> NavigationPathUpdate,
go: @escaping (AnyScreen, ScreenID) -> Void,
goToOnScreen: @escaping (AnyScreen, AnyScreen) -> Void,
goToPath: @escaping ([AnyScreen], ScreenID) -> Void,
Expand Down
71 changes: 42 additions & 29 deletions Sources/ComposableNavigator/Navigator/NavigatorDatasource.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,18 @@ import Foundation
public extension Navigator {
/// Observable Object exposing navigation path changes
class Datasource: ObservableObject {
@Published public var path: PathUpdate
@Published public var path: NavigationPathUpdate

private let screenID: () -> ScreenID

init(
path: [IdentifiedScreen],
screenID: @escaping () -> ScreenID = ScreenID.init
) {
self.path = PathUpdate(previous: [], current: path)
self.path = NavigationPathUpdate(
previous: [],
current: path.map(NavigationPathElement.screen)
)
self.screenID = screenID
}

Expand All @@ -22,10 +25,12 @@ public extension Navigator {

update(
path: path.current.prefix(through: index) + [
IdentifiedScreen(
id: screenID(),
content: successor,
hasAppeared: false
.screen(
IdentifiedScreen(
id: screenID(),
content: successor,
hasAppeared: false
)
)
]
)
Expand Down Expand Up @@ -54,19 +59,21 @@ public extension Navigator {
let suffixRange = suffix.startIndex..<suffix.startIndex.advanced(by: newPath.count)

let appendedPath = zip(suffixRange, newPath)
.map { index, element -> IdentifiedScreen in
let oldPathElement: IdentifiedScreen? = matchingContentRange.contains(index)
.map { index, element -> NavigationPathElement in
let oldPathElement: NavigationPathElement? = matchingContentRange.contains(index)
? suffix[index]
: nil

let id: ScreenID = oldPathElement.map(\.id) ?? screenID()
let hasAppeared = oldPathElement.map(\.hasAppeared) ?? false
&& (index != matchingContentRange.last || index == path.current.endIndex.advanced(by: -1))

return IdentifiedScreen(
id: id,
content: element,
hasAppeared: hasAppeared
return .screen(
IdentifiedScreen(
id: id,
content: element,
hasAppeared: hasAppeared
)
)
}

Expand Down Expand Up @@ -106,8 +113,8 @@ public extension Navigator {

let newPath = path
.enumerated()
.map { (index, element) -> IdentifiedScreen in
let oldPathElement: IdentifiedScreen? = matchingContentRange.contains(index)
.map { (index, element) -> NavigationPathElement in
let oldPathElement: NavigationPathElement? = matchingContentRange.contains(index)
? self.path.current[index]
: nil

Expand All @@ -116,10 +123,12 @@ public extension Navigator {
let hasAppeared = oldPathElement.map(\.hasAppeared) ?? false
&& (index != matchingContentRange.last || index == self.path.current.endIndex.advanced(by: -1))

return IdentifiedScreen(
id: id,
content: element,
hasAppeared: hasAppeared
return .screen(
IdentifiedScreen(
id: id,
content: element,
hasAppeared: hasAppeared
)
)
}

Expand Down Expand Up @@ -149,10 +158,12 @@ public extension Navigator {
return element
}

return IdentifiedScreen(
id: element.id,
content: newContent,
hasAppeared: element.hasAppeared
return .screen(
IdentifiedScreen(
id: element.id,
content: newContent,
hasAppeared: element.hasAppeared
)
)
}
)
Expand All @@ -173,19 +184,21 @@ public extension Navigator {
return element
}

return IdentifiedScreen(
id: element.id,
content: element.content,
hasAppeared: true
return .screen(
IdentifiedScreen(
id: element.id,
content: element.content,
hasAppeared: true
)
)
}
)
}

private func update(path newValue: [IdentifiedScreen]) {
private func update(path newValue: NavigationPath) {
guard newValue != path.current else { return }

path = PathUpdate(
path = NavigationPathUpdate(
previous: path.current,
current: newValue
)
Expand All @@ -195,7 +208,7 @@ public extension Navigator {

// MARK: - Screen based navigation
extension Navigator.Datasource {
private func identifiedScreen(for content: AnyScreen) -> IdentifiedScreen? {
private func identifiedScreen(for content: AnyScreen) -> NavigationPathElement? {
path.current.last(where: { $0.content == content })
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
public typealias NavigationPath = [NavigationPathElement]
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
public struct NavigationPathElementUpdate: Hashable {
public let previous: NavigationPathElement?
public let current: NavigationPathElement?

public init(previous: NavigationPathElement?, current: NavigationPathElement?) {
self.previous = previous
self.current = current
}
}
Original file line number Diff line number Diff line change
@@ -1,32 +1,32 @@
public struct PathUpdate: Equatable {
public let previous: [IdentifiedScreen]
public let current: [IdentifiedScreen]
public struct NavigationPathUpdate: Equatable {
public let previous: NavigationPath
public let current: NavigationPath

public func component(for id: ScreenID) -> PathComponentUpdate {
PathComponentUpdate(
public func component(for id: ScreenID) -> NavigationPathElementUpdate {
NavigationPathElementUpdate(
previous: extractPathComponent(for: id, from: previous),
current: extractPathComponent(for: id, from: current)
)
}

public func successor(of id: ScreenID) -> PathComponentUpdate {
PathComponentUpdate(
public func successor(of id: ScreenID) -> NavigationPathElementUpdate {
NavigationPathElementUpdate(
previous: extractSuccessor(of: id, from: previous),
current: extractSuccessor(of: id, from: current)
)
}

private func extractPathComponent(
for id: ScreenID,
from path: [IdentifiedScreen]
) -> IdentifiedScreen? {
from path: NavigationPath
) -> NavigationPathElement? {
return path.first(where: { $0.id == id })
}

private func extractSuccessor(
of id: ScreenID,
from path: [IdentifiedScreen]
) -> IdentifiedScreen? {
from path: NavigationPath
) -> NavigationPathElement? {
guard let successorIndex = path.firstIndex(where: { $0.id == id })?.advanced(by: 1),
path.indices.contains(successorIndex)
else {
Expand Down

This file was deleted.

Loading