Skip to content

Commit

Permalink
Merge pull request #20 from giginet/version3
Browse files Browse the repository at this point in the history
Version3 🎉
  • Loading branch information
giginet authored Aug 8, 2019
2 parents 2a8a55b + 1f5bebd commit b92a257
Show file tree
Hide file tree
Showing 16 changed files with 498 additions and 260 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.1.0"
s.version = "3.0.0"
s.summary = "Route URL schemes easily"
s.description = <<-DESC
Crossroad is an URL router focused on handling Custom URL Scheme.
Expand Down
24 changes: 16 additions & 8 deletions Crossroad.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,21 @@
objects = {

/* Begin PBXBuildFile section */
0751010D22F8270400DFA53F /* URLParserTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0751010B22F8270200DFA53F /* URLParserTests.swift */; };
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 */; };
546DEAB920B0991900923325 /* Extractable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 546DEAB120B0991900923325 /* Extractable.swift */; };
546DEABA20B0991900923325 /* Crossroad.h in Headers */ = {isa = PBXBuildFile; fileRef = 546DEAB220B0991900923325 /* Crossroad.h */; settings = {ATTRIBUTES = (Public, ); }; };
546DEABB20B0991900923325 /* Context.swift in Sources */ = {isa = PBXBuildFile; fileRef = 546DEAB320B0991900923325 /* Context.swift */; };
546DEABD20B0991900923325 /* Router.swift in Sources */ = {isa = PBXBuildFile; fileRef = 546DEAB520B0991900923325 /* Router.swift */; };
546DEABE20B0991900923325 /* Route.swift in Sources */ = {isa = PBXBuildFile; fileRef = 546DEAB620B0991900923325 /* Route.swift */; };
546DEAC020B0993100923325 /* PatternURLTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 546DEAB420B0991900923325 /* PatternURLTests.swift */; };
546DEAC120B0994800923325 /* RouterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 546DEAAD20B0990500923325 /* RouterTests.swift */; };
54AB581620B09AD800E2FD28 /* ExtractableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54AB581420B09AD400E2FD28 /* ExtractableTests.swift */; };
54AB581820B0AED300E2FD28 /* OpenURLOption.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54AB581720B0AED300E2FD28 /* OpenURLOption.swift */; };
54AB581A20B0B29700E2FD28 /* OpenURLOptionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54AB581920B0B29700E2FD28 /* OpenURLOptionTests.swift */; };
54D5D48722F8013F00317EBF /* Parsable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54D5D48622F8013E00317EBF /* Parsable.swift */; };
54D5D48922F8015500317EBF /* ParsableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54D5D48822F8015500317EBF /* ParsableTests.swift */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
Expand All @@ -33,22 +35,24 @@
/* End PBXContainerItemProxy section */

