-
Notifications
You must be signed in to change notification settings - Fork 47
Expand file tree
/
Copy pathCompass.swift
More file actions
103 lines (78 loc) · 2.82 KB
/
Copy pathCompass.swift
File metadata and controls
103 lines (78 loc) · 2.82 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
import Foundation
public struct Compass {
typealias Result = (
route: String,
arguments: [String: String],
concreteMatchCount: Int,
wildcardMatchCount: Int)
fileprivate static var internalScheme = ""
public static var delimiter: String = ":"
public static var scheme: String {
set { Compass.internalScheme = newValue }
get { return "\(Compass.internalScheme)://" }
}
public static var routes = [String]()
public static func parse(url: URL, payload: Any? = nil) -> Location? {
let path = url.absoluteString.substring(from: scheme.endIndex)
guard !(path.contains("?") || path.contains("#")) else {
return parseComponents(url: url, payload: payload)
}
let results: [Result] = routes.flatMap {
return findMatch(routeString: $0, pathString: path)
}.sorted { (r1: Result, r2: Result) in
if r1.concreteMatchCount == r2.concreteMatchCount {
return r1.wildcardMatchCount > r2.wildcardMatchCount
}
return r1.concreteMatchCount > r2.concreteMatchCount
}
if let result = results.first {
return Location(path: result.route, arguments: result.arguments, payload: payload)
}
return nil
}
static func parseComponents(url: URL, payload: Any? = nil) -> Location? {
guard let host = url.host else { return nil }
let route = "\(host)\(url.path)"
let urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false)
var arguments = [String : String]()
urlComponents?.queryItems?.forEach { queryItem in
arguments[queryItem.name] = queryItem.value
}
if let fragment = urlComponents?.fragment {
arguments = fragment.queryParameters()
}
return Location(path: route, arguments: arguments, payload: payload)
}
static func findMatch(routeString: String, pathString: String) -> Result? {
let routes = routeString.split(delimiter)
let paths = pathString.split(delimiter)
guard routes.count == paths.count else { return nil }
var arguments: [String: String] = [:]
var concreteMatchCount = 0
var wildcardMatchCount = 0
for (route, path) in zip(routes, paths) {
if route.hasPrefix("{") {
let key = route.replacingOccurrences(of: "{", with: "")
.replacingOccurrences(of: "}", with: "")
arguments[key] = path
wildcardMatchCount += 1
continue
}
if route == path {
concreteMatchCount += 1
} else {
return nil
}
}
return (route: routeString,
arguments: arguments,
concreteMatchCount: concreteMatchCount,
wildcardMatchCount: wildcardMatchCount)
}
}
extension Compass {
public static func navigate(to urn: String, scheme: String = Compass.scheme) {
guard let url = URL(string: "\(scheme)\(urn)") else { return }
open(url: url)
}
}