Skip to content

Commit 771ac9c

Browse files
authored
Merge pull request #6 from jasl/octwu/refactor_api
refactor router api
2 parents 199ed62 + eebfc76 commit 771ac9c

14 files changed

+159
-136
lines changed

Demo.playground/Contents.swift

Lines changed: 34 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import Foundation
44
import RouterX
5-
65
//: Here I define some pattern
76

87
let pattern1 = "/articles(/page/:page(/per_page/:per_page))(/sort/:sort)(.:format)"
@@ -13,34 +12,37 @@ let pattern4 = "/:article_id"
1312
//: Initialize the router, I can give a default closure to handle while given a URI path but match no one.
1413

1514
// This is the handler that would be performed after no pattern match
16-
let defaultUnmatchHandler = { (url: URL, context: AnyObject?) -> Void in
17-
// Do something here, e.g: give some tips or show a default UI
18-
print("\(url) is unmatched.")
19-
20-
// context can be provided on matching patterns
21-
if let context = context as? String {
22-
print("Context is \"\(context)\"")
23-
}
15+
let defaultUnmatchHandler: Router<Any>.UnmatchHandler = { url, context in
16+
// Do something here, e.g: give some tips or show a default UI
17+
print("Default unmatch handler")
18+
print("\(url) is unmatched")
19+
20+
// context can be provided on matching patterns
21+
if let context = context as? String {
22+
print("Context is \"\(context)\"")
23+
}
24+
print("\n")
2425
}
2526

2627
// Initialize a router instance, consider it's global and singleton
27-
let router = Router(defaultUnmatchHandler: defaultUnmatchHandler)
28+
// Router.T is used for specifying context type
29+
let router = Router<Any>(defaultUnmatchHandler: defaultUnmatchHandler)
2830

2931
//: Register patterns, the closure is the handle when matched the pattern.
3032

3133
// Set a route pattern, the closure is a handler that would be performed after match the pattern
32-
router.register(pattern: pattern1) { (url, parameters, context) in
33-
// Do something here, e.g: show a UI
34-
var string = "URL is \(url), parameter is \(parameters)"
35-
if let context = context as? String {
36-
string += " Context is \"\(context)\""
37-
}
38-
print(string)
34+
router.register(pattern: pattern1) { result in
35+
// Now, registered pattern has been matched
36+
// Do anything you want, e.g: show a UI
37+
print(result)
38+
print("\n")
39+
3940
}
4041

41-
router.register(pattern: pattern2) { _, _, _ in
42-
// Do something here, e.g: show a UI
43-
print("call new article")
42+
router.register(pattern: pattern2) { _ in
43+
// Now, registered pattern has been matched
44+
// Do anything you want, e.g: show a UI
45+
print("call new article")
4446
}
4547

4648
//: Let match some URI Path.
@@ -49,24 +51,25 @@ router.register(pattern: pattern2) { _, _, _ in
4951
let path1 = "/articles/page/2/sort/recent.json?foo=bar&baz"
5052

5153
// It's will be matched, and perform the handler that we have set up.
52-
router.match(urlPath: path1)
54+
router.match(path1)
5355
// It can pass the context for handler
54-
router.match(urlPath: path1, context: "fooo" as AnyObject?)
56+
router.match(path1, context: "fooo")
5557

5658
// A case that shouldn't be matched
5759
let path2 = "/articles/2/edit"
5860

59-
let customUnmatchHandler: UnmatchRouteHandler = { (url, context) in
60-
var string = "\(url) is no match..."
61-
// context can be provided on matching patterns
62-
if let context = context as? String {
63-
string += "Context is \"\(context)\""
64-
}
61+
let customUnmatchHandler: Router<Any>.UnmatchHandler = { (url, context) in
62+
print("This is custom unmatch handler")
63+
var string = "\(url) is no match..."
64+
// context can be provided on matching patterns
65+
if let context = context as? String {
66+
string += "\nContext is \"\(context)\""
67+
}
6568

66-
print(string)
69+
print(string)
6770
}
6871
// It's will not be matched, and perform the default unmatch handler that we have set up
69-
router.match(urlPath: path2)
72+
router.match(path2)
7073

7174
// It can provide a custome unmatch handler to override the default, also can pass the context
72-
router.match(urlPath: path2, context: "bar" as AnyObject?, unmatchHandler: customUnmatchHandler)
75+
router.match(path2, context: "bar", unmatchHandler: customUnmatchHandler)

RouterX.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
/* End PBXAggregateTarget section */
2222

2323
/* Begin PBXBuildFile section */
24+
48E62CA721A695000005838B /* MatchResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 48E62CA621A695000005838B /* MatchResult.swift */; };
2425
OBJ_35 /* Router.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_9 /* Router.swift */; };
2526
OBJ_36 /* RouterXCore.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_10 /* RouterXCore.swift */; };
2627
OBJ_37 /* RoutingGraph.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_11 /* RoutingGraph.swift */; };
@@ -57,6 +58,7 @@
5758
/* End PBXContainerItemProxy section */
5859

5960
/* Begin PBXFileReference section */
61+
48E62CA621A695000005838B /* MatchResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MatchResult.swift; sourceTree = "<group>"; };
6062
OBJ_10 /* RouterXCore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RouterXCore.swift; sourceTree = "<group>"; };
6163
OBJ_11 /* RoutingGraph.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoutingGraph.swift; sourceTree = "<group>"; };
6264
OBJ_12 /* RoutingPatternParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoutingPatternParser.swift; sourceTree = "<group>"; };
@@ -156,6 +158,7 @@
156158
isa = PBXGroup;
157159
children = (
158160
OBJ_9 /* Router.swift */,
161+
48E62CA621A695000005838B /* MatchResult.swift */,
159162
OBJ_10 /* RouterXCore.swift */,
160163
OBJ_11 /* RoutingGraph.swift */,
161164
OBJ_12 /* RoutingPatternParser.swift */,
@@ -252,6 +255,7 @@
252255
buildActionMask = 0;
253256
files = (
254257
OBJ_35 /* Router.swift in Sources */,
258+
48E62CA721A695000005838B /* MatchResult.swift in Sources */,
255259
OBJ_36 /* RouterXCore.swift in Sources */,
256260
OBJ_37 /* RoutingGraph.swift in Sources */,
257261
OBJ_38 /* RoutingPatternParser.swift in Sources */,

Sources/RouterX/MatchResult.swift

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import Foundation
2+
3+
public struct MatchResult<Context>: CustomDebugStringConvertible, CustomStringConvertible {
4+
public let url: URL
5+
public let parameters: [String: String]
6+
public let context: Context?
7+
8+
public var description: String {
9+
return """
10+
MatchResult<\(Context.self)> {
11+
url: \(url)
12+
parameters: \(parameters)
13+
context: \(String(describing: context))
14+
}
15+
"""
16+
}
17+
18+
public var debugDescription: String {
19+
return description
20+
}
21+
}

Sources/RouterX/Router.swift

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,46 @@
11
import Foundation
22

3-
public typealias MatchRouteHandler = ((URL, _ parameters: [String: String], _ context: AnyObject?) -> Void)
4-
public typealias UnmatchRouteHandler = ((URL, _ context: AnyObject?) -> Void)
3+
open class Router<Context> {
4+
5+
public typealias MatchedHandler = (MatchResult<Context>) -> Void
6+
public typealias UnmatchHandler = ((URL, _ context: Context?) -> Void)
57

6-
open class Router {
78
private let core: RouterXCore = RouterXCore()
8-
private let defaultUnmatchHandler: UnmatchRouteHandler?
9+
private let defaultUnmatchHandler: UnmatchHandler?
910

10-
private var handlerMappings: [PatternIdentifier: MatchRouteHandler] = [:]
11+
private var handlerMappings: [PatternIdentifier: MatchedHandler] = [:]
1112

12-
public init(defaultUnmatchHandler: UnmatchRouteHandler? = nil) {
13+
public init(defaultUnmatchHandler: UnmatchHandler? = nil) {
1314
self.defaultUnmatchHandler = defaultUnmatchHandler
1415
}
1516

16-
open func register(pattern: String, handler: @escaping MatchRouteHandler) -> Bool {
17-
let patternIdentifier = pattern.hashValue
18-
if self.core.registerRoutingPattern(pattern, patternIdentifier: patternIdentifier) {
19-
self.handlerMappings[patternIdentifier] = handler
20-
17+
open func register(pattern: String, handler: @escaping MatchedHandler) -> Bool {
18+
if self.core.registerRoutingPattern(pattern) {
19+
self.handlerMappings[pattern] = handler
2120
return true
2221
} else {
2322
return false
2423
}
2524
}
2625

27-
open func match(url: URL, context: AnyObject? = nil, unmatchHandler: UnmatchRouteHandler? = nil) -> Bool {
26+
@discardableResult
27+
open func match(_ url: URL, context: Context? = nil, unmatchHandler: UnmatchHandler? = nil) -> Bool {
2828
guard let matchedRoute = core.matchURL(url),
29-
let matchHandler = handlerMappings[matchedRoute.patternIdentifier] else {
30-
let expectUnmatchHandler = unmatchHandler ?? defaultUnmatchHandler
31-
expectUnmatchHandler?(url, context)
32-
return false
29+
let matchHandler = handlerMappings[matchedRoute.patternIdentifier] else {
30+
let expectUnmatchHandler = unmatchHandler ?? defaultUnmatchHandler
31+
expectUnmatchHandler?(url, context)
32+
return false
3333
}
3434

35-
matchHandler(url, matchedRoute.parametars, context)
35+
let result = MatchResult<Context>(url: url, parameters: matchedRoute.parametars, context: context)
36+
matchHandler(result)
3637
return true
3738
}
3839

39-
open func match(urlPath: String, context: AnyObject? = nil, unmatchHandler: UnmatchRouteHandler? = nil) -> Bool {
40-
guard let url = URL(string: urlPath) else { return false }
40+
@discardableResult
41+
open func match(_ path: String, context: Context? = nil, unmatchHandler: UnmatchHandler? = nil) -> Bool {
42+
guard let url = URL(string: path) else { return false }
4143

42-
return match(url: url, context: context, unmatchHandler: unmatchHandler)
44+
return match(url, context: context, unmatchHandler: unmatchHandler)
4345
}
4446
}

Sources/RouterX/RouterXCore.swift

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,25 +12,25 @@ public struct MatchedRoute {
1212
}
1313
}
1414

15-
open class RouterXCore {
15+
public class RouterXCore {
1616
private let rootRoute: RouteVertex
1717

1818
public init() {
1919
self.rootRoute = RouteVertex()
2020
}
2121

22-
open func registerRoutingPattern(_ pattern: String, patternIdentifier: PatternIdentifier) -> Bool {
22+
public func registerRoutingPattern(_ pattern: String) -> Bool {
2323
let tokens = RoutingPatternScanner.tokenize(pattern)
2424

2525
do {
26-
try RoutingPatternParser.parseAndAppendTo(self.rootRoute, routingPatternTokens: tokens, patternIdentifier: patternIdentifier)
26+
try RoutingPatternParser.parseAndAppendTo(self.rootRoute, routingPatternTokens: tokens, patternIdentifier: pattern)
2727
return true
2828
} catch {
2929
return false
3030
}
3131
}
3232

33-
open func matchURL(_ url: URL) -> MatchedRoute? {
33+
public func matchURL(_ url: URL) -> MatchedRoute? {
3434
let path = url.path
3535

3636
let tokens = URLPathScanner.tokenize(path)
@@ -52,13 +52,13 @@ open class RouterXCore {
5252
return nil
5353
}
5454
}
55-
55+
5656
guard let pathPatternIdentifier = targetRoute.patternIdentifier else { return nil }
5757

5858
return MatchedRoute(url: url, parameters: parameters, patternIdentifier: pathPatternIdentifier)
5959
}
6060

61-
open func matchURLPath(_ urlPath: String) -> MatchedRoute? {
61+
public func matchURLPath(_ urlPath: String) -> MatchedRoute? {
6262
guard let url = URL(string: urlPath) else { return nil }
6363
return matchURL(url)
6464
}

Sources/RouterX/RoutingGraph.swift

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
import Foundation
22

3-
public typealias PatternIdentifier = Int
3+
public typealias PatternIdentifier = String
44

5-
public enum RouteEdge {
5+
internal enum RouteEdge {
66
case dot
77
case slash
88
case literal(String)
99
}
1010

1111
extension RouteEdge: Hashable, CustomDebugStringConvertible, CustomStringConvertible {
12-
public var description: String {
12+
var description: String {
1313
switch self {
1414
case .literal(let value):
1515
return value
@@ -20,29 +20,29 @@ extension RouteEdge: Hashable, CustomDebugStringConvertible, CustomStringConvert
2020
}
2121
}
2222

23-
public var debugDescription: String {
23+
var debugDescription: String {
2424
return self.description
2525
}
2626

27-
public func hash(into hasher: inout Hasher) {
27+
func hash(into hasher: inout Hasher) {
2828
hasher.combine(description)
2929
}
3030
}
3131

32-
open class RouteVertex {
33-
open var nextRoutes: [RouteEdge: RouteVertex] = [:]
34-
open var epsilonRoute: (String, RouteVertex)?
35-
open var patternIdentifier: PatternIdentifier?
32+
internal class RouteVertex {
33+
var nextRoutes: [RouteEdge: RouteVertex] = [:]
34+
var epsilonRoute: (String, RouteVertex)?
35+
var patternIdentifier: PatternIdentifier?
3636

37-
public init(patternIdentifier: PatternIdentifier? = nil) {
37+
init(patternIdentifier: PatternIdentifier? = nil) {
3838
self.patternIdentifier = patternIdentifier
3939
}
4040

41-
open var isTerminal: Bool {
41+
var isTerminal: Bool {
4242
return self.patternIdentifier != nil
4343
}
4444

45-
open var isFinale: Bool {
45+
var isFinale: Bool {
4646
return self.nextRoutes.isEmpty && self.epsilonRoute == nil
4747
}
4848
}

0 commit comments

Comments
 (0)