Skip to content

Commit dacfd01

Browse files
committed
Merge pull request #18 from ReSwift/benji/router-improvements
Various Router Improvements
2 parents 48b6a36 + fca7a2b commit dacfd01

File tree

8 files changed

+229
-39
lines changed

8 files changed

+229
-39
lines changed

ReSwiftRouter.xcodeproj/xcshareddata/xcschemes/ReSwiftRouter-iOS.xcscheme

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@
2626
buildConfiguration = "Debug"
2727
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
2828
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
29-
shouldUseLaunchSchemeArgsEnv = "YES">
29+
shouldUseLaunchSchemeArgsEnv = "YES"
30+
codeCoverageEnabled = "YES">
3031
<Testables>
3132
<TestableReference
3233
skipped = "NO">

ReSwiftRouter/NavigationActions.swift

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,35 @@ public let typeMap: [String: StandardActionConvertible.Type] =
1515
public struct SetRouteAction: StandardActionConvertible {
1616

1717
let route: Route
18+
let animated: Bool
1819
public static let type = "RE_SWIFT_ROUTER_SET_ROUTE"
1920

20-
public init (_ route: Route) {
21+
public init (_ route: Route, animated: Bool = true) {
2122
self.route = route
23+
self.animated = animated
2224
}
2325

2426
public init(_ action: StandardAction) {
2527
self.route = action.payload!["route"] as! Route
28+
self.animated = action.payload!["animated"] as! Bool
2629
}
2730

2831
public func toStandardAction() -> StandardAction {
29-
return StandardAction(type: SetRouteAction.type, payload: ["route": route], isTypedAction: true)
32+
return StandardAction(
33+
type: SetRouteAction.type,
34+
payload: ["route": route, "animated": animated],
35+
isTypedAction: true
36+
)
3037
}
3138

32-
}
39+
}
40+
41+
public struct SetRouteSpecificData: Action {
42+
let route: Route
43+
let data: Any
44+
45+
public init(route: Route, data: Any) {
46+
self.route = route
47+
self.data = data
48+
}
49+
}

ReSwiftRouter/NavigationReducer.swift

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,18 +21,32 @@ public struct NavigationReducer {
2121

2222
switch action {
2323
case let action as SetRouteAction:
24-
return setRoute(state, route: action.route)
24+
return setRoute(state, setRouteAction: action)
25+
case let action as SetRouteSpecificData:
26+
return setRouteSpecificData(state, route: action.route, data: action.data)
2527
default:
2628
break
2729
}
2830

2931
return state
3032
}
3133

32-
static func setRoute(var state: NavigationState, route: Route) -> NavigationState {
33-
state.route = route
34+
static func setRoute(var state: NavigationState, setRouteAction: SetRouteAction) -> NavigationState {
35+
state.route = setRouteAction.route
36+
state.changeRouteAnimated = setRouteAction.animated
3437

3538
return state
3639
}
3740

41+
static func setRouteSpecificData(
42+
var state: NavigationState,
43+
route: Route,
44+
data: Any) -> NavigationState{
45+
let routeHash = RouteHash(route: route)
46+
47+
state.routeSpecificState[routeHash] = data
48+
49+
return state
50+
}
51+
3852
}

ReSwiftRouter/NavigationState.swift

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,36 @@ import ReSwift
1111
public typealias RouteElementIdentifier = String
1212
public typealias Route = [RouteElementIdentifier]
1313

14+
public struct RouteHash: Hashable {
15+
let routeHash: String
16+
17+
init(route: Route) {
18+
self.routeHash = route.joinWithSeparator("/")
19+
}
20+
21+
public var hashValue: Int { return self.routeHash.hashValue }
22+
}
23+
24+
public func == (lhs: RouteHash, rhs: RouteHash) -> Bool {
25+
return lhs.routeHash == rhs.routeHash
26+
}
27+
1428
public struct NavigationState {
1529
public init() {}
1630

1731
public var route: Route = []
18-
public var subRouteState: [StateType] = []
32+
public var routeSpecificState: [RouteHash: Any] = [:]
33+
var changeRouteAnimated: Bool = true
34+
}
35+
36+
extension NavigationState {
37+
public func getRouteSpecificState<T>(route: Route) -> T? {
38+
let hash = RouteHash(route: route)
39+
40+
return self.routeSpecificState[hash] as? T
41+
}
42+
}
43+
44+
public protocol HasNavigationState {
45+
var navigationState: NavigationState { get set }
1946
}

ReSwiftRouter/Routable.swift

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,31 +10,46 @@ public typealias RoutingCompletionHandler = () -> Void
1010