/* Begin PBXFileReference section */
0751010B22F8270200DFA53F /* URLParserTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = URLParserTests.swift; sourceTree = "<group>"; };
079956D622F0304200FC48CF /* URLParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLParser.swift; sourceTree = "<group>"; };
07CBB57320B705A600C272A5 /* ContextTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContextTests.swift; sourceTree = "<group>"; };
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; };
546DEAAC20B0990500923325 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
546DEAAD20B0990500923325 /* RouterTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RouterTests.swift; sourceTree = "<group>"; };
546DEAB020B0991800923325 /* PatternURL.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PatternURL.swift; sourceTree = "<group>"; };
546DEAB120B0991900923325 /* Extractable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Extractable.swift; sourceTree = "<group>"; };
546DEAB220B0991900923325 /* Crossroad.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Crossroad.h; sourceTree = "<group>"; };
546DEAB320B0991900923325 /* Context.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Context.swift; sourceTree = "<group>"; };
546DEAB420B0991900923325 /* PatternURLTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PatternURLTests.swift; sourceTree = "<group>"; };
546DEAB520B0991900923325 /* Router.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Router.swift; sourceTree = "<group>"; };
546DEAB620B0991900923325 /* Route.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Route.swift; sourceTree = "<group>"; };
546DEAB720B0991900923325 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
54AB581420B09AD400E2FD28 /* ExtractableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtractableTests.swift; sourceTree = "<group>"; };
54AB581720B0AED300E2FD28 /* OpenURLOption.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenURLOption.swift; sourceTree = "<group>"; };
54AB581920B0B29700E2FD28 /* OpenURLOptionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenURLOptionTests.swift; sourceTree = "<group>"; };
54D5D48622F8013E00317EBF /* Parsable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Parsable.swift; sourceTree = "<group>"; };
54D5D48822F8015500317EBF /* ParsableTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ParsableTests.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -93,12 +97,13 @@
children = (
546DEAB320B0991900923325 /* Context.swift */,
546DEAB220B0991900923325 /* Crossroad.h */,
546DEAB120B0991900923325 /* Extractable.swift */,
546DEAB720B0991900923325 /* Info.plist */,
54AB581720B0AED300E2FD28 /* OpenURLOption.swift */,
54D5D48622F8013E00317EBF /* Parsable.swift */,
546DEAB020B0991800923325 /* PatternURL.swift */,
546DEAB620B0991900923325 /* Route.swift */,
546DEAB520B0991900923325 /* Router.swift */,
079956D622F0304200FC48CF /* URLParser.swift */,
);
name = Crossroad;
path = Sources/Crossroad;
Expand All @@ -108,11 +113,12 @@
isa = PBXGroup;
children = (
07CBB57320B705A600C272A5 /* ContextTests.swift */,
54AB581420B09AD400E2FD28 /* ExtractableTests.swift */,
546DEAAC20B0990500923325 /* Info.plist */,
54AB581920B0B29700E2FD28 /* OpenURLOptionTests.swift */,
54D5D48822F8015500317EBF /* ParsableTests.swift */,
546DEAB420B0991900923325 /* PatternURLTests.swift */,
546DEAAD20B0990500923325 /* RouterTests.swift */,
0751010B22F8270200DFA53F /* URLParserTests.swift */,
);
name = CrossroadTests;
path = Tests/CrossroadTests;
Expand Down Expand Up @@ -229,10 +235,11 @@
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 */,
546DEAB920B0991900923325 /* Extractable.swift in Sources */,
54D5D48722F8013F00317EBF /* Parsable.swift in Sources */,
546DEABD20B0991900923325 /* Router.swift in Sources */,
546DEABE20B0991900923325 /* Route.swift in Sources */,
);
Expand All @@ -246,7 +253,8 @@
546DEAC120B0994800923325 /* RouterTests.swift in Sources */,
546DEAC020B0993100923325 /* PatternURLTests.swift in Sources */,
07CBB57520B7074B00C272A5 /* ContextTests.swift in Sources */,
54AB581620B09AD800E2FD28 /* ExtractableTests.swift in Sources */,
0751010D22F8270400DFA53F /* URLParserTests.swift in Sources */,
54D5D48922F8015500317EBF /* ParsableTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
42 changes: 27 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,13 @@ Imagine to implement Pokédex on iOS. You can access somewhere via URL scheme.
```swift
router = DefaultRouter(scheme: "pokedex")
router.register([
("pokedex://pokemons", { context in
let type: Type? = context.parameter(for: "type")
("/pokemons", { context in
let type: Type? = context[parameter: "type"]
presentPokedexListViewController(for: type)
return true
}),
("pokedex://pokemons/:pokedexID", { context in
guard let pokedexID: Int? = try? context.argument(for: "pokedexID") else {
("/pokemons/:pokedexID", { context in
guard let pokedexID: Int? = context[argument: "pokedexID"] else {
// pokedexID must be Int
return false
}
Expand Down Expand Up @@ -82,24 +82,24 @@ For example, if passed URL matches `pokedex://search/:keyword`, you can get `key

```swift
// matches: pokedex://search/Pikachu
let keyword: String = try! context.argument(for: "keyword") // Pikachu
let keyword: String = context[argument: "keyword"] // Pikachu
```

And more, you can get query parameters if exist.

```swift
// matches: pokedex://search/Pikachu?generation=1
let generation: Int? = context.parameter(for: "generation") // 1
let generation: Int? = context[parameter: "generation"] // 1
```

Currently supported type is `Int`, `Int64`, `Float`, `Double`, `Bool`, `String` and `URL`.

### Enum argument

You can use enum as arguments by implementing `Extractable`.
You can use enum as arguments by implementing `Parsable`.

```swift
enum Type: String, Extractable {
enum Type: String, Parsable {
case normal
case fire
case water
Expand All @@ -108,7 +108,7 @@ enum Type: String, Extractable {
}

// matches: pokedex://pokemons?type=fire
let type: Type? = context.parameter(for: "type") // .fire
let type: Type? = context[parameter: "type"] // .fire
```

### Comma-separated list
Expand All @@ -117,21 +117,21 @@ You can treat comma-separated query strings as `Array`.

```swift
// matches: pokedex://pokemons?types=water,grass
let types: [Type]? = context.parameter(for: "types") // [.water, .grass]
let types: [Type]? = context[parameter: "types"] // [.water, .grass]
```

### Custom argument

You can also define own arguments by implementing `Extractable`.
You can also define own arguments by implementing `Parsable`.
This is an example to parse custom struct.

```swift
struct User {
let name: String
}
extension User: Extractable {
static func extract(from string: String) -> User? {
return User(name: string)
extension User: Parsable {
init?(from string: String) {
self.name = string
}
}
```
Expand Down Expand Up @@ -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<Void>()
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.
Expand All @@ -175,8 +186,9 @@ Use 1.x instead on Swift 4.1 or lower.

|Crossroad Version|Swift Version|Xcode Version|
|-----------------|-------------|-------------|
|3.x |5.0 |Xcode 10.3 |
|2.x |5.0 |Xcode 10.2 |
|1.x |4.0 ~ 4.2 |~ Xcode 10.1 |
|1.x |4.0 ~ 4.2 |~ Xcode 10.1 |

## License

Expand Down
34 changes: 19 additions & 15 deletions Sources/Crossroad/Context.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,44 +20,48 @@ public struct Context<UserInfo> {
self.userInfo = userInfo
}

public func argument<T: Extractable>(for key: String) throws -> T {
public subscript<T: Parsable>(argument keyword: String) -> T? {
return try? argument(for: keyword)
}

public subscript<T: Parsable>(parameter key: String) -> T? {
return parameter(for: key)
}

public func argument<T: Parsable>(for key: String) throws -> T {
if let argument = arguments[key] {
if let value = T.extract(from: argument) {
if let value = T(from: argument) {
return value
}
}
throw Error.parsingArgumentFailed
}

public func parameter<T: Extractable>(for key: String, caseInsensitive: Bool = false) -> T? {
if let queryItem = queryItem(from: key, caseInsensitive: caseInsensitive) {
public func parameter<T: Parsable>(for key: String) -> T? {
if let queryItem = queryItem(from: key) {
if let queryValue = queryItem.value,
let value = T.extract(from: queryValue) {
let value = T(from: queryValue) {
return value
}
}
return nil
}

public func parameter<T: Extractable>(matchesIn regexp: NSRegularExpression) -> T? {
public func parameter<T: Parsable>(matchesIn regexp: NSRegularExpression) -> T? {
if let queryItem = queryItem(matchesIn: regexp) {
if let queryValue = queryItem.value,
let value = T.extract(from: queryValue) {
let value = T(from: queryValue) {
return value
}
}
return nil
}

private func queryItem(from key: String, caseInsensitive: Bool) -> URLQueryItem? {
func isEqual(_ lhs: String, _ rhs: String, caseInsensitive: Bool) -> Bool {
if caseInsensitive {
return lhs.lowercased() == rhs.lowercased()
} else {
return lhs == rhs
}
private func queryItem(from key: String) -> URLQueryItem? {
func isEqual(_ lhs: String, _ rhs: String) -> Bool {
return lhs.lowercased() == rhs.lowercased()
}
return parameters.first { isEqual($0.name, key, caseInsensitive: caseInsensitive) }
return parameters.first { isEqual($0.name, key) }
}

private func queryItem(matchesIn regexp: NSRegularExpression) -> URLQueryItem? {
Expand Down
62 changes: 0 additions & 62 deletions Sources/Crossroad/Extractable.swift

This file was deleted.

Loading

0 comments on commit b92a257

Please sign in to comment.