Skip to content

Commit ce67350

Browse files
authored
Merge pull request #41 from giginet/introduce-context-protocol
Remove UserInfo from unnecessary methods
2 parents af2138a + 9374bba commit ce67350

File tree

8 files changed

+55
-51
lines changed

8 files changed

+55
-51
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ If you maintain a complex application and you want to use independent URL patter
230230
You can use `ContextParser`.
231231

232232
```swift
233-
let parser = ContextParser<Void>()
233+
let parser = ContextParser()
234234
let context = parser.parse(URL(string: "pokedex:/pokemons/25")!,
235235
with: "pokedex://pokemons/:id")
236236
```

Sources/Crossroad/Context.swift

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

3-
struct Arguments {
3+
public struct Arguments {
44
enum Error: Swift.Error {
55
case keyNotFound(String)
66
case couldNotParse(Parsable.Type)
@@ -72,22 +72,49 @@ public struct QueryParameters {
7272
private var storage: [URLQueryItem]
7373
}
7474

75-
public struct Context<UserInfo> {
75+
public protocol ContextProtocol {
76+
var url: URL { get }
77+
/// This struct is for internal usage.
78+
var internalArgumentsContainer: Arguments { get }
79+
var queryParameters: QueryParameters { get }
80+
}
81+
82+
extension ContextProtocol {
83+
func attach<UserInfo>(_ userInfo: UserInfo) -> Context<UserInfo> {
84+
Context<UserInfo>(url: url, internalArgumentsContainer: internalArgumentsContainer, queryParameters: queryParameters, userInfo: userInfo)
85+
}
86+
}
87+
88+
struct AbstractContext: ContextProtocol {
89+
let url: URL
90+
let internalArgumentsContainer: Arguments
91+
let queryParameters: QueryParameters
92+
93+
init(url: URL, arguments: Arguments, queryParameters: QueryParameters) {
94+
self.url = url
95+
self.internalArgumentsContainer = arguments
96+
self.queryParameters = queryParameters
97+
}
98+
}
99+
100+
public struct Context<UserInfo>: ContextProtocol {
76101
public let url: URL
77-
private let arguments: Arguments
102+
public let internalArgumentsContainer: Arguments
78103
public let queryParameters: QueryParameters
79104
public let userInfo: UserInfo
80105

81-
internal init(url: URL, arguments: Arguments, queryParameters: QueryParameters, userInfo: UserInfo) {
106+
internal init(url: URL, internalArgumentsContainer: Arguments, queryParameters: QueryParameters, userInfo: UserInfo) {
82107
self.url = url
83-
self.arguments = arguments
108+
self.internalArgumentsContainer = internalArgumentsContainer
84109
self.queryParameters = queryParameters
85110
self.userInfo = userInfo
86111
}
112+
}
87113

114+
extension ContextProtocol {
88115
@available(*, deprecated, message: "subscript for an argument is depricated.", renamed: "argument(named:)")
89116
public subscript<T: Parsable>(argument keyword: String) -> T? {
90-
return try? arguments.get(named: keyword)
117+
return try? internalArgumentsContainer.get(named: keyword)
91118
}
92119

93120
@available(*, deprecated, message: "Use queryParameters[key] instead")
@@ -96,7 +123,7 @@ public struct Context<UserInfo> {
96123
}
97124

98125
public func argument<T: Parsable>(named key: String, as type: T.Type = T.self) throws -> T {
99-
return try arguments.get(named: key)
126+
return try internalArgumentsContainer.get(named: key)
100127
}
101128

102129
@available(*, deprecated, renamed: "queryParameter(named:)")
@@ -132,6 +159,6 @@ public struct Context<UserInfo> {
132159

133160
extension Context where UserInfo == Void {
134161
init(url: URL, arguments: Arguments, queryParameters: QueryParameters) {
135-
self.init(url: url, arguments: arguments, queryParameters: queryParameters, userInfo: ())
162+
self.init(url: url, internalArgumentsContainer: arguments, queryParameters: queryParameters, userInfo: ())
136163
}
137164
}

Sources/Crossroad/ContextParser.swift

Lines changed: 7 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import Foundation
33
@available(*, deprecated, renamed: "ContextParser")
44
public typealias URLParser = ContextParser
55

6-
public class ContextParser<UserInfo> {
6+
public class ContextParser {
77
private let keywordPrefix = ":"
88

99
public enum Error: Swift.Error {
@@ -16,17 +16,17 @@ public class ContextParser<UserInfo> {
1616

1717
public init() { }
1818

19-
public func parse(_ url: URL, with patternString: String, userInfo: UserInfo) throws -> Context<UserInfo> {
19+
public func parse(_ url: URL, with patternString: String) throws -> ContextProtocol {
2020
let pattern = try Pattern(patternString: patternString)
21-
return try parse(url, with: pattern, userInfo: userInfo)
21+
return try parse(url, with: pattern)
2222
}
2323

2424
@available(*, deprecated, renamed: "parse(_:with:userInfo:)")
25-
public func parse(_ url: URL, in patternString: String, userInfo: UserInfo) throws -> Context<UserInfo> {
26-
try parse(url, with: patternString, userInfo: userInfo)
25+
public func parse(_ url: URL, in patternString: String) throws -> ContextProtocol {
26+
try parse(url, with: patternString)
2727
}
2828

29-
func parse(_ url: URL, with pattern: Pattern, userInfo: UserInfo) throws -> Context<UserInfo> {
29+
func parse(_ url: URL, with pattern: Pattern) throws -> ContextProtocol {
3030
let expectedComponents: [String]
3131
let actualURLComponents: [String]
3232
let shouldBeCaseSensitives: [Bool]
@@ -69,7 +69,7 @@ public class ContextParser<UserInfo> {
6969

7070
let argumentContainer = Arguments(arguments)
7171
let parameters = parseParameters(from: url)
72-
return Context<UserInfo>(url: url, arguments: argumentContainer, queryParameters: parameters, userInfo: userInfo)
72+
return AbstractContext(url: url, arguments: argumentContainer, queryParameters: parameters)
7373
}
7474

7575
private func compare(_ lhs: String, _ rhs: String, isCaseSensitive: Bool) -> Bool {
@@ -90,14 +90,3 @@ public class ContextParser<UserInfo> {
9090
return QueryParameters(parameters)
9191
}
9292
}
93-
94-
extension ContextParser where UserInfo == Void {
95-
public func parse(_ url: URL, with patternString: String) throws -> Context<UserInfo> {
96-
return try parse(url, with: patternString, userInfo: ())
97-
}
98-
99-
@available(*, deprecated, renamed: "parse(_:with:)")
100-
public func parse(_ url: URL, in patternString: String) throws -> Context<UserInfo> {
101-
try parse(url, with: patternString, userInfo: ())
102-
}
103-
}

Sources/Crossroad/OpenURLOption.swift

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,6 @@ public extension Router where UserInfo == OpenURLOption {
3232
func openIfPossible(_ url: URL, options: ApplicationOpenURLOptions) -> Bool {
3333
return openIfPossible(url, userInfo: OpenURLOption(options: options))
3434
}
35-
36-
func responds(to url: URL, options: ApplicationOpenURLOptions) -> Bool {
37-
return responds(to: url, userInfo: OpenURLOption(options: options))
38-
}
3935
}
4036

4137
@available(iOS 13.0, *)
@@ -44,10 +40,6 @@ extension Router where UserInfo == OpenURLOption {
4440
public func openIfPossible(_ url: URL, options: UIScene.OpenURLOptions) -> Bool {
4541
return openIfPossible(url, userInfo: OpenURLOption(options: options))
4642
}
47-
48-
public func responds(to url: URL, options: UIScene.OpenURLOptions) -> Bool {
49-
return responds(to: url, userInfo: OpenURLOption(options: options))
50-
}
5143
}
5244

5345
extension Context where UserInfo == OpenURLOption {

Sources/Crossroad/Router.swift

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ public final class Router<UserInfo> {
77

88
let linkSources: Set<LinkSource>
99
private(set) var routes: [Route]
10-
private let parser = ContextParser<UserInfo>()
10+
private let parser = ContextParser()
1111

1212
public convenience init(accepting linkSource: LinkSource) {
1313
self.init(linkSources: [linkSource])
@@ -52,18 +52,18 @@ public final class Router<UserInfo> {
5252

5353
@discardableResult
5454
public func openIfPossible(_ url: URL, userInfo: UserInfo) -> Bool {
55-
searchMatchingRoutes(to: url, userInfo: userInfo)
55+
searchMatchingRoutes(to: url)
5656
.first { result in
5757
do {
58-
return try result.route.executeHandler(context: result.context)
58+
return try result.route.executeHandler(context: result.context.attach(userInfo))
5959
} catch {
6060
return false
6161
}
6262
} != nil
6363
}
6464

65-
public func responds(to url: URL, userInfo: UserInfo) -> Bool {
66-
!searchMatchingRoutes(to: url, userInfo: userInfo).isEmpty
65+
public func responds(to url: URL) -> Bool {
66+
!searchMatchingRoutes(to: url).isEmpty
6767
}
6868

6969
private func expandAcceptablePattern(of route: Route) -> Set<Pattern> {
@@ -79,12 +79,12 @@ public final class Router<UserInfo> {
7979

8080
private struct MatchResult {
8181
let route: Route
82-
let context: Context<UserInfo>
82+
let context: ContextProtocol
8383
}
84-
private func searchMatchingRoutes(to url: URL, userInfo: UserInfo) -> [MatchResult] {
84+
private func searchMatchingRoutes(to url: URL) -> [MatchResult] {
8585
routes.reduce(into: []) { matchings, route in
8686
for pattern in expandAcceptablePattern(of: route) {
87-
if let context = try? parser.parse(url, with: pattern, userInfo: userInfo) {
87+
if let context = try? parser.parse(url, with: pattern) {
8888
let result = MatchResult(route: route, context: context)
8989
matchings.append(result)
9090
}
@@ -98,10 +98,6 @@ public extension Router where UserInfo == Void {
9898
func openIfPossible(_ url: URL) -> Bool {
9999
return openIfPossible(url, userInfo: ())
100100
}
101-
102-
func responds(to url: URL) -> Bool {
103-
return responds(to: url, userInfo: ())
104-
}
105101
}
106102

107103
public extension Router {

Tests/CrossroadTests/ContextTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ final class ContextTests: XCTestCase {
1313

1414
var context: Context<Void> {
1515
return Context<Void>(url: url,
16-
arguments: Arguments(["pokedexID": "25", "name": "Pikachu"]),
16+
internalArgumentsContainer: Arguments(["pokedexID": "25", "name": "Pikachu"]),
1717
queryParameters: QueryParameters([
1818
URLQueryItem(name: "name", value: "Pikachu"),
1919
URLQueryItem(name: "type", value: "electric"),

Tests/CrossroadTests/OpenURLOptionTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ final class OpenURLOptionTests: XCTestCase {
1919

2020
func testContextWithOpenURLOption() {
2121
let context = Context<OpenURLOption>(url: URL(string: "https://example.com")!,
22-
arguments: .init([:]),
22+
internalArgumentsContainer: .init([:]),
2323
queryParameters: .init([]),
2424
userInfo: options)
2525
XCTAssertEqual(context.options.sourceApplication, "org.giginet.myapp")

Tests/CrossroadTests/ParserTests.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@ import XCTest
33

44
final class ParserTests: XCTestCase {
55
func testURLParser() throws {
6-
let parser = ContextParser<Void>()
6+
let parser = ContextParser()
77
let patternString = "pokedex://pokemons/:pokedexID"
88
let context = try parser.parse(URL(string: "pokedex://pokemons/25")!, with: patternString)
99
XCTAssertEqual(try context.argument(named: "pokedexID"), 25)
1010
}
1111

1212
func testPatternCase() throws {
13-
let parser = ContextParser<Void>()
13+
let parser = ContextParser()
1414

1515
let testCases: [(String, String, Bool, UInt)] = [
1616
("http://my-awesome-pokedex.com/pokemons", "HTTP://MY-AWESOME-POKEDEX.COM/pokemons", true, #line),

0 commit comments

Comments
 (0)