1111
public protocol Routable {
1212

13-
func changeRouteSegment(from: RouteElementIdentifier,
14-
to: RouteElementIdentifier,
15-
completionHandler: RoutingCompletionHandler) -> Routable
16-
17-
func pushRouteSegment(routeElementIdentifier: RouteElementIdentifier,
13+
func pushRouteSegment(
14+
routeElementIdentifier: RouteElementIdentifier,
15+
animated: Bool,
1816
completionHandler: RoutingCompletionHandler) -> Routable
1917

20-
func popRouteSegment(routeElementIdentifier: RouteElementIdentifier,
18+
func popRouteSegment(
19+
routeElementIdentifier: RouteElementIdentifier,
20+
animated: Bool,
2121
completionHandler: RoutingCompletionHandler)
2222

23+
func changeRouteSegment(
24+
from: RouteElementIdentifier,
25+
to: RouteElementIdentifier,
26+
animated: Bool,
27+
completionHandler: RoutingCompletionHandler) -> Routable
28+
2329
}
2430

2531
extension Routable {
26-
public func changeRouteSegment(from: RouteElementIdentifier,
27-
to: RouteElementIdentifier, completionHandler: RoutingCompletionHandler) -> Routable {
32+
33+
public func pushRouteSegment(
34+
routeElementIdentifier: RouteElementIdentifier,
35+
animated: Bool,
36+
completionHandler: RoutingCompletionHandler) -> Routable {
2837
fatalError("This routable cannot change segments. You have not implemented it.")
2938
}
3039

31-
public func popRouteSegment(routeElementIdentifier: RouteElementIdentifier,
40+
public func popRouteSegment(
41+
routeElementIdentifier: RouteElementIdentifier,
42+
animated: Bool,
3243
completionHandler: RoutingCompletionHandler) {
3344
fatalError("This routable cannot change segments. You have not implemented it.")
3445
}
3546

36-
public func pushRouteSegment(routeElementIdentifier: RouteElementIdentifier,
47+
public func changeRouteSegment(
48+
from: RouteElementIdentifier,
49+
to: RouteElementIdentifier,
50+
animated: Bool,
3751
completionHandler: RoutingCompletionHandler) -> Routable {
3852
fatalError("This routable cannot change segments. You have not implemented it.")
3953
}
54+
4055
}

ReSwiftRouter/Router.swift

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,10 @@ public class Router<State: StateType>: StoreSubscriber {
4444
case let .Pop(responsibleRoutableIndex, segmentToBePopped):
4545
dispatch_async(dispatch_get_main_queue()) {
4646
self.routables[responsibleRoutableIndex]
47-
.popRouteSegment(segmentToBePopped) {
48-
dispatch_semaphore_signal(semaphore)
47+
.popRouteSegment(
48+
segmentToBePopped,
49+
animated: state.changeRouteAnimated) {
50+
dispatch_semaphore_signal(semaphore)
4951
}
5052

5153
self.routables.removeAtIndex(responsibleRoutableIndex + 1)
@@ -55,8 +57,10 @@ public class Router<State: StateType>: StoreSubscriber {
5557
dispatch_async(dispatch_get_main_queue()) {
5658
self.routables[responsibleRoutableIndex + 1] =
5759
self.routables[responsibleRoutableIndex]
58-
.changeRouteSegment(segmentToBeReplaced,
59-
to: newSegment) {
60+
.changeRouteSegment(
61+
segmentToBeReplaced,
62+
to: newSegment,
63+
animated: state.changeRouteAnimated) {
6064
dispatch_semaphore_signal(semaphore)
6165
}
6266
}
@@ -65,8 +69,10 @@ public class Router<State: StateType>: StoreSubscriber {
6569
dispatch_async(dispatch_get_main_queue()) {
6670
self.routables.append(
6771
self.routables[responsibleRoutableIndex]
68-
.pushRouteSegment(segmentToBePushed) {
69-
dispatch_semaphore_signal(semaphore)
72+
.pushRouteSegment(
73+
segmentToBePushed,
74+
animated: state.changeRouteAnimated) {
75+
dispatch_semaphore_signal(semaphore)
7076
}
7177
)
7278
}
@@ -77,9 +83,12 @@ public class Router<State: StateType>: StoreSubscriber {
7783
let result = dispatch_semaphore_wait(semaphore, waitUntil)
7884

7985
if result != 0 {
80-
assertionFailure("[SwiftFlowRouter]: Router is stuck waiting for a" +
81-
" completion handler to be called. Ensure that you have called the " +
86+
print("[SwiftFlowRouter]: Router is stuck waiting for a" +
87+
" completion handler to be called. Ensure that you have called the" +
8288
" completion handler in each Routable element.")
89+
print("Set a symbolic breakpoint for the `ReSwiftRouterStuck` symbol in order" +
90+
" to halt the program when this happens")
91+
ReSwiftRouterStuck()
8392
}
8493
}
8594

@@ -194,6 +203,8 @@ public class Router<State: StateType>: StoreSubscriber {
194203

195204
}
196205

206+
func ReSwiftRouterStuck() {}
207+
197208
enum RoutingActions {
198209
case Push(responsibleRoutableIndex: Int, segmentToBePushed: RouteElementIdentifier)
199210
case Pop(responsibleRoutableIndex: Int, segmentToBePopped: RouteElementIdentifier)

0 commit comments

Comments
 (0)