Skip to content

Commit

Permalink
Support setting the route without navigating
Browse files Browse the repository at this point in the history
Necessary for mutating route specific data without causing navigation changes.
The desired usage here would be that `oldState.map { $0.instanceIdentifier } == newState.map { $0.instanceIdentifier}`, and has significant side effects (described below) if not the case.

Note this carries a risk of having unintended effects if the user uses this incorrectly as it could cause the UI & navigationState to go out-of-sync if one changes the actual segments in the route.

However, the above side effect also Fixes #26, in that one can use this to react to UIKit based changes to the route. (IE system UINavigationBar back button)
  • Loading branch information
Malcolm Jarvis committed Dec 13, 2016
1 parent f1d6ad9 commit 5e7aa9b
Show file tree
Hide file tree
Showing 5 changed files with 47 additions and 0 deletions.
2 changes: 2 additions & 0 deletions ReSwiftRouter/NavigationActions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,12 @@ public struct SetRouteAction: Action {

let route: [RouteSegment]
let animated: Bool
let navigate: Bool

public init(route: [RouteSegment], animated: Bool = true, navigate: Bool = true) {
self.route = route
self.animated = animated
self.navigate = navigate
}
}

Expand Down
14 changes: 14 additions & 0 deletions ReSwiftRouter/NavigationReducer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ public struct NavigationReducer {
switch action {
case let action as SetRouteAction:
return self.setRoute(state: state, action: action)
case let action as MutateRouteSegmentAction:
return self.mutateComponent(state: state, action: action)
default: break
}

Expand All @@ -33,6 +35,18 @@ public struct NavigationReducer {

state.route = action.route
state.changeRouteAnimated = action.animated
state.shouldNavigate = action.navigate

return state
}

private static func mutateComponent(state: NavigationState, action: MutateRouteSegmentAction) -> NavigationState {
guard let index = state.route.index(where: { $0.instanceIdentifier == action.component.instanceIdentifier }) else { return state }

var state = state

state.route[index] = action.component
state.shouldNavigate = false

return state
}
Expand Down
1 change: 1 addition & 0 deletions ReSwiftRouter/NavigationState.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ public struct NavigationState {

public var route: [RouteSegment] = []
var changeRouteAnimated: Bool = true
var shouldNavigate: Bool = true // Set to false if the router should not respond to state change

public func segmentWith(instanceIdentifier: UUID) -> RouteSegment? {
return self.route.first(where: { $0.instanceIdentifier == instanceIdentifier })
Expand Down
4 changes: 4 additions & 0 deletions ReSwiftRouter/Router.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ open class Router<State: StateType>: StoreSubscriber {
}

open func newState(state: NavigationState) {
guard state.shouldNavigate == true else {
return
}

let routingActions = Router.routingActionsForTransitionFrom(
lastNavigationState.route, newRoute: state.route)

Expand Down
26 changes: 26 additions & 0 deletions ReSwiftRouterTests/ReSwiftRouterIntegrationTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,33 @@ class SwiftFlowRouterIntegrationTests: QuickSpec {
}
}

describe("changing route without navigating") {
var store: Store<FakeAppState>!
var mockRoutable: MockRoutable!
var router: Router<FakeAppState>!
let someRoute = MockRouteSegment()

beforeEach {
store = Store(reducer: AppReducer(), state: nil)
mockRoutable = MockRoutable()
router = Router(store: store, rootRoutable: mockRoutable) { state in
state.navigationState
}

// silence router not read warning, need to keep router alive via reference
_ = router
}

context("when dispatching a route change with navigation: false") {
beforeEach {
store.dispatch(SetRouteAction(route: [someRoute], navigate: false))
}

it("does not call to push route segment") {
expect(mockRoutable.callsToPushRouteSegment).toEventually(beEmpty())
}
}
}
}

}

0 comments on commit 5e7aa9b

Please sign in to comment.