Skip to content
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
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
shouldUseLaunchSchemeArgsEnv = "YES"
codeCoverageEnabled = "YES">
<Testables>
<TestableReference
skipped = "NO">
Expand Down
23 changes: 20 additions & 3 deletions ReSwiftRouter/NavigationActions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,35 @@ public let typeMap: [String: StandardActionConvertible.Type] =
public struct SetRouteAction: StandardActionConvertible {

let route: Route
let animated: Bool
public static let type = "RE_SWIFT_ROUTER_SET_ROUTE"

public init (_ route: Route) {
public init (_ route: Route, animated: Bool = true) {
self.route = route
self.animated = animated
}

public init(_ action: StandardAction) {
self.route = action.payload!["route"] as! Route
self.animated = action.payload!["animated"] as! Bool
}

public func toStandardAction() -> StandardAction {
return StandardAction(type: SetRouteAction.type, payload: ["route": route], isTypedAction: true)
return StandardAction(
type: SetRouteAction.type,
payload: ["route": route, "animated": animated],
isTypedAction: true
)
}

}
}

public struct SetRouteSpecificData: Action {
let route: Route
let data: Any

public init(route: Route, data: Any) {
self.route = route
self.data = data
}
}
20 changes: 17 additions & 3 deletions ReSwiftRouter/NavigationReducer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,32 @@ public struct NavigationReducer {

switch action {
case let action as SetRouteAction:
return setRoute(state, route: action.route)
return setRoute(state, setRouteAction: action)
case let action as SetRouteSpecificData:
return setRouteSpecificData(state, route: action.route, data: action.data)
default:
break
}

return state
}

static func setRoute(var state: NavigationState, route: Route) -> NavigationState {
state.route = route
static func setRoute(var state: NavigationState, setRouteAction: SetRouteAction) -> NavigationState {
state.route = setRouteAction.route
state.changeRouteAnimated = setRouteAction.animated

return state
}

static func setRouteSpecificData(
var state: NavigationState,
route: Route,
data: Any) -> NavigationState{
let routeHash = RouteHash(route: route)

state.routeSpecificState[routeHash] = data

return state
}

}
29 changes: 28 additions & 1 deletion ReSwiftRouter/NavigationState.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,36 @@ import ReSwift
public typealias RouteElementIdentifier = String
public typealias Route = [RouteElementIdentifier]

public struct RouteHash: Hashable {
let routeHash: String

init(route: Route) {
self.routeHash = route.joinWithSeparator("/")
}

public var hashValue: Int { return self.routeHash.hashValue }
}

public func == (lhs: RouteHash, rhs: RouteHash) -> Bool {
return lhs.routeHash == rhs.routeHash
}

public struct NavigationState {
public init() {}

public var route: Route = []
public var subRouteState: [StateType] = []
public var routeSpecificState: [RouteHash: Any] = [:]
var changeRouteAnimated: Bool = true
}

extension NavigationState {
public func getRouteSpecificState<T>(route: Route) -> T? {
let hash = RouteHash(route: route)

return self.routeSpecificState[hash] as? T
}
}

public protocol HasNavigationState {
var navigationState: NavigationState { get set }
}
35 changes: 25 additions & 10 deletions ReSwiftRouter/Routable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,31 +10,46 @@ public typealias RoutingCompletionHandler = () -> Void

