diff --git a/Sources/Compass.swift b/Sources/Compass.swift index 256973d..743296c 100644 --- a/Sources/Compass.swift +++ b/Sources/Compass.swift @@ -18,11 +18,11 @@ public struct Compass { public static var routes = [String]() - public static func parse(url: NSURL, fragments: [String : AnyObject] = [:]) -> Location? { + public static func parse(url: NSURL, payload: Any? = nil) -> Location? { let path = url.absoluteString.substringFromIndex(scheme.endIndex) guard !(path.containsString("?") || path.containsString("#")) else { - return parseAsURL(url, fragments: fragments) + return parseAsURL(url, payload: payload) } let results: [Result] = routes.flatMap { @@ -36,13 +36,13 @@ public struct Compass { } if let result = results.first { - return Location(path: result.route, arguments: result.arguments, fragments: fragments) + return Location(path: result.route, arguments: result.arguments, payload: payload) } return nil } - static func parseAsURL(url: NSURL, fragments: [String : AnyObject] = [:]) -> Location? { + static func parseAsURL(url: NSURL, payload: Any? = nil) -> Location? { guard let route = url.host else { return nil } let urlComponents = NSURLComponents(URL: url, resolvingAgainstBaseURL: false) @@ -56,7 +56,7 @@ public struct Compass { arguments = fragment.queryParameters() } - return Location(path: route, arguments: arguments, fragments: fragments) + return Location(path: route, arguments: arguments, payload: payload) } static func findMatch(routeString: String, pathString: String) -> Result? { diff --git a/Sources/Location.swift b/Sources/Location.swift index 5f26382..28dce93 100644 --- a/Sources/Location.swift +++ b/Sources/Location.swift @@ -2,15 +2,15 @@ public struct Location { public let path: String public let arguments: [String: String] - public let fragments: [String: AnyObject] + public let payload: Any? public var scheme: String { return Compass.scheme } - public init(path: String, arguments: [String: String] = [:], fragments: [String: AnyObject] = [:]) { + public init(path: String, arguments: [String: String] = [:], payload: Any? = nil) { self.path = path self.arguments = arguments - self.fragments = fragments + self.payload = payload } } diff --git a/Sources/Router.swift b/Sources/Router.swift index ac05da5..94891a2 100644 --- a/Sources/Router.swift +++ b/Sources/Router.swift @@ -1,16 +1,36 @@ +public enum RouteError: ErrorType { + case NotFound + case InvalidArguments(Location) + case InvalidPayload(Location) +} + public protocol Routable { - func navigate(to location: Location, from currentController: Controller) + func navigate(to location: Location, from currentController: Controller) throws +} + +public protocol ErrorRoutable { + + func handle(routeError: ErrorType, from currentController: Controller) } public struct Router: Routable { public var routes = [String: Routable]() + public var errorRoute: ErrorRoutable? public init() {} public func navigate(to location: Location, from currentController: Controller) { - guard let route = routes[location.path] else { return } - route.navigate(to: location, from: currentController) + guard let route = routes[location.path] else { + errorRoute?.handle(RouteError.NotFound, from: currentController) + return + } + + do { + try route.navigate(to: location, from: currentController) + } catch { + errorRoute?.handle(error, from: currentController) + } } } diff --git a/Tests/Compass/CompassTests.swift b/Tests/Compass/CompassTests.swift index d1deab3..21a3fe0 100644 --- a/Tests/Compass/CompassTests.swift +++ b/Tests/Compass/CompassTests.swift @@ -38,17 +38,20 @@ class CompassTests: XCTestCase { XCTAssertEqual(location.arguments["user"], "testUser") } - func testParseFragments() { + func testParsePayload() { let url = NSURL(string: "compassTests://profile:testUser")! - guard let location = Compass.parse(url, fragments: ["meta" : "foo"]) else { + typealias Payload = (firstName: String, lastName: String) + + guard let location = Compass.parse(url, payload: Payload(firstName: "foo", lastName: "bar")) else { XCTFail("Compass parsing failed") return } XCTAssertEqual("profile:{user}", location.path) XCTAssertEqual(location.arguments["user"], "testUser") - XCTAssertEqual("foo" , location.fragments["meta"] as? String) + XCTAssertEqual("foo" , (location.payload as? Payload)?.firstName) + XCTAssertEqual("bar" , (location.payload as? Payload)?.lastName) } func testParseRouteConcreateMatchCount() { diff --git a/Tests/Compass/Helpers.swift b/Tests/Compass/Helpers.swift index 2709968..68e7061 100644 --- a/Tests/Compass/Helpers.swift +++ b/Tests/Compass/Helpers.swift @@ -7,11 +7,31 @@ class TestRoute: Routable { var resolved = false - func navigate(to location: Location, from currentController: Controller) { + func navigate(to location: Location, from currentController: Controller) throws { resolved = true } } +class ThrowableRoute: Routable { + + enum Error: ErrorType { + case Unknown + } + + func navigate(to location: Location, from currentController: Controller) throws { + throw Error.Unknown + } +} + +class ErrorRoute: ErrorRoutable { + + var error: ErrorType? + + func handle(routeError: ErrorType, from currentController: Controller) { + error = routeError + } +} + // MARK: - Shuffle extension CollectionType { diff --git a/Tests/Compass/RouterTests.swift b/Tests/Compass/RouterTests.swift index a14dd15..4acd51e 100644 --- a/Tests/Compass/RouterTests.swift +++ b/Tests/Compass/RouterTests.swift @@ -5,12 +5,17 @@ import XCTest class RouterTests: XCTestCase { var router: Router! - var controller = Controller() var route: TestRoute! + var controller: Controller! + var errorRoute: ErrorRoute! override func setUp() { router = Router() route = TestRoute() + controller = Controller() + errorRoute = ErrorRoute() + + router.errorRoute = errorRoute } func testNavigateIfRouteRegistered() { @@ -18,11 +23,20 @@ class RouterTests: XCTestCase { router.navigate(to: Location(path: "test"), from: controller) XCTAssertTrue(route.resolved) + XCTAssertNil(errorRoute.error) } func testNavigateIfRouteNotRegistered() { router.navigate(to: Location(path: "test"), from: controller) XCTAssertFalse(route.resolved) + XCTAssertTrue(errorRoute.error is RouteError) + } + + func testNavigateIfRouteThrowsError() { + router.routes["throw"] = ThrowableRoute() + router.navigate(to: Location(path: "throw"), from: controller) + + XCTAssertTrue(errorRoute.error is ThrowableRoute.Error) } }