From a420a7f9fe097bc9e0135a33dad45bc8c2985a98 Mon Sep 17 00:00:00 2001 From: giginet Date: Tue, 25 Jun 2019 22:13:26 +0900 Subject: [PATCH 01/10] Support prefixed URL --- Sources/Crossroad/PatternURL.swift | 8 +++++ Sources/Crossroad/Router.swift | 50 ++++++++++++++++++++++++------ 2 files changed, 48 insertions(+), 10 deletions(-) diff --git a/Sources/Crossroad/PatternURL.swift b/Sources/Crossroad/PatternURL.swift index 84a0ea8..0f19c01 100644 --- a/Sources/Crossroad/PatternURL.swift +++ b/Sources/Crossroad/PatternURL.swift @@ -37,4 +37,12 @@ internal struct PatternURL { pathComponents = [] } } + + var fullPath: String { + return "\(scheme)\(PatternURL.schemeSeparator)\(host)\(PatternURL.pathSeparator)\(pathComponents.joined(separator: "/"))" + } + + func hasPrefix(url: URL) -> Bool { + return fullPath.hasPrefix(url.absoluteString) + } } diff --git a/Sources/Crossroad/Router.swift b/Sources/Crossroad/Router.swift index 5954d79..d8b9c10 100644 --- a/Sources/Crossroad/Router.swift +++ b/Sources/Crossroad/Router.swift @@ -3,16 +3,37 @@ import Foundation public typealias SimpleRouter = Router public final class Router { - private let scheme: String + private enum Prefix { + case scheme(String) + case url(URL) + } + private let prefix: Prefix private var routes: [Route] = [] public init(scheme: String) { - self.scheme = scheme + prefix = .scheme(scheme) + } + + public init(url: URL) { + prefix = .url(url) + } + + private func isValidURLPattern(_ patternURL: PatternURL) -> Bool { + switch prefix { + case .scheme(let scheme): + return scheme != patternURL.scheme + case .url(let url): + return patternURL.hasPrefix(url: url) + } + } + + private func canRespond(to url: URL) -> Bool { + return true } internal func register(_ route: Route) { - if scheme != route.patternURL.scheme { - assertionFailure("Router and pattern must have the same schemes. expect: \(scheme), actual: \(route.patternURL.scheme)") + if isValidURLPattern(route.patternURL) { + assertionFailure("Unexpected URL Pattern") } else { routes.append(route) } @@ -20,14 +41,14 @@ public final class Router { @discardableResult public func openIfPossible(_ url: URL, userInfo: UserInfo) -> Bool { - if scheme != url.scheme { + if !canRespond(to: url) { return false } return routes.first { $0.openIfPossible(url, userInfo: userInfo) } != nil } public func responds(to url: URL, userInfo: UserInfo) -> Bool { - if scheme != url.scheme { + if !canRespond(to: url) { return false } return routes.first { $0.responds(to: url, userInfo: userInfo) } != nil @@ -36,10 +57,19 @@ public final class Router { public func register(_ routes: [(String, Route.Handler)]) { for (pattern, handler) in routes { let patternURLString: String - if pattern.hasPrefix("\(scheme)://") { - patternURLString = pattern - } else { - patternURLString = "\(scheme)://\(pattern)" + switch prefix { + case .scheme(let scheme): + if pattern.hasPrefix("\(scheme)://") { + patternURLString = pattern + } else { + patternURLString = "\(scheme)://\(pattern)" + } + case .url(let url): + if pattern.hasPrefix(url.absoluteString) { + patternURLString = pattern + } else { + patternURLString = url.appendingPathComponent(pattern).absoluteString + } } guard let patternURL = PatternURL(string: patternURLString) else { assertionFailure("\(pattern) is invalid") From 79e6337b95bb957b38d26a388935eea607bc7e83 Mon Sep 17 00:00:00 2001 From: giginet Date: Tue, 25 Jun 2019 22:25:05 +0900 Subject: [PATCH 02/10] Add tests for hasPrefix --- Sources/Crossroad/PatternURL.swift | 8 +++----- Tests/CrossroadTests/PatternURLTests.swift | 9 +++++++++ 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/Sources/Crossroad/PatternURL.swift b/Sources/Crossroad/PatternURL.swift index 0f19c01..e603b01 100644 --- a/Sources/Crossroad/PatternURL.swift +++ b/Sources/Crossroad/PatternURL.swift @@ -8,6 +8,7 @@ internal struct PatternURL { let scheme: String let host: String let pathComponents: [String] + let patternString: String private static let schemeSeparator = "://" private static let pathSeparator = "/" @@ -24,6 +25,7 @@ internal struct PatternURL { } self.scheme = scheme self.host = host + self.patternString = string if components.count > 1 { let left = components[1 ..< components.count] // In URL, pathComponents includes the starting "/" so do the same. @@ -38,11 +40,7 @@ internal struct PatternURL { } } - var fullPath: String { - return "\(scheme)\(PatternURL.schemeSeparator)\(host)\(PatternURL.pathSeparator)\(pathComponents.joined(separator: "/"))" - } - func hasPrefix(url: URL) -> Bool { - return fullPath.hasPrefix(url.absoluteString) + return patternString.hasPrefix(url.absoluteString) } } diff --git a/Tests/CrossroadTests/PatternURLTests.swift b/Tests/CrossroadTests/PatternURLTests.swift index ed05ac1..d3d4ad9 100644 --- a/Tests/CrossroadTests/PatternURLTests.swift +++ b/Tests/CrossroadTests/PatternURLTests.swift @@ -45,4 +45,13 @@ final class PatternURLTests: XCTestCase { assertShouldFailed("without_schema") assertShouldFailed("invalid_schema://////aaaaaaa") } + + func testHasPrefix() { + XCTAssertTrue(PatternURL(string: "https://example.com")!.hasPrefix(url: URL(string: "https://example.com")!)) + XCTAssertTrue(PatternURL(string: "https://example.com/users/:id")!.hasPrefix(url: URL(string: "https://example.com")!)) + XCTAssertTrue(PatternURL(string: "https://example.com/users/:id")!.hasPrefix(url: URL(string: "https://example.com/")!)) + XCTAssertTrue(PatternURL(string: "https://example.com/users/:id")!.hasPrefix(url: URL(string: "https://example.com/users")!)) + XCTAssertTrue(PatternURL(string: "https://example.com/users/:id")!.hasPrefix(url: URL(string: "https://example.com/users/")!)) + XCTAssertFalse(PatternURL(string: "https://example.com/users/:id")!.hasPrefix(url: URL(string: "https://example.com/users/10")!)) + } } From 1a2be6ea310399219d3e3c5245bc977a57d794c3 Mon Sep 17 00:00:00 2001 From: giginet Date: Tue, 25 Jun 2019 22:39:24 +0900 Subject: [PATCH 03/10] Implement canRespond --- Sources/Crossroad/Router.swift | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/Sources/Crossroad/Router.swift b/Sources/Crossroad/Router.swift index d8b9c10..87e33d3 100644 --- a/Sources/Crossroad/Router.swift +++ b/Sources/Crossroad/Router.swift @@ -21,21 +21,26 @@ public final class Router { private func isValidURLPattern(_ patternURL: PatternURL) -> Bool { switch prefix { case .scheme(let scheme): - return scheme != patternURL.scheme + return scheme == patternURL.scheme case .url(let url): return patternURL.hasPrefix(url: url) } } private func canRespond(to url: URL) -> Bool { - return true + switch prefix { + case .scheme(let scheme): + return scheme == url.scheme + case .url(let prefixURL): + return url.absoluteString.hasPrefix(prefixURL.absoluteString) + } } internal func register(_ route: Route) { if isValidURLPattern(route.patternURL) { - assertionFailure("Unexpected URL Pattern") - } else { routes.append(route) + } else { + assertionFailure("Unexpected URL Pattern") } } From 02aca9c11a4d3a4e8a6aa4b6fb00ba60d4050c6e Mon Sep 17 00:00:00 2001 From: giginet Date: Tue, 25 Jun 2019 23:11:50 +0900 Subject: [PATCH 04/10] Add tests for URL prefix --- Tests/CrossroadTests/RouterTests.swift | 47 ++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/Tests/CrossroadTests/RouterTests.swift b/Tests/CrossroadTests/RouterTests.swift index b0f3a35..8abfa5e 100644 --- a/Tests/CrossroadTests/RouterTests.swift +++ b/Tests/CrossroadTests/RouterTests.swift @@ -29,6 +29,30 @@ final class RouterTest: XCTestCase { XCTAssertFalse(router.responds(to: URL(string: "spam/ham")!)) } + func testCanRespondWithURLPrefix() { + let router = SimpleRouter(url: URL(string: "https://example.com")!) + router.register([ + ("https://example.com/static", { _ in true }), + ("https://example.com/foo/bar", { _ in true }), + ("https://example.com/spam/ham", { _ in false }), + ("https://example.com/:keyword", { _ in true }), + ("https://example.com/foo/:keyword", { _ in true }), + ]) + XCTAssertTrue(router.responds(to: URL(string: "https://example.com/static")!)) + XCTAssertTrue(router.responds(to: URL(string: "https://example.com/foo")!)) + XCTAssertTrue(router.responds(to: URL(string: "https://example.com/foo/bar")!)) + XCTAssertTrue(router.responds(to: URL(string: "https://example.com/foo/10000")!)) + XCTAssertFalse(router.responds(to: URL(string: "https://example.com/aaa/bbb")!)) + XCTAssertFalse(router.responds(to: URL(string: "nothttps://example.com/aaa/bbb")!)) + XCTAssertTrue(router.responds(to: URL(string: "https://example.com/spam/ham")!)) + XCTAssertFalse(router.responds(to: URL(string: "static")!)) + XCTAssertFalse(router.responds(to: URL(string: "foo")!)) + XCTAssertFalse(router.responds(to: URL(string: "foo/bar")!)) + XCTAssertFalse(router.responds(to: URL(string: "foo/10000")!)) + XCTAssertFalse(router.responds(to: URL(string: "aaa/bbb")!)) + XCTAssertFalse(router.responds(to: URL(string: "spam/ham")!)) + } + func testCanRespondWithoutScheme() { let router = SimpleRouter(scheme: scheme) router.register([ @@ -52,6 +76,29 @@ final class RouterTest: XCTestCase { XCTAssertFalse(router.responds(to: URL(string: "spam/ham")!)) } + func testCanRespondWithoutSchemeWithURLPrefix() { + let router = SimpleRouter(url: URL(string: "https://example.com/")!) + router.register([ + ("static", { _ in true }), + ("foo/bar", { _ in true }), + ("spam/ham", { _ in false }), + (":keyword", { _ in true }), + ("foo/:keyword", { _ in true }), + ]) + XCTAssertTrue(router.responds(to: URL(string: "https://example.com/static")!)) + XCTAssertTrue(router.responds(to: URL(string: "https://example.com/foo")!)) + XCTAssertTrue(router.responds(to: URL(string: "https://example.com/foo/bar")!)) + XCTAssertTrue(router.responds(to: URL(string: "https://example.com/foo/10000")!)) + XCTAssertFalse(router.responds(to: URL(string: "nothttps://example.com/aaa/bbb")!)) + XCTAssertTrue(router.responds(to: URL(string: "https://example.com/spam/ham")!)) + XCTAssertFalse(router.responds(to: URL(string: "static")!)) + XCTAssertFalse(router.responds(to: URL(string: "foo")!)) + XCTAssertFalse(router.responds(to: URL(string: "foo/bar")!)) + XCTAssertFalse(router.responds(to: URL(string: "foo/10000")!)) + XCTAssertFalse(router.responds(to: URL(string: "aaa/bbb")!)) + XCTAssertFalse(router.responds(to: URL(string: "spam/ham")!)) + } + func testHandle() { let router = SimpleRouter(scheme: scheme) let expectation = self.expectation(description: "Should called handler four times") From 2c2b92cfb5f65bf286ffcd0971d1b993e82bbfbe Mon Sep 17 00:00:00 2001 From: giginet Date: Wed, 26 Jun 2019 18:06:13 +0900 Subject: [PATCH 05/10] =?UTF-8?q?Add=20tests=20=F0=9F=94=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Tests/CrossroadTests/RouterTests.swift | 234 ++++++++++++++++++++++++- 1 file changed, 228 insertions(+), 6 deletions(-) diff --git a/Tests/CrossroadTests/RouterTests.swift b/Tests/CrossroadTests/RouterTests.swift index 8abfa5e..519e6cc 100644 --- a/Tests/CrossroadTests/RouterTests.swift +++ b/Tests/CrossroadTests/RouterTests.swift @@ -53,7 +53,7 @@ final class RouterTest: XCTestCase { XCTAssertFalse(router.responds(to: URL(string: "spam/ham")!)) } - func testCanRespondWithoutScheme() { + func testCanRespondWithoutPrefix() { let router = SimpleRouter(scheme: scheme) router.register([ ("static", { _ in true }), @@ -76,7 +76,7 @@ final class RouterTest: XCTestCase { XCTAssertFalse(router.responds(to: URL(string: "spam/ham")!)) } - func testCanRespondWithoutSchemeWithURLPrefix() { + func testCanRespondWithoutPrefixWithURLPrefix() { let router = SimpleRouter(url: URL(string: "https://example.com/")!) router.register([ ("static", { _ in true }), @@ -143,7 +143,51 @@ final class RouterTest: XCTestCase { wait(for: [expectation], timeout: 2.0) } - func testHandleWithoutScheme() { + func testHandleWithURLPrefix() { + let router = SimpleRouter(url: URL(string: "https://example.com")!) + let expectation = self.expectation(description: "Should called handler four times") + expectation.expectedFulfillmentCount = 4 + router.register([ + ("https://example.com/static", { context in + XCTAssertEqual(context.url, URL(string: "https://example.com/static")!) + expectation.fulfill() + return true + }), + ("https://example.com/foo/bar", { context in + XCTAssertEqual(context.parameter(for: "param0"), 123) + XCTAssertEqual(context.url, URL(string: "https://example.com/foo/bar?param0=123")!) + expectation.fulfill() + return true + }), + ("https://example.com/:keyword", { context in + XCTAssertEqual(context.url, URL(string: "https://example.com/hoge")!) + XCTAssertEqual(try? context.argument(for: "keyword"), "hoge") + expectation.fulfill() + return true + }), + ("https://example.com/foo/:keyword/:keyword2", { context in + XCTAssertEqual(context.url, URL(string: "https://example.com/foo/hoge/fuga")!) + XCTAssertEqual(try? context.argument(for: "keyword"), "hoge") + XCTAssertEqual(try? context.argument(for: "keyword2"), "fuga") + expectation.fulfill() + return true + }), + ]) + XCTAssertTrue(router.openIfPossible(URL(string: "https://example.com/static")!)) + XCTAssertTrue(router.openIfPossible(URL(string: "https://example.com/foo/bar?param0=123")!)) + XCTAssertTrue(router.openIfPossible(URL(string: "https://example.com/hoge")!)) + XCTAssertTrue(router.openIfPossible(URL(string: "https://example.com/foo/hoge/fuga")!)) + XCTAssertFalse(router.openIfPossible(URL(string: "https://example.com/spam/ham")!)) + XCTAssertFalse(router.openIfPossible(URL(string: "nothttps://example.com/static")!)) + XCTAssertFalse(router.openIfPossible(URL(string: "static")!)) + XCTAssertFalse(router.openIfPossible(URL(string: "foo/bar?param0=123")!)) + XCTAssertFalse(router.openIfPossible(URL(string: "hoge")!)) + XCTAssertFalse(router.openIfPossible(URL(string: "foo/hoge/fuga")!)) + XCTAssertFalse(router.openIfPossible(URL(string: "spam/ham")!)) + wait(for: [expectation], timeout: 2.0) + } + + func testHandleWithoutPrefix() { let router = SimpleRouter(scheme: scheme) let expectation = self.expectation(description: "Should called handler four times") expectation.expectedFulfillmentCount = 4 @@ -187,6 +231,50 @@ final class RouterTest: XCTestCase { wait(for: [expectation], timeout: 2.0) } + func testHandleWithoutPrefixWithURLPrefix() { + let router = SimpleRouter(url: URL(string: "https://example.com")!) + let expectation = self.expectation(description: "Should called handler four times") + expectation.expectedFulfillmentCount = 4 + router.register([ + ("static", { context in + XCTAssertEqual(context.url, URL(string: "https://example.com/static")!) + expectation.fulfill() + return true + }), + ("foo/bar", { context in + XCTAssertEqual(context.parameter(for: "param0"), 123) + XCTAssertEqual(context.url, URL(string: "https://example.com/foo/bar?param0=123")!) + expectation.fulfill() + return true + }), + (":keyword", { context in + XCTAssertEqual(context.url, URL(string: "https://example.com/hoge")!) + XCTAssertEqual(try? context.argument(for: "keyword"), "hoge") + expectation.fulfill() + return true + }), + ("foo/:keyword/:keyword2", { context in + XCTAssertEqual(context.url, URL(string: "https://example.com/foo/hoge/fuga")!) + XCTAssertEqual(try? context.argument(for: "keyword"), "hoge") + XCTAssertEqual(try? context.argument(for: "keyword2"), "fuga") + expectation.fulfill() + return true + }), + ]) + XCTAssertTrue(router.openIfPossible(URL(string: "https://example.com/static")!)) + XCTAssertTrue(router.openIfPossible(URL(string: "https://example.com/foo/bar?param0=123")!)) + XCTAssertTrue(router.openIfPossible(URL(string: "https://example.com/hoge")!)) + XCTAssertTrue(router.openIfPossible(URL(string: "https://example.com/foo/hoge/fuga")!)) + XCTAssertFalse(router.openIfPossible(URL(string: "https://example.com/spam/ham")!)) + XCTAssertFalse(router.openIfPossible(URL(string: "nothttps://example.com/static")!)) + XCTAssertFalse(router.openIfPossible(URL(string: "static")!)) + XCTAssertFalse(router.openIfPossible(URL(string: "foo/bar?param0=123")!)) + XCTAssertFalse(router.openIfPossible(URL(string: "hoge")!)) + XCTAssertFalse(router.openIfPossible(URL(string: "foo/hoge/fuga")!)) + XCTAssertFalse(router.openIfPossible(URL(string: "spam/ham")!)) + wait(for: [expectation], timeout: 2.0) + } + func testHandlerWithSamePatterns() { let router = SimpleRouter(scheme: scheme) let idExpectation = self.expectation(description: "Should called handler with ID") @@ -216,7 +304,36 @@ final class RouterTest: XCTestCase { wait(for: [idExpectation, keywordExpectation], timeout: 2.0) } - func testHandlerWithSamePatternsWithoutScheme() { + func testHandlerWithSamePatternsWithURLPrefix() { + let router = SimpleRouter(url: URL(string: "https://example.com/")!) + let idExpectation = self.expectation(description: "Should called handler with ID") + let keywordExpectation = self.expectation(description: "Should called handler with keyword") + router.register([ + ("https://example.com/foo/:id", { context in + guard let id: Int = try? context.argument(for: "id") else { + return false + } + XCTAssertEqual(context.url, URL(string: "https://example.com/foo/42")!) + XCTAssertEqual(id, 42) + idExpectation.fulfill() + return true + }), + ("https://example.com/foo/:keyword", { context in + let keyword: String = try! context.argument(for: "keyword") + XCTAssertEqual(context.url, URL(string: "https://example.com/foo/bar")!) + XCTAssertEqual(keyword, "bar") + keywordExpectation.fulfill() + return true + }), + ]) + XCTAssertTrue(router.openIfPossible(URL(string: "https://example.com/foo/42")!)) + XCTAssertTrue(router.openIfPossible(URL(string: "https://example.com/foo/bar")!)) + XCTAssertFalse(router.openIfPossible(URL(string: "foo/42")!)) + XCTAssertFalse(router.openIfPossible(URL(string: "foo/bar")!)) + wait(for: [idExpectation, keywordExpectation], timeout: 2.0) + } + + func testHandlerWithSamePatternsWithoutPrefix() { let router = SimpleRouter(scheme: scheme) let idExpectation = self.expectation(description: "Should called handler with ID") let keywordExpectation = self.expectation(description: "Should called handler with keyword") @@ -245,6 +362,35 @@ final class RouterTest: XCTestCase { wait(for: [idExpectation, keywordExpectation], timeout: 2.0) } + func testHandlerWithSamePatternsWithoutPrefixWithURLPrefix() { + let router = SimpleRouter(url: URL(string: "https://example.com/")!) + let idExpectation = self.expectation(description: "Should called handler with ID") + let keywordExpectation = self.expectation(description: "Should called handler with keyword") + router.register([ + ("foo/:id", { context in + guard let id: Int = try? context.argument(for: "id") else { + return false + } + XCTAssertEqual(context.url, URL(string: "https://example.com/foo/42")!) + XCTAssertEqual(id, 42) + idExpectation.fulfill() + return true + }), + ("foo/:keyword", { context in + let keyword: String = try! context.argument(for: "keyword") + XCTAssertEqual(context.url, URL(string: "https://example.com/foo/bar")!) + XCTAssertEqual(keyword, "bar") + keywordExpectation.fulfill() + return true + }), + ]) + XCTAssertTrue(router.openIfPossible(URL(string: "https://example.com/foo/42")!)) + XCTAssertTrue(router.openIfPossible(URL(string: "https://example.com/foo/bar")!)) + XCTAssertFalse(router.openIfPossible(URL(string: "foo/42")!)) + XCTAssertFalse(router.openIfPossible(URL(string: "foo/bar")!)) + wait(for: [idExpectation, keywordExpectation], timeout: 2.0) + } + func testHandleReturnsFalse() { let router = SimpleRouter(scheme: scheme) let expectation = self.expectation(description: "Should called handler twice") @@ -265,7 +411,27 @@ final class RouterTest: XCTestCase { wait(for: [expectation], timeout: 2.0) } - func testHandleReturnsFalseWithoutScheme() { + func testHandleReturnsFalseWithURLPrefix() { + let router = SimpleRouter(url: URL(string: "https://example.com/")!) + let expectation = self.expectation(description: "Should called handler twice") + expectation.expectedFulfillmentCount = 2 + router.register([ + ("https://example.com/foo/bar", { _ in + expectation.fulfill() + return false + }), + ("https://example.com/foo/:keyword", { context in + XCTAssertEqual(try? context.argument(for: "keyword"), "bar") + expectation.fulfill() + return true + }), + ]) + XCTAssertTrue(router.openIfPossible(URL(string: "https://example.com/foo/bar")!)) + XCTAssertFalse(router.openIfPossible(URL(string: "foo/bar")!)) + wait(for: [expectation], timeout: 2.0) + } + + func testHandleReturnsFalseWithoutPrefix() { let router = SimpleRouter(scheme: scheme) let expectation = self.expectation(description: "Should called handler twice") expectation.expectedFulfillmentCount = 2 @@ -285,6 +451,26 @@ final class RouterTest: XCTestCase { wait(for: [expectation], timeout: 2.0) } + func testHandleReturnsFalseWithoutPrefixWithURLPrefix() { + let router = SimpleRouter(url: URL(string: "https://example.com/")!) + let expectation = self.expectation(description: "Should called handler twice") + expectation.expectedFulfillmentCount = 2 + router.register([ + ("foo/bar", { _ in + expectation.fulfill() + return false + }), + ("foo/:keyword", { context in + XCTAssertEqual(try? context.argument(for: "keyword"), "bar") + expectation.fulfill() + return true + }), + ]) + XCTAssertTrue(router.openIfPossible(URL(string: "https://example.com/foo/bar")!)) + XCTAssertFalse(router.openIfPossible(URL(string: "foo/bar")!)) + wait(for: [expectation], timeout: 2.0) + } + func testWithUserInfo() { struct UserInfo { let value: Int @@ -303,7 +489,25 @@ final class RouterTest: XCTestCase { XCTAssertEqual(userInfo?.value, 42) } - func testWithUserInfoWithoutScheme() { + func testWithUserInfoWithURLPrefix() { + struct UserInfo { + let value: Int + } + let router = Router(url: URL(string: "https://example.com/")!) + var userInfo: UserInfo? + router.register([ + ("https://example.com/static", { context in + XCTAssertEqual(context.url, URL(string: "https://example.com/static")!) + userInfo = context.userInfo + return true + }), + ]) + XCTAssertTrue(router.openIfPossible(URL(string: "https://example.com/static")!, userInfo: UserInfo(value: 42))) + XCTAssertFalse(router.openIfPossible(URL(string: "static")!, userInfo: UserInfo(value: 42))) + XCTAssertEqual(userInfo?.value, 42) + } + + func testWithUserInfoWithoutPrefix() { struct UserInfo { let value: Int } @@ -320,4 +524,22 @@ final class RouterTest: XCTestCase { XCTAssertFalse(router.openIfPossible(URL(string: "static")!, userInfo: UserInfo(value: 42))) XCTAssertEqual(userInfo?.value, 42) } + + func testWithUserInfoWithoutPrefixWithURLPrefix() { + struct UserInfo { + let value: Int + } + let router = Router(url: URL(string: "https://example.com/")!) + var userInfo: UserInfo? + router.register([ + ("static", { context in + XCTAssertEqual(context.url, URL(string: "https://example.com/static")!) + userInfo = context.userInfo + return true + }), + ]) + XCTAssertTrue(router.openIfPossible(URL(string: "https://example.com/static")!, userInfo: UserInfo(value: 42))) + XCTAssertFalse(router.openIfPossible(URL(string: "static")!, userInfo: UserInfo(value: 42))) + XCTAssertEqual(userInfo?.value, 42) + } } From b89bedbf189b2538c2307d3ed34f143234c11fd4 Mon Sep 17 00:00:00 2001 From: giginet Date: Wed, 26 Jun 2019 18:13:23 +0900 Subject: [PATCH 06/10] Add README --- README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.md b/README.md index ab0f4eb..49204f0 100644 --- a/README.md +++ b/README.md @@ -157,6 +157,16 @@ let userInfo = UserInfo(userID: User.current.id) router.openIfPossible(url, userInfo: userInfo) ``` +## Universal Links + +You can make routers handle with Universal Links. + +Of course, you can also use [Firebase Dynamic Link](https://firebase.google.com/docs/dynamic-links) or other similar services. + +```swift +let router = DefaultRouter(url: URL(string: "https://my-awesome-pokedex.com")!) +``` + ## Supported version Latest version of Crossroad requires Swift 5.0 or above. From 27ffee57dd86c718e030fbcbe506c8f1af1d8d2b Mon Sep 17 00:00:00 2001 From: giginet Date: Wed, 26 Jun 2019 18:13:44 +0900 Subject: [PATCH 07/10] Bump up version to 2.1.0 --- Crossroad.podspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Crossroad.podspec b/Crossroad.podspec index d5c5a7e..1c205dd 100644 --- a/Crossroad.podspec +++ b/Crossroad.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "Crossroad" - s.version = "2.0.0" + s.version = "2.1.0" s.summary = "Route URL schemes easily" s.description = <<-DESC Crossroad is an URL router focused on handling Custom URL Scheme. From 7e47ad7bb5e93a7220f800310393376257a29806 Mon Sep 17 00:00:00 2001 From: giginet Date: Wed, 26 Jun 2019 18:23:07 +0900 Subject: [PATCH 08/10] Add test case --- Tests/CrossroadTests/PatternURLTests.swift | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Tests/CrossroadTests/PatternURLTests.swift b/Tests/CrossroadTests/PatternURLTests.swift index d3d4ad9..be58fbf 100644 --- a/Tests/CrossroadTests/PatternURLTests.swift +++ b/Tests/CrossroadTests/PatternURLTests.swift @@ -48,6 +48,9 @@ final class PatternURLTests: XCTestCase { func testHasPrefix() { XCTAssertTrue(PatternURL(string: "https://example.com")!.hasPrefix(url: URL(string: "https://example.com")!)) + XCTAssertTrue(PatternURL(string: "https://example.com/")!.hasPrefix(url: URL(string: "https://example.com/")!)) + XCTAssertTrue(PatternURL(string: "https://example.com")!.hasPrefix(url: URL(string: "https://example.com/")!)) + XCTAssertTrue(PatternURL(string: "https://example.com/")!.hasPrefix(url: URL(string: "https://example.com")!)) XCTAssertTrue(PatternURL(string: "https://example.com/users/:id")!.hasPrefix(url: URL(string: "https://example.com")!)) XCTAssertTrue(PatternURL(string: "https://example.com/users/:id")!.hasPrefix(url: URL(string: "https://example.com/")!)) XCTAssertTrue(PatternURL(string: "https://example.com/users/:id")!.hasPrefix(url: URL(string: "https://example.com/users")!)) From 527cf8a81e33edaf7951c2c70891973c09b5e614 Mon Sep 17 00:00:00 2001 From: giginet Date: Wed, 26 Jun 2019 18:23:37 +0900 Subject: [PATCH 09/10] Disable file_length --- Tests/.swiftlint.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/Tests/.swiftlint.yml b/Tests/.swiftlint.yml index eda0e0b..2e07681 100644 --- a/Tests/.swiftlint.yml +++ b/Tests/.swiftlint.yml @@ -3,6 +3,7 @@ disabled_rules: - force_try - identifier_name - type_body_length + - file_length opt_in_rules: - trailing_comma trailing_comma: From 2c5a8f38f0cbd51c1aee3d68df2eb1e1c7edbb72 Mon Sep 17 00:00:00 2001 From: giginet Date: Wed, 26 Jun 2019 18:29:30 +0900 Subject: [PATCH 10/10] Remove test case --- Tests/CrossroadTests/PatternURLTests.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/Tests/CrossroadTests/PatternURLTests.swift b/Tests/CrossroadTests/PatternURLTests.swift index be58fbf..e3315ff 100644 --- a/Tests/CrossroadTests/PatternURLTests.swift +++ b/Tests/CrossroadTests/PatternURLTests.swift @@ -49,7 +49,6 @@ final class PatternURLTests: XCTestCase { func testHasPrefix() { XCTAssertTrue(PatternURL(string: "https://example.com")!.hasPrefix(url: URL(string: "https://example.com")!)) XCTAssertTrue(PatternURL(string: "https://example.com/")!.hasPrefix(url: URL(string: "https://example.com/")!)) - XCTAssertTrue(PatternURL(string: "https://example.com")!.hasPrefix(url: URL(string: "https://example.com/")!)) XCTAssertTrue(PatternURL(string: "https://example.com/")!.hasPrefix(url: URL(string: "https://example.com")!)) XCTAssertTrue(PatternURL(string: "https://example.com/users/:id")!.hasPrefix(url: URL(string: "https://example.com")!)) XCTAssertTrue(PatternURL(string: "https://example.com/users/:id")!.hasPrefix(url: URL(string: "https://example.com/")!))