From 1d74d6aeb345f4adc1f75ab3849f7b649eac7471 Mon Sep 17 00:00:00 2001 From: Henrik Lineholm Date: Sun, 14 Oct 2018 19:12:45 +0200 Subject: [PATCH 1/4] Rename Segment to Element --- README.md | 52 ++++---- ReSwiftRouter/NavigationState.swift | 4 +- ReSwiftRouter/Routable.swift | 34 +++--- ReSwiftRouter/Router.swift | 56 ++++----- .../ReSwiftRouterIntegrationTests.swift | 74 ++++++------ .../ReSwiftRouterTestsUnitTests.swift | 112 +++++++++--------- 6 files changed, 169 insertions(+), 163 deletions(-) diff --git a/README.md b/README.md index b64360d..561c9dd 100644 --- a/README.md +++ b/README.md @@ -84,45 +84,52 @@ This will make reducer handle all routing relevant actions. # Implementing `Routable` -ReSwiftRouter works with routes that are defined, similar to URLs, as a sequence of identifiers e.g. `["Home", "User", "UserDetail"]`. +ReSwiftRouter works with routes that are defined, similar to URLs, as a sequence of elements e.g. `["Home", "User", "UserDetail"]`. ReSwiftRouter is agnostic of the UI framework you are using - it uses `Routable`s to implement that interaction. -Each route segment is mapped to one responsible `Routable`. The `Routable` needs to be able to present a child, hide a child or replace a child with another child. +Each route element is mapped to one responsible `Routable`. The `Routable` needs to be able to present a child, hide a child or replace a child with another child. Here is the `Routable` protocol with the methods you should implement: ```swift -protocol Routable { - func changeRouteSegment(from: RouteElementIdentifier, - to: RouteElementIdentifier, - completionHandler: RoutingCompletionHandler) -> Routable +public protocol Routable { - func pushRouteSegment(routeElementIdentifier: RouteElementIdentifier, - completionHandler: RoutingCompletionHandler) -> Routable + func push( + _ element: RouteElement, + animated: Bool, + completionHandler: @escaping RoutingCompletionHandler) -> Routable - func popRouteSegment(routeElementIdentifier: RouteElementIdentifier, - completionHandler: RoutingCompletionHandler) + func pop( + _ element: RouteElement, + animated: Bool, + completionHandler: @escaping RoutingCompletionHandler) + + func change( + _ from: RouteElement, + to: RouteElement, + animated: Bool, + completionHandler: @escaping RoutingCompletionHandler) -> Routable } + ``` -As part of initializing `Router` you need to pass the first `Routable` as an argument. That root `Routable` will be responsible for the first route segment. +As part of initializing `Router` you need to pass the first `Routable` as an argument. That root `Routable` will be responsible for the first route element. -If e.g. you set the route of your application to `["Home"]`, your root `Routable` will be asked to present the view that corresponds to the identifier `"Home"`. +If e.g. you set the route of your application to `["Home"]`, your root `Routable` will be asked to present the view that corresponds to the element `"Home"`. When working on iOS with UIKit this would mean the `Routable` would need to set the `rootViewController` of the application. -Whenever a `Routable` presents a new route segment, it needs to return a new `Routable` that will be responsible for managing the presented segment. If you want to navigate from `["Home"]` to `["Home", "Users"]` the `Routable` responsible for the `"Home"` segment will be asked to present the `"User"` segment. +Whenever a `Routable` presents a new route element, it needs to return a new `Routable` that will be responsible for managing the presented element. If you want to navigate from `["Home"]` to `["Home", "Users"]` the `Routable` responsible for the `"Home"` element will be asked to present the `"User"` element. -If your navigation stack uses a modal presentation for this transition, the implementation of `Routable` for the `"Home"` segment might look like this: +If your navigation stack uses a modal presentation for this transition, the implementation of `Routable` for the `"Home"` element might look like this: ```swift -func pushRouteSegment(identifier: RouteElementIdentifier, - completionHandler: RoutingCompletionHandler) -> Routable { - - if identifier == "User" { +func push(_ element: RouteElement, animated: Bool, completionHandler: @escaping RoutingCompletionHandler) -> Routable { + + if element == "User" { // 1.) Perform the transition userViewController = UIStoryboard(name: "Main", bundle: nil) .instantiateViewControllerWithIdentifier("UserViewController") as! Routable @@ -131,7 +138,7 @@ func pushRouteSegment(identifier: RouteElementIdentifier, presentViewController(userViewController, animated: false, completion: completionHandler) - // 3.) Return the Routable for the presented segment. For convenience + // 3.) Return the Routable for the presented element. For convenience // this will often be the UIViewController itself. return userViewController } @@ -139,10 +146,9 @@ func pushRouteSegment(identifier: RouteElementIdentifier, // ... } -func popRouteSegment(identifier: RouteElementIdentifier, - completionHandler: RoutingCompletionHandler) { +func pop(_ element: RouteElement, animated: Bool, completionHandler: @escaping RoutingCompletionHandler) { - if identifier == "Home" { + if element == "Home" { dismissViewControllerAnimated(false, completion: completionHandler) } @@ -165,7 +171,7 @@ Currently the only way to change the current application route is by using the ` ) } ``` -As development continues, support for changing individual route segments will be added. +As development continues, support for changing individual route elements will be added. # Contributing diff --git a/ReSwiftRouter/NavigationState.swift b/ReSwiftRouter/NavigationState.swift index 1652ced..3d34bf6 100644 --- a/ReSwiftRouter/NavigationState.swift +++ b/ReSwiftRouter/NavigationState.swift @@ -8,8 +8,8 @@ import ReSwift -public typealias RouteElementIdentifier = String -public typealias Route = [RouteElementIdentifier] +public typealias RouteElement = String +public typealias Route = [RouteElement] /// A `Hashable` and `Equatable` presentation of a route. /// Can be used to check two routes for equality. diff --git a/ReSwiftRouter/Routable.swift b/ReSwiftRouter/Routable.swift index c58703a..d5122dc 100644 --- a/ReSwiftRouter/Routable.swift +++ b/ReSwiftRouter/Routable.swift @@ -10,19 +10,19 @@ public typealias RoutingCompletionHandler = () -> Void public protocol Routable { - func pushRouteSegment( - _ routeElementIdentifier: RouteElementIdentifier, + func push( + _ element: RouteElement, animated: Bool, completionHandler: @escaping RoutingCompletionHandler) -> Routable - func popRouteSegment( - _ routeElementIdentifier: RouteElementIdentifier, + func pop( + _ element: RouteElement, animated: Bool, completionHandler: @escaping RoutingCompletionHandler) - func changeRouteSegment( - _ from: RouteElementIdentifier, - to: RouteElementIdentifier, + func change( + _ from: RouteElement, + to: RouteElement, animated: Bool, completionHandler: @escaping RoutingCompletionHandler) -> Routable @@ -30,26 +30,26 @@ public protocol Routable { extension Routable { - public func pushRouteSegment( - _ routeElementIdentifier: RouteElementIdentifier, + public func push( + _ element: RouteElement, animated: Bool, completionHandler: @escaping RoutingCompletionHandler) -> Routable { - fatalError("This routable cannot push segments. You have not implemented it. (Asked \(type(of: self)) to push \(routeElementIdentifier))") + fatalError("This routable cannot push elements. You have not implemented it. (Asked \(type(of: self)) to push \(element))") } - public func popRouteSegment( - _ routeElementIdentifier: RouteElementIdentifier, + public func pop( + _ element: RouteElement, animated: Bool, completionHandler: @escaping RoutingCompletionHandler) { - fatalError("This routable cannot pop segments. You have not implemented it. (Asked \(type(of: self)) to pop \(routeElementIdentifier))") + fatalError("This routable cannot pop elements. You have not implemented it. (Asked \(type(of: self)) to pop \(element))") } - public func changeRouteSegment( - _ from: RouteElementIdentifier, - to: RouteElementIdentifier, + public func change( + _ from: RouteElement, + to: RouteElement, animated: Bool, completionHandler: @escaping RoutingCompletionHandler) -> Routable { - fatalError("This routable cannot change segments. You have not implemented it. (Asked \(type(of: self)) to change from \(from) to \(to))") + fatalError("This routable cannot change elements. You have not implemented it. (Asked \(type(of: self)) to change from \(from) to \(to))") } } diff --git a/ReSwiftRouter/Router.swift b/ReSwiftRouter/Router.swift index 142ce97..2275aab 100644 --- a/ReSwiftRouter/Router.swift +++ b/ReSwiftRouter/Router.swift @@ -40,11 +40,11 @@ open class Router: StoreSubscriber { waitForRoutingCompletionQueue.async { switch routingAction { - case let .pop(responsibleRoutableIndex, segmentToBePopped): + case let .pop(responsibleRoutableIndex, elementToBePopped): DispatchQueue.main.async { self.routables[responsibleRoutableIndex] - .popRouteSegment( - segmentToBePopped, + .pop( + elementToBePopped, animated: state.changeRouteAnimated) { semaphore.signal() } @@ -52,24 +52,24 @@ open class Router: StoreSubscriber { self.routables.remove(at: responsibleRoutableIndex + 1) } - case let .change(responsibleRoutableIndex, segmentToBeReplaced, newSegment): + case let .change(responsibleRoutableIndex, elementToBeReplaced, newElement): DispatchQueue.main.async { self.routables[responsibleRoutableIndex + 1] = self.routables[responsibleRoutableIndex] - .changeRouteSegment( - segmentToBeReplaced, - to: newSegment, + .change( + elementToBeReplaced, + to: newElement, animated: state.changeRouteAnimated) { semaphore.signal() } } - case let .push(responsibleRoutableIndex, segmentToBePushed): + case let .push(responsibleRoutableIndex, elementToBePushed): DispatchQueue.main.async { self.routables.append( self.routables[responsibleRoutableIndex] - .pushRouteSegment( - segmentToBePushed, + .push( + elementToBePushed, animated: state.changeRouteAnimated) { semaphore.signal() } @@ -114,8 +114,8 @@ open class Router: StoreSubscriber { // is not represented in the route, e.g. // route = ["tabBar"] // routables = [RootRoutable, TabBarRoutable] - static func routableIndex(for segment: Int) -> Int { - return segment + 1 + static func routableIndex(for element: Int) -> Int { + return element + 1 } static func routingActionsForTransition( @@ -134,22 +134,22 @@ open class Router: StoreSubscriber { // We start at the end of the old route var routeBuildingIndex = oldRoute.count - 1 - // Pop all route segments of the old route that are no longer in the new route + // Pop all route elements of the old route that are no longer in the new route // Stop one element ahead of the commonSubroute. When we are one element ahead of the // commmon subroute we have three options: // // 1. The old route had an element after the commonSubroute and the new route does not - // we need to pop the route segment after the commonSubroute + // we need to pop the route element after the commonSubroute // 2. The old route had no element after the commonSubroute and the new route does, we - // we need to push the route segment(s) after the commonSubroute + // we need to push the route element(s) after the commonSubroute // 3. The new route has a different element after the commonSubroute, we need to replace // the old route element with the new one while routeBuildingIndex > commonSubroute + 1 { - let routeSegmentToPop = oldRoute[routeBuildingIndex] + let routeElementToPop = oldRoute[routeBuildingIndex] let popAction = RoutingActions.pop( responsibleRoutableIndex: routableIndex(for: routeBuildingIndex - 1), - segmentToBePopped: routeSegmentToPop + elementToBePopped: routeElementToPop ) routingActions.append(popAction) @@ -162,18 +162,18 @@ open class Router: StoreSubscriber { if oldRoute.count > (commonSubroute + 1) && newRoute.count > (commonSubroute + 1) { let changeAction = RoutingActions.change( responsibleRoutableIndex: routableIndex(for: commonSubroute), - segmentToBeReplaced: oldRoute[commonSubroute + 1], - newSegment: newRoute[commonSubroute + 1]) + elementToBeReplaced: oldRoute[commonSubroute + 1], + newElement: newRoute[commonSubroute + 1]) routingActions.append(changeAction) } // This is the 1. case: // "The old route had an element after the commonSubroute and the new route does not - // we need to pop the route segment after the commonSubroute" + // we need to pop the route element after the commonSubroute" else if oldRoute.count > newRoute.count { let popAction = RoutingActions.pop( responsibleRoutableIndex: routableIndex(for: routeBuildingIndex - 1), - segmentToBePopped: oldRoute[routeBuildingIndex] + elementToBePopped: oldRoute[routeBuildingIndex] ) routingActions.append(popAction) @@ -184,15 +184,15 @@ open class Router: StoreSubscriber { // Push remainder of elements in new Route that weren't in old Route, this covers // the 2. case: // "The old route had no element after the commonSubroute and the new route does, - // we need to push the route segment(s) after the commonSubroute" + // we need to push the route element(s) after the commonSubroute" let newRouteIndex = newRoute.count - 1 while routeBuildingIndex < newRouteIndex { - let routeSegmentToPush = newRoute[routeBuildingIndex + 1] + let routeElementToPush = newRoute[routeBuildingIndex + 1] let pushAction = RoutingActions.push( responsibleRoutableIndex: routableIndex(for: routeBuildingIndex), - segmentToBePushed: routeSegmentToPush + elementToBePushed: routeElementToPush ) routingActions.append(pushAction) @@ -207,8 +207,8 @@ open class Router: StoreSubscriber { func ReSwiftRouterStuck() {} enum RoutingActions { - case push(responsibleRoutableIndex: Int, segmentToBePushed: RouteElementIdentifier) - case pop(responsibleRoutableIndex: Int, segmentToBePopped: RouteElementIdentifier) - case change(responsibleRoutableIndex: Int, segmentToBeReplaced: RouteElementIdentifier, - newSegment: RouteElementIdentifier) + case push(responsibleRoutableIndex: Int, elementToBePushed: RouteElement) + case pop(responsibleRoutableIndex: Int, elementToBePopped: RouteElement) + case change(responsibleRoutableIndex: Int, elementToBeReplaced: RouteElement, + newElement: RouteElement) } diff --git a/ReSwiftRouterTests/ReSwiftRouterIntegrationTests.swift b/ReSwiftRouterTests/ReSwiftRouterIntegrationTests.swift index a537fa4..ef45aea 100644 --- a/ReSwiftRouterTests/ReSwiftRouterIntegrationTests.swift +++ b/ReSwiftRouterTests/ReSwiftRouterIntegrationTests.swift @@ -13,48 +13,48 @@ import ReSwift class MockRoutable: Routable { - var callsToPushRouteSegment: [(routeElement: RouteElementIdentifier, animated: Bool)] = [] - var callsToPopRouteSegment: [(routeElement: RouteElementIdentifier, animated: Bool)] = [] - var callsToChangeRouteSegment: [( - from: RouteElementIdentifier, - to: RouteElementIdentifier, + var callsToPushRouteElement: [(routeElement: RouteElement, animated: Bool)] = [] + var callsToPopRouteElement: [(routeElement: RouteElement, animated: Bool)] = [] + var callsToChangeRouteElement: [( + from: RouteElement, + to: RouteElement, animated: Bool )] = [] - func pushRouteSegment( - _ routeElementIdentifier: RouteElementIdentifier, + func push( + _ element: RouteElement, animated: Bool, completionHandler: @escaping RoutingCompletionHandler ) -> Routable { - callsToPushRouteSegment.append( - (routeElement: routeElementIdentifier, animated: animated) + callsToPushRouteElement.append( + (routeElement: element, animated: animated) ) completionHandler() return MockRoutable() } - func popRouteSegment( - _ routeElementIdentifier: RouteElementIdentifier, + func pop( + _ element: RouteElement, animated: Bool, completionHandler: @escaping RoutingCompletionHandler) { - callsToPopRouteSegment.append( - (routeElement: routeElementIdentifier, animated: animated) + callsToPopRouteElement.append( + (routeElement: element, animated: animated) ) completionHandler() } - func changeRouteSegment( - _ from: RouteElementIdentifier, - to: RouteElementIdentifier, + func change( + _ from: RouteElement, + to: RouteElement, animated: Bool, completionHandler: @escaping RoutingCompletionHandler ) -> Routable { completionHandler() - callsToChangeRouteSegment.append((from: from, to: to, animated: animated)) + callsToChangeRouteElement.append((from: from, to: to, animated: animated)) return MockRoutable() } @@ -94,7 +94,7 @@ class SwiftFlowRouterIntegrationTests: QuickSpec { class FakeRootRoutable: Routable { var called = false - func pushRouteSegment(_ routeElementIdentifier: RouteElementIdentifier, + func push(_ element: RouteElement, completionHandler: RoutingCompletionHandler) -> Routable { called = true return MockRoutable() @@ -109,7 +109,7 @@ class SwiftFlowRouterIntegrationTests: QuickSpec { expect(routable.called).to(beFalse()) } - it("requests the root with identifier when an initial route is provided") { + it("requests the root with element when an initial route is provided") { store.dispatch( SetRouteAction( ["TabBarViewController"] @@ -117,14 +117,14 @@ class SwiftFlowRouterIntegrationTests: QuickSpec { ) class FakeRootRoutable: Routable { - var calledWithIdentifier: (RouteElementIdentifier?) -> Void + var calledWithElement: (RouteElement?) -> Void - init(calledWithIdentifier: @escaping (RouteElementIdentifier?) -> Void) { - self.calledWithIdentifier = calledWithIdentifier + init(calledWithElement: @escaping (RouteElement?) -> Void) { + self.calledWithElement = calledWithElement } - func pushRouteSegment(_ routeElementIdentifier: RouteElementIdentifier, animated: Bool, completionHandler: @escaping RoutingCompletionHandler) -> Routable { - calledWithIdentifier(routeElementIdentifier) + func push(_ element: RouteElement, animated: Bool, completionHandler: @escaping RoutingCompletionHandler) -> Routable { + calledWithElement(element) completionHandler() return MockRoutable() @@ -133,8 +133,8 @@ class SwiftFlowRouterIntegrationTests: QuickSpec { } waitUntil(timeout: 2.0) { fullfill in - let rootRoutable = FakeRootRoutable { identifier in - if identifier == "TabBarViewController" { + let rootRoutable = FakeRootRoutable { element in + if element == "TabBarViewController" { fullfill() } } @@ -153,14 +153,14 @@ class SwiftFlowRouterIntegrationTests: QuickSpec { ) class FakeChildRoutable: Routable { - var calledWithIdentifier: (RouteElementIdentifier?) -> Void + var calledWithElement: (RouteElement?) -> Void - init(calledWithIdentifier: @escaping (RouteElementIdentifier?) -> Void) { - self.calledWithIdentifier = calledWithIdentifier + init(calledWithElement: @escaping (RouteElement?) -> Void) { + self.calledWithElement = calledWithElement } - func pushRouteSegment(_ routeElementIdentifier: RouteElementIdentifier, animated: Bool, completionHandler: @escaping RoutingCompletionHandler) -> Routable { - calledWithIdentifier(routeElementIdentifier) + func push(_ element: RouteElement, animated: Bool, completionHandler: @escaping RoutingCompletionHandler) -> Routable { + calledWithElement(element) completionHandler() return MockRoutable() @@ -168,8 +168,8 @@ class SwiftFlowRouterIntegrationTests: QuickSpec { } waitUntil(timeout: 5.0) { completion in - let fakeChildRoutable = FakeChildRoutable() { identifier in - if identifier == "SecondViewController" { + let fakeChildRoutable = FakeChildRoutable() { element in + if element == "SecondViewController" { completion() } } @@ -181,7 +181,7 @@ class SwiftFlowRouterIntegrationTests: QuickSpec { self.injectedRoutable = injectedRoutable } - func pushRouteSegment(_ routeElementIdentifier: RouteElementIdentifier, + func push(_ element: RouteElement, animated: Bool, completionHandler: @escaping RoutingCompletionHandler) -> Routable { completionHandler() @@ -250,7 +250,7 @@ class SwiftFlowRouterIntegrationTests: QuickSpec { } it("calls routables asking for an animated presentation") { - expect(mockRoutable.callsToPushRouteSegment.last?.animated).toEventually(beTrue()) + expect(mockRoutable.callsToPushRouteElement.last?.animated).toEventually(beTrue()) } } @@ -260,7 +260,7 @@ class SwiftFlowRouterIntegrationTests: QuickSpec { } it("calls routables asking for an animated presentation") { - expect(mockRoutable.callsToPushRouteSegment.last?.animated).toEventually(beFalse()) + expect(mockRoutable.callsToPushRouteElement.last?.animated).toEventually(beFalse()) } } @@ -270,7 +270,7 @@ class SwiftFlowRouterIntegrationTests: QuickSpec { } it("calls routables asking for an animated presentation") { - expect(mockRoutable.callsToPushRouteSegment.last?.animated).toEventually(beTrue()) + expect(mockRoutable.callsToPushRouteElement.last?.animated).toEventually(beTrue()) } } } diff --git a/ReSwiftRouterTests/ReSwiftRouterTestsUnitTests.swift b/ReSwiftRouterTests/ReSwiftRouterTestsUnitTests.swift index 2fc9184..9561624 100644 --- a/ReSwiftRouterTests/ReSwiftRouterTestsUnitTests.swift +++ b/ReSwiftRouterTests/ReSwiftRouterTestsUnitTests.swift @@ -20,14 +20,14 @@ class ReSwiftRouterUnitTests: QuickSpec { override func spec() { describe("routing calls") { - let tabBarViewControllerIdentifier = "TabBarViewController" - let counterViewControllerIdentifier = "CounterViewController" - let statsViewControllerIdentifier = "StatsViewController" - let infoViewControllerIdentifier = "InfoViewController" + let tabBarViewControllerElement = "TabBarViewController" + let counterViewControllerElement = "CounterViewController" + let statsViewControllerElement = "StatsViewController" + let infoViewControllerElement = "InfoViewController" - it("calculates transitions from an empty route to a multi segment route") { + it("calculates transitions from an empty route to a multi element route") { let oldRoute: Route = [] - let newRoute = [tabBarViewControllerIdentifier, statsViewControllerIdentifier] + let newRoute = [tabBarViewControllerElement, statsViewControllerElement] let routingActions = Router.routingActionsForTransition(from: oldRoute, to: newRoute) @@ -35,20 +35,20 @@ class ReSwiftRouterUnitTests: QuickSpec { var action1Correct: Bool? var action2Correct: Bool? - if case let RoutingActions.push(responsibleRoutableIndex, segmentToBePushed) + if case let RoutingActions.push(responsibleRoutableIndex, elementToBePushed) = routingActions[0] { if responsibleRoutableIndex == 0 - && segmentToBePushed == tabBarViewControllerIdentifier { + && elementToBePushed == tabBarViewControllerElement { action1Correct = true } } - if case let RoutingActions.push(responsibleRoutableIndex, segmentToBePushed) + if case let RoutingActions.push(responsibleRoutableIndex, elementToBePushed) = routingActions[1] { if responsibleRoutableIndex == 1 - && segmentToBePushed == statsViewControllerIdentifier { + && elementToBePushed == statsViewControllerElement { action2Correct = true } } @@ -59,16 +59,16 @@ class ReSwiftRouterUnitTests: QuickSpec { } it("generates a Change on the last common subroute for routes of same length") { - - let oldRoute = [tabBarViewControllerIdentifier, counterViewControllerIdentifier] - let newRoute = [tabBarViewControllerIdentifier, statsViewControllerIdentifier] + + let oldRoute = [tabBarViewControllerElement, counterViewControllerElement] + let newRoute = [tabBarViewControllerElement, statsViewControllerElement] let routingActions = Router.routingActionsForTransition(from: oldRoute, to: newRoute) var controllerIndex: Int? - var toBeReplaced: RouteElementIdentifier? - var new: RouteElementIdentifier? + var toBeReplaced: RouteElement? + var new: RouteElement? if case let RoutingActions.change(responsibleControllerIndex, controllerToBeReplaced, @@ -80,14 +80,14 @@ class ReSwiftRouterUnitTests: QuickSpec { expect(routingActions).to(haveCount(1)) expect(controllerIndex).to(equal(1)) - expect(toBeReplaced).to(equal(counterViewControllerIdentifier)) - expect(new).to(equal(statsViewControllerIdentifier)) + expect(toBeReplaced).to(equal(counterViewControllerElement)) + expect(new).to(equal(statsViewControllerElement)) } it("generates a Change on the last common subroute when new route is longer than the old route") { - let oldRoute = [tabBarViewControllerIdentifier, counterViewControllerIdentifier] - let newRoute = [tabBarViewControllerIdentifier, statsViewControllerIdentifier, - infoViewControllerIdentifier] + let oldRoute = [tabBarViewControllerElement, counterViewControllerElement] + let newRoute = [tabBarViewControllerElement, statsViewControllerElement, + infoViewControllerElement] let routingActions = Router.routingActionsForTransition(from: oldRoute, to: newRoute) @@ -95,22 +95,22 @@ class ReSwiftRouterUnitTests: QuickSpec { var action1Correct: Bool? var action2Correct: Bool? - if case let RoutingActions.change(responsibleRoutableIndex, segmentToBeReplaced, - newSegment) + if case let RoutingActions.change(responsibleRoutableIndex, elementToBeReplaced, + newElement) = routingActions[0] { if responsibleRoutableIndex == 1 - && segmentToBeReplaced == counterViewControllerIdentifier - && newSegment == statsViewControllerIdentifier{ + && elementToBeReplaced == counterViewControllerElement + && newElement == statsViewControllerElement{ action1Correct = true } } - if case let RoutingActions.push(responsibleRoutableIndex, segmentToBePushed) + if case let RoutingActions.push(responsibleRoutableIndex, elementToBePushed) = routingActions[1] { if responsibleRoutableIndex == 2 - && segmentToBePushed == infoViewControllerIdentifier { + && elementToBePushed == infoViewControllerElement { action2Correct = true } @@ -122,8 +122,8 @@ class ReSwiftRouterUnitTests: QuickSpec { } it("generates a Change on the last common subroute when the new route is shorter than the old route") { - let oldRoute = [tabBarViewControllerIdentifier, counterViewControllerIdentifier,infoViewControllerIdentifier] - let newRoute = [tabBarViewControllerIdentifier, statsViewControllerIdentifier] + let oldRoute = [tabBarViewControllerElement, counterViewControllerElement,infoViewControllerElement] + let newRoute = [tabBarViewControllerElement, statsViewControllerElement] let routingActions = Router.routingActionsForTransition(from: oldRoute, to: newRoute) @@ -131,23 +131,23 @@ class ReSwiftRouterUnitTests: QuickSpec { var action1Correct: Bool? var action2Correct: Bool? - if case let RoutingActions.pop(responsibleRoutableIndex, segmentToBePopped) + if case let RoutingActions.pop(responsibleRoutableIndex, elementToBePopped) = routingActions[0] { if responsibleRoutableIndex == 2 - && segmentToBePopped == infoViewControllerIdentifier { + && elementToBePopped == infoViewControllerElement { action1Correct = true } } - if case let RoutingActions.change(responsibleRoutableIndex, segmentToBeReplaced, - newSegment) + if case let RoutingActions.change(responsibleRoutableIndex, elementToBeReplaced, + newElement) = routingActions[1] { if responsibleRoutableIndex == 1 - && segmentToBeReplaced == counterViewControllerIdentifier - && newSegment == statsViewControllerIdentifier{ + && elementToBeReplaced == counterViewControllerElement + && newElement == statsViewControllerElement{ action2Correct = true } } @@ -158,15 +158,15 @@ class ReSwiftRouterUnitTests: QuickSpec { } it("generates a Change action on root when root element changes") { - let oldRoute = [tabBarViewControllerIdentifier] - let newRoute = [statsViewControllerIdentifier] + let oldRoute = [tabBarViewControllerElement] + let newRoute = [statsViewControllerElement] let routingActions = Router.routingActionsForTransition(from: oldRoute, to: newRoute) var controllerIndex: Int? - var toBeReplaced: RouteElementIdentifier? - var new: RouteElementIdentifier? + var toBeReplaced: RouteElement? + var new: RouteElement? if case let RoutingActions.change(responsibleControllerIndex, controllerToBeReplaced, @@ -178,8 +178,8 @@ class ReSwiftRouterUnitTests: QuickSpec { expect(routingActions).to(haveCount(1)) expect(controllerIndex).to(equal(0)) - expect(toBeReplaced).to(equal(tabBarViewControllerIdentifier)) - expect(new).to(equal(statsViewControllerIdentifier)) + expect(toBeReplaced).to(equal(tabBarViewControllerElement)) + expect(new).to(equal(statsViewControllerElement)) } it("calculates no actions for transition from empty route to empty route") { @@ -193,8 +193,8 @@ class ReSwiftRouterUnitTests: QuickSpec { } it("calculates no actions for transitions between identical, non-empty routes") { - let oldRoute = [tabBarViewControllerIdentifier, statsViewControllerIdentifier] - let newRoute = [tabBarViewControllerIdentifier, statsViewControllerIdentifier] + let oldRoute = [tabBarViewControllerElement, statsViewControllerElement] + let newRoute = [tabBarViewControllerElement, statsViewControllerElement] let routingActions = Router.routingActionsForTransition(from: oldRoute, to: newRoute) @@ -203,9 +203,9 @@ class ReSwiftRouterUnitTests: QuickSpec { } it("calculates transitions with multiple pops") { - let oldRoute = [tabBarViewControllerIdentifier, statsViewControllerIdentifier, - counterViewControllerIdentifier] - let newRoute = [tabBarViewControllerIdentifier] + let oldRoute = [tabBarViewControllerElement, statsViewControllerElement, + counterViewControllerElement] + let newRoute = [tabBarViewControllerElement] let routingActions = Router.routingActionsForTransition(from: oldRoute, to: newRoute) @@ -213,20 +213,20 @@ class ReSwiftRouterUnitTests: QuickSpec { var action1Correct: Bool? var action2Correct: Bool? - if case let RoutingActions.pop(responsibleRoutableIndex, segmentToBePopped) + if case let RoutingActions.pop(responsibleRoutableIndex, elementToBePopped) = routingActions[0] { if responsibleRoutableIndex == 2 - && segmentToBePopped == counterViewControllerIdentifier { + && elementToBePopped == counterViewControllerElement { action1Correct = true } } - if case let RoutingActions.pop(responsibleRoutableIndex, segmentToBePopped) + if case let RoutingActions.pop(responsibleRoutableIndex, elementToBePopped) = routingActions[1] { if responsibleRoutableIndex == 1 - && segmentToBePopped == statsViewControllerIdentifier { + && elementToBePopped == statsViewControllerElement { action2Correct = true } } @@ -237,9 +237,9 @@ class ReSwiftRouterUnitTests: QuickSpec { } it("calculates transitions with multiple pushes") { - let oldRoute = [tabBarViewControllerIdentifier] - let newRoute = [tabBarViewControllerIdentifier, statsViewControllerIdentifier, - counterViewControllerIdentifier] + let oldRoute = [tabBarViewControllerElement] + let newRoute = [tabBarViewControllerElement, statsViewControllerElement, + counterViewControllerElement] let routingActions = Router.routingActionsForTransition(from: oldRoute, to: newRoute) @@ -247,20 +247,20 @@ class ReSwiftRouterUnitTests: QuickSpec { var action1Correct: Bool? var action2Correct: Bool? - if case let RoutingActions.push(responsibleRoutableIndex, segmentToBePushed) + if case let RoutingActions.push(responsibleRoutableIndex, elementToBePushed) = routingActions[0] { if responsibleRoutableIndex == 1 - && segmentToBePushed == statsViewControllerIdentifier { + && elementToBePushed == statsViewControllerElement { action1Correct = true } } - if case let RoutingActions.push(responsibleRoutableIndex, segmentToBePushed) + if case let RoutingActions.push(responsibleRoutableIndex, elementToBePushed) = routingActions[1] { if responsibleRoutableIndex == 2 - && segmentToBePushed == counterViewControllerIdentifier { + && elementToBePushed == counterViewControllerElement { action2Correct = true } } From 31ba7be0e3268c135263d0b7e3f6e1eaf618169a Mon Sep 17 00:00:00 2001 From: Henrik Lineholm Date: Sun, 14 Oct 2018 19:18:00 +0200 Subject: [PATCH 2/4] Rename RoutingCompletionHandler to RoutingCompletion --- README.md | 18 +++++------ ReSwiftRouter/Routable.swift | 14 ++++---- .../ReSwiftRouterIntegrationTests.swift | 32 +++++++++---------- 3 files changed, 32 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index 561c9dd..f3ecf01 100644 --- a/README.md +++ b/README.md @@ -99,18 +99,18 @@ public protocol Routable { func push( _ element: RouteElement, animated: Bool, - completionHandler: @escaping RoutingCompletionHandler) -> Routable + completion: @escaping RoutingCompletion) -> Routable func pop( _ element: RouteElement, animated: Bool, - completionHandler: @escaping RoutingCompletionHandler) + completion: @escaping RoutingCompletion) func change( _ from: RouteElement, to: RouteElement, animated: Bool, - completionHandler: @escaping RoutingCompletionHandler) -> Routable + completion: @escaping RoutingCompletion) -> Routable } @@ -127,16 +127,16 @@ Whenever a `Routable` presents a new route element, it needs to return a new `Ro If your navigation stack uses a modal presentation for this transition, the implementation of `Routable` for the `"Home"` element might look like this: ```swift -func push(_ element: RouteElement, animated: Bool, completionHandler: @escaping RoutingCompletionHandler) -> Routable { +func push(_ element: RouteElement, animated: Bool, completion: @escaping RoutingCompletion) -> Routable { if element == "User" { // 1.) Perform the transition userViewController = UIStoryboard(name: "Main", bundle: nil) .instantiateViewControllerWithIdentifier("UserViewController") as! Routable - // 2.) Call the `completionHandler` once the transition is complete + // 2.) Call the `completion` once the transition is complete presentViewController(userViewController, animated: false, - completion: completionHandler) + completion: completion) // 3.) Return the Routable for the presented element. For convenience // this will often be the UIViewController itself. @@ -146,10 +146,10 @@ func push(_ element: RouteElement, animated: Bool, completionHandler: @escaping // ... } -func pop(_ element: RouteElement, animated: Bool, completionHandler: @escaping RoutingCompletionHandler) { +func pop(_ element: RouteElement, animated: Bool, completion: @escaping RoutingCompletion) if element == "Home" { - dismissViewControllerAnimated(false, completion: completionHandler) + dismissViewControllerAnimated(false, completion: completion) } // ... @@ -158,7 +158,7 @@ func pop(_ element: RouteElement, animated: Bool, completionHandler: @escaping R ## Calling the Completion Handler within Routables -ReSwiftRouter needs to throttle the navigation actions, since many UI frameworks including UIKit don't allow to perform multiple navigation steps in parallel. Therefor every method of `Routable` receives a `completionHandler`. The router will not perform any further navigation actions until the completion handler is called. +ReSwiftRouter needs to throttle the navigation actions, since many UI frameworks including UIKit don't allow to perform multiple navigation steps in parallel. Therefor every method of `Routable` receives a `completion` handler. The router will not perform any further navigation actions until the completion handler is called. # Changing the Current Route diff --git a/ReSwiftRouter/Routable.swift b/ReSwiftRouter/Routable.swift index d5122dc..9590aaf 100644 --- a/ReSwiftRouter/Routable.swift +++ b/ReSwiftRouter/Routable.swift @@ -6,25 +6,25 @@ // Copyright © 2015 DigiTales. All rights reserved. // -public typealias RoutingCompletionHandler = () -> Void +public typealias RoutingCompletion = () -> Void public protocol Routable { func push( _ element: RouteElement, animated: Bool, - completionHandler: @escaping RoutingCompletionHandler) -> Routable + completion: @escaping RoutingCompletion) -> Routable func pop( _ element: RouteElement, animated: Bool, - completionHandler: @escaping RoutingCompletionHandler) + completion: @escaping RoutingCompletion) func change( _ from: RouteElement, to: RouteElement, animated: Bool, - completionHandler: @escaping RoutingCompletionHandler) -> Routable + completion: @escaping RoutingCompletion) -> Routable } @@ -33,14 +33,14 @@ extension Routable { public func push( _ element: RouteElement, animated: Bool, - completionHandler: @escaping RoutingCompletionHandler) -> Routable { + completion: @escaping RoutingCompletion) -> Routable { fatalError("This routable cannot push elements. You have not implemented it. (Asked \(type(of: self)) to push \(element))") } public func pop( _ element: RouteElement, animated: Bool, - completionHandler: @escaping RoutingCompletionHandler) { + completion: @escaping RoutingCompletion) { fatalError("This routable cannot pop elements. You have not implemented it. (Asked \(type(of: self)) to pop \(element))") } @@ -48,7 +48,7 @@ extension Routable { _ from: RouteElement, to: RouteElement, animated: Bool, - completionHandler: @escaping RoutingCompletionHandler) -> Routable { + completion: @escaping RoutingCompletion) -> Routable { fatalError("This routable cannot change elements. You have not implemented it. (Asked \(type(of: self)) to change from \(from) to \(to))") } diff --git a/ReSwiftRouterTests/ReSwiftRouterIntegrationTests.swift b/ReSwiftRouterTests/ReSwiftRouterIntegrationTests.swift index ef45aea..ea606b3 100644 --- a/ReSwiftRouterTests/ReSwiftRouterIntegrationTests.swift +++ b/ReSwiftRouterTests/ReSwiftRouterIntegrationTests.swift @@ -24,35 +24,35 @@ class MockRoutable: Routable { func push( _ element: RouteElement, animated: Bool, - completionHandler: @escaping RoutingCompletionHandler + completion: @escaping RoutingCompletion ) -> Routable { callsToPushRouteElement.append( (routeElement: element, animated: animated) ) - completionHandler() + completion() return MockRoutable() } func pop( _ element: RouteElement, animated: Bool, - completionHandler: @escaping RoutingCompletionHandler) { + completion: @escaping RoutingCompletion) { callsToPopRouteElement.append( (routeElement: element, animated: animated) ) - completionHandler() + completion() } func change( _ from: RouteElement, to: RouteElement, animated: Bool, - completionHandler: @escaping RoutingCompletionHandler + completion: @escaping RoutingCompletion ) -> Routable { - completionHandler() + completion() callsToChangeRouteElement.append((from: from, to: to, animated: animated)) @@ -95,7 +95,7 @@ class SwiftFlowRouterIntegrationTests: QuickSpec { var called = false func push(_ element: RouteElement, - completionHandler: RoutingCompletionHandler) -> Routable { + completion: RoutingCompletion) -> Routable { called = true return MockRoutable() } @@ -123,10 +123,10 @@ class SwiftFlowRouterIntegrationTests: QuickSpec { self.calledWithElement = calledWithElement } - func push(_ element: RouteElement, animated: Bool, completionHandler: @escaping RoutingCompletionHandler) -> Routable { + func push(_ element: RouteElement, animated: Bool, completion: @escaping RoutingCompletion) -> Routable { calledWithElement(element) - completionHandler() + completion() return MockRoutable() } @@ -159,10 +159,10 @@ class SwiftFlowRouterIntegrationTests: QuickSpec { self.calledWithElement = calledWithElement } - func push(_ element: RouteElement, animated: Bool, completionHandler: @escaping RoutingCompletionHandler) -> Routable { + func push(_ element: RouteElement, animated: Bool, completion: @escaping RoutingCompletion) -> Routable { calledWithElement(element) - completionHandler() + completion() return MockRoutable() } } @@ -183,8 +183,8 @@ class SwiftFlowRouterIntegrationTests: QuickSpec { func push(_ element: RouteElement, animated: Bool, - completionHandler: @escaping RoutingCompletionHandler) -> Routable { - completionHandler() + completion: @escaping RoutingCompletion) -> Routable { + completion() return injectedRoutable } } @@ -222,9 +222,9 @@ class SwiftFlowRouterIntegrationTests: QuickSpec { expect(data).toEventually(equal("UserID_10")) } - + } - + } describe("configuring animated/unanimated navigation") { @@ -277,5 +277,5 @@ class SwiftFlowRouterIntegrationTests: QuickSpec { } - + } From c3c8c3156c4c911dfa7961319d1772da76bf6602 Mon Sep 17 00:00:00 2001 From: Henrik Lineholm Date: Thu, 1 Aug 2019 09:38:42 +0200 Subject: [PATCH 3/4] Reline Routable Protocol --- ReSwiftRouter/Routable.swift | 42 ++++++++++-------------------------- 1 file changed, 11 insertions(+), 31 deletions(-) diff --git a/ReSwiftRouter/Routable.swift b/ReSwiftRouter/Routable.swift index 9590aaf..77a3e0f 100644 --- a/ReSwiftRouter/Routable.swift +++ b/ReSwiftRouter/Routable.swift @@ -10,46 +10,26 @@ public typealias RoutingCompletion = () -> Void public protocol Routable { - func push( - _ element: RouteElement, - animated: Bool, - completion: @escaping RoutingCompletion) -> Routable - - func pop( - _ element: RouteElement, - animated: Bool, - completion: @escaping RoutingCompletion) - - func change( - _ from: RouteElement, - to: RouteElement, - animated: Bool, - completion: @escaping RoutingCompletion) -> Routable + func push(_ element: RouteElement, animated: Bool, completion: @escaping RoutingCompletion) -> Routable + + func pop(_ element: RouteElement, animated: Bool, completion: @escaping RoutingCompletion) + + func change(_ from: RouteElement, to: RouteElement, animated: Bool, completion: @escaping RoutingCompletion) -> Routable } extension Routable { - public func push( - _ element: RouteElement, - animated: Bool, - completion: @escaping RoutingCompletion) -> Routable { - fatalError("This routable cannot push elements. You have not implemented it. (Asked \(type(of: self)) to push \(element))") + public func push(_ element: RouteElement, animated: Bool, completion: @escaping RoutingCompletion) -> Routable { + fatalError("This routable cannot push elements. You have not implemented it. (Asked \(type(of: self)) to push \(element))") } - public func pop( - _ element: RouteElement, - animated: Bool, - completion: @escaping RoutingCompletion) { - fatalError("This routable cannot pop elements. You have not implemented it. (Asked \(type(of: self)) to pop \(element))") + public func pop(_ element: RouteElement, animated: Bool, completion: @escaping RoutingCompletion) { + fatalError("This routable cannot pop elements. You have not implemented it. (Asked \(type(of: self)) to pop \(element))") } - public func change( - _ from: RouteElement, - to: RouteElement, - animated: Bool, - completion: @escaping RoutingCompletion) -> Routable { - fatalError("This routable cannot change elements. You have not implemented it. (Asked \(type(of: self)) to change from \(from) to \(to))") + public func change(_ from: RouteElement, to: RouteElement, animated: Bool, completion: @escaping RoutingCompletion) -> Routable { + fatalError("This routable cannot change elements. You have not implemented it. (Asked \(type(of: self)) to change from \(from) to \(to))") } } From 38f72860455ab20b7c02bcd5347cffc73cf242cc Mon Sep 17 00:00:00 2001 From: Christian Tietze Date: Wed, 7 Aug 2019 18:08:36 +0200 Subject: [PATCH 4/4] add PR to Changelog --- Changelog.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Changelog.md b/Changelog.md index d3a3ca4..a8307c2 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,9 @@ # Upcoming +**Breaking API Changes:** +- Rename Components of the `Routable` protocol (#107) - @hlineholm + + # 0.7.0 *Released: 2019-08-01*