Skip to content

Commit

Permalink
Merge pull request #14 from giginet/support-prefix-url
Browse files Browse the repository at this point in the history
Support Universal Links
  • Loading branch information
giginet committed Jun 26, 2019
2 parents bbe4a1c + 2c5a8f3 commit 2a8a55b
Show file tree
Hide file tree
Showing 7 changed files with 349 additions and 17 deletions.
2 changes: 1 addition & 1 deletion Crossroad.podspec
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
6 changes: 6 additions & 0 deletions Sources/Crossroad/PatternURL.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 = "/"
Expand All @@ -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.
Expand All @@ -37,4 +39,8 @@ internal struct PatternURL {
pathComponents = []
}
}

func hasPrefix(url: URL) -> Bool {
return patternString.hasPrefix(url.absoluteString)
}
}
57 changes: 46 additions & 11 deletions Sources/Crossroad/Router.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,31 +3,57 @@ import Foundation
public typealias SimpleRouter = Router<Void>

public final class Router<UserInfo> {
private let scheme: String
private enum Prefix {
case scheme(String)
case url(URL)
}
private let prefix: Prefix
private var routes: [Route<UserInfo>] = []

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 {
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<UserInfo>) {
if scheme != route.patternURL.scheme {
assertionFailure("Router and pattern must have the same schemes. expect: \(scheme), actual: \(route.patternURL.scheme)")
} else {
if isValidURLPattern(route.patternURL) {
routes.append(route)
} else {
assertionFailure("Unexpected URL Pattern")
}
}

@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
Expand All @@ -36,10 +62,19 @@ public final class Router<UserInfo> {
public func register(_ routes: [(String, Route<UserInfo>.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")
Expand Down
1 change: 1 addition & 0 deletions Tests/.swiftlint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ disabled_rules:
- force_try
- identifier_name
- type_body_length
- file_length
opt_in_rules:
- trailing_comma
trailing_comma:
Expand Down
11 changes: 11 additions & 0 deletions Tests/CrossroadTests/PatternURLTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,15 @@ 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/")!.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")!))
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")!))
}
}
Loading

0 comments on commit 2a8a55b

Please sign in to comment.