public protocol Routable {

func changeRouteSegment(from: RouteElementIdentifier,
to: RouteElementIdentifier,
completionHandler: RoutingCompletionHandler) -> Routable

func pushRouteSegment(routeElementIdentifier: RouteElementIdentifier,
func pushRouteSegment(
routeElementIdentifier: RouteElementIdentifier,
animated: Bool,
completionHandler: RoutingCompletionHandler) -> Routable

func popRouteSegment(routeElementIdentifier: RouteElementIdentifier,
func popRouteSegment(
routeElementIdentifier: RouteElementIdentifier,
animated: Bool,
completionHandler: RoutingCompletionHandler)

func changeRouteSegment(
from: RouteElementIdentifier,
to: RouteElementIdentifier,
animated: Bool,
completionHandler: RoutingCompletionHandler) -> Routable

}

extension Routable {
public func changeRouteSegment(from: RouteElementIdentifier,
to: RouteElementIdentifier, completionHandler: RoutingCompletionHandler) -> Routable {

public func pushRouteSegment(
routeElementIdentifier: RouteElementIdentifier,
animated: Bool,
completionHandler: RoutingCompletionHandler) -> Routable {
fatalError("This routable cannot change segments. You have not implemented it.")
}

public func popRouteSegment(routeElementIdentifier: RouteElementIdentifier,
public func popRouteSegment(
routeElementIdentifier: RouteElementIdentifier,
animated: Bool,
completionHandler: RoutingCompletionHandler) {
fatalError("This routable cannot change segments. You have not implemented it.")
}

public func pushRouteSegment(routeElementIdentifier: RouteElementIdentifier,
public func changeRouteSegment(
from: RouteElementIdentifier,
to: RouteElementIdentifier,
animated: Bool,
completionHandler: RoutingCompletionHandler) -> Routable {
fatalError("This routable cannot change segments. You have not implemented it.")
}

}
27 changes: 19 additions & 8 deletions ReSwiftRouter/Router.swift
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,10 @@ public class Router<State: StateType>: StoreSubscriber {
case let .Pop(responsibleRoutableIndex, segmentToBePopped):
dispatch_async(dispatch_get_main_queue()) {
self.routables[responsibleRoutableIndex]
.popRouteSegment(segmentToBePopped) {
dispatch_semaphore_signal(semaphore)
.popRouteSegment(
segmentToBePopped,
animated: state.changeRouteAnimated) {
dispatch_semaphore_signal(semaphore)
}

self.routables.removeAtIndex(responsibleRoutableIndex + 1)
Expand All @@ -55,8 +57,10 @@ public class Router<State: StateType>: StoreSubscriber {
dispatch_async(dispatch_get_main_queue()) {
self.routables[responsibleRoutableIndex + 1] =
self.routables[responsibleRoutableIndex]
.changeRouteSegment(segmentToBeReplaced,
to: newSegment) {
.changeRouteSegment(
segmentToBeReplaced,
to: newSegment,
animated: state.changeRouteAnimated) {
dispatch_semaphore_signal(semaphore)
}
}
Expand All @@ -65,8 +69,10 @@ public class Router<State: StateType>: StoreSubscriber {
dispatch_async(dispatch_get_main_queue()) {
self.routables.append(
self.routables[responsibleRoutableIndex]
.pushRouteSegment(segmentToBePushed) {
dispatch_semaphore_signal(semaphore)
.pushRouteSegment(
segmentToBePushed,
animated: state.changeRouteAnimated) {
dispatch_semaphore_signal(semaphore)
}
)
}
Expand All @@ -77,9 +83,12 @@ public class Router<State: StateType>: StoreSubscriber {
let result = dispatch_semaphore_wait(semaphore, waitUntil)

if result != 0 {
assertionFailure("[SwiftFlowRouter]: Router is stuck waiting for a" +
" completion handler to be called. Ensure that you have called the " +
print("[SwiftFlowRouter]: Router is stuck waiting for a" +
" completion handler to be called. Ensure that you have called the" +
" completion handler in each Routable element.")
print("Set a symbolic breakpoint for the `ReSwiftRouterStuck` symbol in order" +
" to halt the program when this happens")
ReSwiftRouterStuck()
}
}

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

}

func ReSwiftRouterStuck() {}

enum RoutingActions {
case Push(responsibleRoutableIndex: Int, segmentToBePushed: RouteElementIdentifier)
case Pop(responsibleRoutableIndex: Int, segmentToBePopped: RouteElementIdentifier)
Expand Down
Loading