diff --git a/Crossroad.xcodeproj/project.pbxproj b/Crossroad.xcodeproj/project.pbxproj index 8c96829..2f8677c 100644 --- a/Crossroad.xcodeproj/project.pbxproj +++ b/Crossroad.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 079956D722F0304200FC48CF /* URLParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 079956D622F0304200FC48CF /* URLParser.swift */; }; 07CBB57520B7074B00C272A5 /* ContextTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07CBB57320B705A600C272A5 /* ContextTests.swift */; }; 546DEA8420AD77F800923325 /* Crossroad.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 546DEA7A20AD77F800923325 /* Crossroad.framework */; }; 546DEAB820B0991900923325 /* PatternURL.swift in Sources */ = {isa = PBXBuildFile; fileRef = 546DEAB020B0991800923325 /* PatternURL.swift */; }; @@ -33,6 +34,7 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 079956D622F0304200FC48CF /* URLParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLParser.swift; sourceTree = ""; }; 07CBB57320B705A600C272A5 /* ContextTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContextTests.swift; sourceTree = ""; }; 546DEA7A20AD77F800923325 /* Crossroad.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Crossroad.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 546DEA8320AD77F800923325 /* CrossroadTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CrossroadTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -99,6 +101,7 @@ 546DEAB020B0991800923325 /* PatternURL.swift */, 546DEAB620B0991900923325 /* Route.swift */, 546DEAB520B0991900923325 /* Router.swift */, + 079956D622F0304200FC48CF /* URLParser.swift */, ); name = Crossroad; path = Sources/Crossroad; @@ -229,6 +232,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 079956D722F0304200FC48CF /* URLParser.swift in Sources */, 54AB581820B0AED300E2FD28 /* OpenURLOption.swift in Sources */, 546DEAB820B0991900923325 /* PatternURL.swift in Sources */, 546DEABB20B0991900923325 /* Context.swift in Sources */, diff --git a/README.md b/README.md index 49204f0..f7448d5 100644 --- a/README.md +++ b/README.md @@ -167,6 +167,17 @@ Of course, you can also use [Firebase Dynamic Link](https://firebase.google.com/ let router = DefaultRouter(url: URL(string: "https://my-awesome-pokedex.com")!) ``` +## Parse URL patterns + +If you maintain a complex application and you want to use independent URL pattern parsers without Router. +You can use `URLParser`. + +```swift +let parser = URLParser() +let context = parser.parse(URL(string: "pokedex:/pokemons/25")!, + in: URLPattern("pokedex://pokemons/:id"))) +``` + ## Supported version Latest version of Crossroad requires Swift 5.0 or above. diff --git a/Sources/Crossroad/Route.swift b/Sources/Crossroad/Route.swift index 1509873..413a9a3 100644 --- a/Sources/Crossroad/Route.swift +++ b/Sources/Crossroad/Route.swift @@ -5,6 +5,7 @@ public struct Route { internal let patternURL: PatternURL private let handler: Handler + private let parser: URLParser = .init() internal init(pattern patternURL: PatternURL, handler: @escaping Handler) { self.patternURL = patternURL @@ -12,48 +13,13 @@ public struct Route { } internal func responds(to url: URL, userInfo: UserInfo) -> Bool { - return parse(url, with: userInfo) != nil + return parser.parse(url, in: patternURL, userInfo: userInfo) != nil } internal func openIfPossible(_ url: URL, userInfo: UserInfo) -> Bool { - guard let context = parse(url, with: userInfo) else { + guard let context = parser.parse(url, in: patternURL, userInfo: userInfo) else { return false } return handler(context) } - - internal func parse(_ url: URL, with userInfo: UserInfo) -> Context? { - guard let scheme = url.scheme, let host = url.host else { - return nil - } - if scheme != patternURL.scheme || patternURL.pathComponents.count != url.pathComponents.count { - return nil - } - - var arguments: Arguments = [:] - if patternURL.host.hasPrefix(PatternURL.keywordPrefix) { - let keyword = String(patternURL.host[PatternURL.keywordPrefix.endIndex...]) - arguments[keyword] = host - } else if host != patternURL.host { - return nil - } - - for (patternComponent, component) in zip(patternURL.pathComponents, url.pathComponents) { - if patternComponent.hasPrefix(PatternURL.keywordPrefix) { - let keyword = String(patternComponent[PatternURL.keywordPrefix.endIndex...]) - arguments[keyword] = component - } else if patternComponent == component { - continue - } else { - return nil - } - } - let parameters: Parameters - if let components = URLComponents(url: url, resolvingAgainstBaseURL: true) { - parameters = components.queryItems ?? [] - } else { - parameters = [] - } - return Context(url: url, arguments: arguments, parameters: parameters, userInfo: userInfo) - } } diff --git a/Sources/Crossroad/URLParser.swift b/Sources/Crossroad/URLParser.swift new file mode 100644 index 0000000..f012180 --- /dev/null +++ b/Sources/Crossroad/URLParser.swift @@ -0,0 +1,44 @@ +import Foundation + +public struct URLParser { + func parse(_ url: URL, in patternURL: PatternURL, userInfo: UserInfo) -> Context? { + guard let scheme = url.scheme, let host = url.host else { + return nil + } + if scheme != patternURL.scheme || patternURL.pathComponents.count != url.pathComponents.count { + return nil + } + + var arguments: Arguments = [:] + if patternURL.host.hasPrefix(PatternURL.keywordPrefix) { + let keyword = String(patternURL.host[PatternURL.keywordPrefix.endIndex...]) + arguments[keyword] = host + } else if host != patternURL.host { + return nil + } + + for (patternComponent, component) in zip(patternURL.pathComponents, url.pathComponents) { + if patternComponent.hasPrefix(PatternURL.keywordPrefix) { + let keyword = String(patternComponent[PatternURL.keywordPrefix.endIndex...]) + arguments[keyword] = component + } else if patternComponent == component { + continue + } else { + return nil + } + } + let parameters: Parameters + if let components = URLComponents(url: url, resolvingAgainstBaseURL: true) { + parameters = components.queryItems ?? [] + } else { + parameters = [] + } + return Context(url: url, arguments: arguments, parameters: parameters, userInfo: userInfo) + } +} + +extension URLParser where UserInfo == Void { + func parse(_ url: URL, in patternURL: PatternURL) -> Context? { + return parse(url, in: patternURL, userInfo: ()) + } +}