Skip to content

Commit 2a8a55b

Browse files
authored
Merge pull request #14 from giginet/support-prefix-url
Support Universal Links
2 parents bbe4a1c + 2c5a8f3 commit 2a8a55b

File tree

7 files changed

+349
-17
lines changed

7 files changed

+349
-17
lines changed

Crossroad.podspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Pod::Spec.new do |s|
22
s.name = "Crossroad"
3-
s.version = "2.0.0"
3+
s.version = "2.1.0"
44
s.summary = "Route URL schemes easily"
55
s.description = <<-DESC
66
Crossroad is an URL router focused on handling Custom URL Scheme.

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,16 @@ let userInfo = UserInfo(userID: User.current.id)
157157
router.openIfPossible(url, userInfo: userInfo)
158158
```
159159

160+
## Universal Links
161+
162+
You can make routers handle with Universal Links.
163+
164+
Of course, you can also use [Firebase Dynamic Link](https://firebase.google.com/docs/dynamic-links) or other similar services.
165+
166+
```swift
167+
let router = DefaultRouter(url: URL(string: "https://my-awesome-pokedex.com")!)
168+
```
169+
160170
## Supported version
161171

162172
Latest version of Crossroad requires Swift 5.0 or above.

Sources/Crossroad/PatternURL.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ internal struct PatternURL {
88
let scheme: String
99
let host: String
1010
let pathComponents: [String]
11+
let patternString: String
1112

1213
private static let schemeSeparator = "://"
1314
private static let pathSeparator = "/"
@@ -24,6 +25,7 @@ internal struct PatternURL {
2425
}
2526
self.scheme = scheme
2627
self.host = host
28+
self.patternString = string
2729
if components.count > 1 {
2830
let left = components[1 ..< components.count]
2931
// In URL, pathComponents includes the starting "/" so do the same.
@@ -37,4 +39,8 @@ internal struct PatternURL {
3739
pathComponents = []
3840
}
3941
}
42+
43+
func hasPrefix(url: URL) -> Bool {
44+
return patternString.hasPrefix(url.absoluteString)
45+
}
4046
}

Sources/Crossroad/Router.swift

Lines changed: 46 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,31 +3,57 @@ import Foundation
33
public typealias SimpleRouter = Router<Void>
44

55
public final class Router<UserInfo> {
6-
private let scheme: String
6+
private enum Prefix {
7+
case scheme(String)
8+
case url(URL)
9+
}
10+
private let prefix: Prefix
711
private var routes: [Route<UserInfo>] = []
812

913
public init(scheme: String) {
10-
self.scheme = scheme
14+
prefix = .scheme(scheme)
15+
}
16+
17+
public init(url: URL) {
18+
prefix = .url(url)
19+
}
20+
21+
private func isValidURLPattern(_ patternURL: PatternURL) -> Bool {
22+
switch prefix {
23+
case .scheme(let scheme):
24+
return scheme == patternURL.scheme
25+
case .url(let url):
26+
return patternURL.hasPrefix(url: url)
27+
}
28+
}
29+
30+
private func canRespond(to url: URL) -> Bool {
31+
switch prefix {
32+
case .scheme(let scheme):
33+
return scheme == url.scheme
34+
case .url(let prefixURL):
35+
return url.absoluteString.hasPrefix(prefixURL.absoluteString)
36+
}
1137
}
1238

1339
internal func register(_ route: Route<UserInfo>) {
14-
if scheme != route.patternURL.scheme {
15-
assertionFailure("Router and pattern must have the same schemes. expect: \(scheme), actual: \(route.patternURL.scheme)")
16-
} else {
40+
if isValidURLPattern(route.patternURL) {
1741
routes.append(route)
42+
} else {
43+
assertionFailure("Unexpected URL Pattern")
1844
}
1945
}
2046

2147
@discardableResult
2248
public func openIfPossible(_ url: URL, userInfo: UserInfo) -> Bool {
23-
if scheme != url.scheme {
49+
if !canRespond(to: url) {
2450
return false
2551
}
2652
return routes.first { $0.openIfPossible(url, userInfo: userInfo) } != nil
2753
}
2854

2955
public func responds(to url: URL, userInfo: UserInfo) -> Bool {
30-
if scheme != url.scheme {
56+
if !canRespond(to: url) {
3157
return false
3258
}
3359
return routes.first { $0.responds(to: url, userInfo: userInfo) } != nil
@@ -36,10 +62,19 @@ public final class Router<UserInfo> {
3662
public func register(_ routes: [(String, Route<UserInfo>.Handler)]) {
3763
for (pattern, handler) in routes {
3864
let patternURLString: String
39-
if pattern.hasPrefix("\(scheme)://") {
40-
patternURLString = pattern
41-
} else {
42-
patternURLString = "\(scheme)://\(pattern)"
65+
switch prefix {
66+
case .scheme(let scheme):
67+
if pattern.hasPrefix("\(scheme)://") {
68+
patternURLString = pattern
69+
} else {
70+
patternURLString = "\(scheme)://\(pattern)"
71+
}
72+
case .url(let url):
73+
if pattern.hasPrefix(url.absoluteString) {
74+
patternURLString = pattern
75+
} else {
76+
patternURLString = url.appendingPathComponent(pattern).absoluteString
77+
}
4378
}
4479
guard let patternURL = PatternURL(string: patternURLString) else {
4580
assertionFailure("\(pattern) is invalid")

Tests/.swiftlint.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ disabled_rules:
33
- force_try
44
- identifier_name
55
- type_body_length
6+
- file_length
67
opt_in_rules:
78
- trailing_comma
89
trailing_comma:

Tests/CrossroadTests/PatternURLTests.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,4 +45,15 @@ final class PatternURLTests: XCTestCase {
4545
assertShouldFailed("without_schema")
4646
assertShouldFailed("invalid_schema://////aaaaaaa")
4747
}
48+
49+
func testHasPrefix() {
50+
XCTAssertTrue(PatternURL(string: "https://example.com")!.hasPrefix(url: URL(string: "https://example.com")!))
51+
XCTAssertTrue(PatternURL(string: "https://example.com/")!.hasPrefix(url: URL(string: "https://example.com/")!))
52+
XCTAssertTrue(PatternURL(string: "https://example.com/")!.hasPrefix(url: URL(string: "https://example.com")!))
53+
XCTAssertTrue(PatternURL(string: "https://example.com/users/:id")!.hasPrefix(url: URL(string: "https://example.com")!))
54+
XCTAssertTrue(PatternURL(string: "https://example.com/users/:id")!.hasPrefix(url: URL(string: "https://example.com/")!))
55+
XCTAssertTrue(PatternURL(string: "https://example.com/users/:id")!.hasPrefix(url: URL(string: "https://example.com/users")!))
56+
XCTAssertTrue(PatternURL(string: "https://example.com/users/:id")!.hasPrefix(url: URL(string: "https://example.com/users/")!))
57+
XCTAssertFalse(PatternURL(string: "https://example.com/users/:id")!.hasPrefix(url: URL(string: "https://example.com/users/10")!))
58+
}
4859
}

0 commit comments

Comments
 (0)