├── .gitignore ├── .sourcery.yml ├── .travis.yml ├── LICENSE ├── Package.resolved ├── Package.swift ├── Podfile ├── Podfile.lock ├── README.md ├── Sources ├── Assets │ └── .gitkeep └── Classes │ ├── .gitkeep │ ├── Extension.swift │ ├── Navigatorible.swift │ └── Parameterible.swift ├── Tests ├── Info.plist └── Tests.swift ├── URLNavigatorExt.podspec ├── URLNavigatorExt.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── xcshareddata │ └── xcschemes │ └── URLNavigatorExt-Example.xcscheme ├── URLNavigatorExt.xcworkspace ├── contents.xcworkspacedata └── xcshareddata │ └── IDEWorkspaceChecks.plist └── URLNavigatorExt ├── AppDelegate.swift ├── Info.plist ├── Models ├── Repo.swift └── User.swift ├── Networking ├── GitHub.swift └── HTTP.swift ├── Resources ├── Base.lproj │ ├── LaunchScreen.xib │ └── Main.storyboard └── Images.xcassets │ └── AppIcon.appiconset │ └── Contents.json ├── ViewControllers ├── UserListViewController.swift └── UserViewController.swift ├── Views ├── RepoCell.swift └── UserCell.swift ├── generated ├── Router.generated.swift └── RouterExtension.swift └── templates └── Router.swifttemplate /.gitignore: -------------------------------------------------------------------------------- 1 | # OS X 2 | .DS_Store 3 | 4 | # Xcode 5 | build/ 6 | *.pbxuser 7 | !default.pbxuser 8 | *.mode1v3 9 | !default.mode1v3 10 | *.mode2v3 11 | !default.mode2v3 12 | *.perspectivev3 13 | !default.perspectivev3 14 | xcuserdata/ 15 | *.xccheckout 16 | profile 17 | *.moved-aside 18 | DerivedData 19 | *.hmap 20 | *.ipa 21 | 22 | # Bundler 23 | .bundle 24 | 25 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 26 | # Carthage/Checkouts 27 | 28 | Carthage/Build 29 | 30 | # We recommend against adding the Pods directory to your .gitignore. However 31 | # you should judge for yourself, the pros and cons are mentioned at: 32 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 33 | # 34 | # Note: if you ignore the Pods directory, make sure to uncomment 35 | # `pod install` in .travis.yml 36 | # 37 | # Pods/ 38 | Pods 39 | -------------------------------------------------------------------------------- /.sourcery.yml: -------------------------------------------------------------------------------- 1 | sources: 2 | - ./URLNavigatorExt 3 | templates: 4 | - ./URLNavigatorExt/templates 5 | output: 6 | ./URLNavigatorExt/generated 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # references: 2 | # * https://www.objc.io/issues/6-build-tools/travis-ci/ 3 | # * https://github.com/supermarin/xcpretty#usage 4 | 5 | osx_image: xcode7.3 6 | language: objective-c 7 | # cache: cocoapods 8 | # podfile: Example/Podfile 9 | # before_install: 10 | # - gem install cocoapods # Since Travis is not always on latest version 11 | # - pod install --project-directory=Example 12 | script: 13 | - set -o pipefail && xcodebuild test -enableCodeCoverage YES -workspace Example/URLNavigatorExt.xcworkspace -scheme URLNavigatorExt-Example -sdk iphonesimulator9.3 ONLY_ACTIVE_ARCH=NO | xcpretty 14 | - pod lib lint 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019 goo.gle@foxmail.com 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "object": { 3 | "pins": [ 4 | { 5 | "package": "Nimble", 6 | "repositoryURL": "https://github.com/Quick/Nimble.git", 7 | "state": { 8 | "branch": null, 9 | "revision": "7a46a5fc86cb917f69e3daf79fcb045283d8f008", 10 | "version": "8.1.2" 11 | } 12 | }, 13 | { 14 | "package": "Quick", 15 | "repositoryURL": "https://github.com/Quick/Quick.git", 16 | "state": { 17 | "branch": null, 18 | "revision": "09b3becb37cb2163919a3842a4c5fa6ec7130792", 19 | "version": "2.2.1" 20 | } 21 | }, 22 | { 23 | "package": "Stubber", 24 | "repositoryURL": "https://github.com/devxoul/Stubber.git", 25 | "state": { 26 | "branch": null, 27 | "revision": "9f1833587f2ec1b590e0047b00ab11c85d3ae57b", 28 | "version": "1.5.3" 29 | } 30 | }, 31 | { 32 | "package": "URLNavigator", 33 | "repositoryURL": "https://github.com/devxoul/URLNavigator.git", 34 | "state": { 35 | "branch": null, 36 | "revision": "c493d24371cfd4ea00a7be47ad30d3b9614f0306", 37 | "version": "2.3.0" 38 | } 39 | } 40 | ] 41 | }, 42 | "version": 1 43 | } 44 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.1 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import PackageDescription 5 | 6 | let package = Package( 7 | name: "URLNavigatorExt", 8 | platforms: [ 9 | .iOS(.v9) 10 | ], 11 | products: [ 12 | .library( 13 | name: "URLNavigatorExt", 14 | targets: ["URLNavigatorExt"]), 15 | ], 16 | dependencies: [ 17 | .package(url: "https://github.com/cdoky/URLNavigator.git", .branch("master")), 18 | ], 19 | targets: [ 20 | .target( 21 | name: "URLNavigatorExt", 22 | dependencies: ["URLNavigator"], 23 | path: "Sources/Classes" 24 | ), 25 | ] 26 | ) 27 | -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | use_frameworks! 2 | 3 | target 'URLNavigatorExtExample' do 4 | pod 'URLNavigatorExt', :path => './' 5 | 6 | target 'URLNavigatorExt_Tests' do 7 | inherit! :search_paths 8 | 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - URLNavigator (2.4.1) 3 | - URLNavigatorExt (2.1.0): 4 | - URLNavigator 5 | 6 | DEPENDENCIES: 7 | - URLNavigatorExt (from `./`) 8 | 9 | SPEC REPOS: 10 | trunk: 11 | - URLNavigator 12 | 13 | EXTERNAL SOURCES: 14 | URLNavigatorExt: 15 | :path: "./" 16 | 17 | SPEC CHECKSUMS: 18 | URLNavigator: 9e277a422a5c131a3e37970c2558e0e866cc22bd 19 | URLNavigatorExt: 712e3cb67383616b1f75d1e97a6cdbb0e40ba1ec 20 | 21 | PODFILE CHECKSUM: a7c92b001813b6e4c1bcc26250e523759ee40a81 22 | 23 | COCOAPODS: 1.11.3 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # URLNavigatorExt 2 | 3 | [![CI Status](https://img.shields.io/travis/goo.gle@foxmail.com/URLNavigatorExt.svg?style=flat)](https://travis-ci.org/goo.gle@foxmail.com/URLNavigatorExt) 4 | [![Version](https://img.shields.io/cocoapods/v/URLNavigatorExt.svg?style=flat)](https://cocoapods.org/pods/URLNavigatorExt) 5 | [![License](https://img.shields.io/cocoapods/l/URLNavigatorExt.svg?style=flat)](https://cocoapods.org/pods/URLNavigatorExt) 6 | [![Platform](https://img.shields.io/cocoapods/p/URLNavigatorExt.svg?style=flat)](https://cocoapods.org/pods/URLNavigatorExt) 7 | 8 | ## Example 9 | 10 | To run the example project, clone the repo, and run `pod install && sourcery` from the Example directory first. 11 | 12 | ## Dependencies 13 | - [URLNavigator](https://github.com/devxoul/URLNavigator):Elegant URL Routing for Swift 14 | - [Sourcery](https://github.com/krzysztofzablocki/Sourcery):Meta-programming for Swift, stop writing boilerplate code. http://merowing.info 15 | 16 | ## Installation 17 | 18 | URLNavigatorExt is available through [CocoaPods](https://cocoapods.org). To install 19 | it, simply add the following line to your Podfile: 20 | 21 | ```ruby 22 | pod 'URLNavigatorExt' 23 | ``` 24 | 25 | ## Usage 26 | - install `sourcery` 27 | > `brew install sourcery` 28 | - touch `.sourcery.yml` in ${SRCROOT} [sourcery document](https://cdn.rawgit.com/krzysztofzablocki/Sourcery/master/docs/usage.html) 29 | > 30 | ```yml 31 | sources: 32 | - ./${PROJECT_NAME} 33 | templates: 34 | - ./${PROJECT_NAME}/config 35 | output: 36 | ./${PROJECT_NAME}/config 37 | - Build Phases 38 | ```shell 39 | sourcery 40 | ``` 41 | 42 | - AppDelegate.swift 43 | ```swift 44 | import URLNavigator 45 | import URLNavigatorExt 46 | 47 | let navigator = Navigator() 48 | 49 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 50 | // .... 51 | Router.registOuterUrl(jsonStr: Router.config, navigator: navigator) 52 | // .... 53 | return true 54 | } 55 | ``` 56 | 57 | - Viewcontroller 58 | - // sourcery: router: desc="这是页面描述" 59 | - // sourcery: router: name="这是页面名字" 60 | - // sourcery: router: path="这是页面的路径 eg: /mine/profile" 61 | - // sourcery: router: parameter="参数名称:参数类型(自定义类型或数组,需要实现`Typeible`协议):默认值" 62 | 63 | `注意:`ViewController 必须实现`Navigatorible`协议 64 | ```swift 65 | // sourcery: router: desc="第一个viewController" 66 | // sourcery: router: name="home_page" 67 | // sourcery: router: path="/home" 68 | // sourcery: router: parameter="type:TestEnum:.a" 69 | // sourcery: router: parameter="type1:TestEnum?:.b" 70 | // sourcery: router: parameter="p3:String?:abc123" 71 | // sourcery: router: parameter="p4:String?" 72 | // sourcery: router: parameter="p5:Test2Enum?:.c" 73 | // sourcery: router: parameter="p6:Test2Enum:.d" 74 | // sourcery: router: parameter="p7:Int64" 75 | // sourcery: router: parameter="blk:(()->Void)?" 76 | class ViewController: UIViewController, Navigatorible { 77 | var navigator: NavigatorType 78 | var parameter: Router.PRHome_page? 79 | required init(navigator: NavigatorType, parameterible: Parameterible?) { 80 | self.navigator = navigator 81 | super.init(nibName: nil, bundle: nil) 82 | self.parameter = parameterible as? Router.PRHome_page 83 | } 84 | // ... 85 | } 86 | 87 | /// output 88 | /* 89 | /// desc: 第一个viewController 90 | /// view: ViewController 91 | /// parameters: 92 | /// - blk: (()->Void)? 93 | /// - p7: Int64 94 | /// - p6: Test2Enum default: .d 95 | /// - c: 100 96 | /// - d: 101 97 | /// - p5: Test2Enum? default: .c 98 | /// - c: 100 99 | /// - d: 101 100 | /// - p4: String? 101 | /// - p3: String? default: abc123 102 | /// - type1: TestEnum? default: .b 103 | /// - a: a 104 | /// - b: b 105 | /// - type: TestEnum default: .a 106 | /// - a: a 107 | /// - b: b 108 | public static let home_page = "\(Scheme.domain)/home" 109 | 110 | /// desc: 第一个viewController 111 | /// view: ViewController 112 | /// path: /home 113 | /// name: home_page 114 | public struct PRHome_page: Parameterible { 115 | var blk: (()->Void)? 116 | var p7: Int64 117 | var p6: Test2Enum 118 | var p5: Test2Enum? 119 | var p4: String? 120 | var p3: String? 121 | var type1: TestEnum? 122 | var type: TestEnum 123 | 124 | init( 125 | type: TestEnum, 126 | p6: Test2Enum, 127 | p7: Int64, 128 | type1: TestEnum? = nil, 129 | p3: String? = nil, 130 | p4: String? = nil, 131 | p5: Test2Enum? = nil, 132 | blk: (()->Void)? = nil 133 | ) { 134 | self.blk = blk 135 | self.p7 = p7 136 | self.p6 = p6 137 | self.p5 = p5 138 | self.p4 = p4 139 | self.p3 = p3 140 | self.type1 = type1 141 | self.type = type 142 | } 143 | 144 | public static func instance(by queryItem: [String: String]) -> Self? { 145 | let items = queryItem.map({ ($0.key.lowercased(), $0.value) }) 146 | let dict = [String: String](uniqueKeysWithValues: items) 147 | var _type: TestEnum? = .a 148 | if let value = dict["type"]{ 149 | if let _enum = TestEnum(rawValue: value) { 150 | _type = _enum 151 | } 152 | } 153 | var _p6: Test2Enum? = .d 154 | if let value = dict["p6"]{ 155 | if let _value = Int(value), let _enum = Test2Enum(rawValue: _value) { 156 | _p6 = _enum 157 | } 158 | } 159 | var _p7: Int64? = nil 160 | if let value = dict["p7"]{ 161 | _p7 = Int64(value) 162 | } 163 | var type1: TestEnum? = .b 164 | if let value = dict["type1"]{ 165 | if let _enum = TestEnum(rawValue: value) { 166 | type1 = _enum 167 | } 168 | } 169 | var p3: String? = "abc123" 170 | if let value = dict["p3"]{ 171 | p3 = value 172 | } 173 | var p4: String? = "" 174 | if let value = dict["p4"]{ 175 | p4 = value 176 | } 177 | var p5: Test2Enum? = .c 178 | if let value = dict["p5"]{ 179 | if let _value = Int(value), let _enum = Test2Enum(rawValue: _value) { 180 | p5 = _enum 181 | } 182 | } 183 | guard 184 | let type = _type, 185 | let p6 = _p6, 186 | let p7 = _p7 187 | else { return nil} 188 | return PRHome_page( 189 | type: type, 190 | p6: p6, 191 | p7: p7, 192 | type1: type1, 193 | p3: p3, 194 | p4: p4, 195 | p5: p5, 196 | blk: nil 197 | ) 198 | } 199 | } 200 | */ 201 | ``` 202 | - Push 203 | ```swift 204 | let para = Router.PRHome_page(type: .a, p6: .c, p7: 100) 205 | self.navigator.push(Router.home_page, context: para) 206 | 207 | // or 208 | self.navigator.push("\(Router.home_page)?type=a&type1=b&p7=1") 209 | ``` 210 | 211 | ## Author 212 | 213 | goo.gle@foxmail.com 214 | 215 | ## License 216 | 217 | URLNavigatorExt is available under the MIT license. See the LICENSE file for more info. 218 | -------------------------------------------------------------------------------- /Sources/Assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cdoky/URLNavigatorExt/0c32a3bc5b8aa10427ee131038d805ca64f7f2c5/Sources/Assets/.gitkeep -------------------------------------------------------------------------------- /Sources/Classes/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cdoky/URLNavigatorExt/0c32a3bc5b8aa10427ee131038d805ca64f7f2c5/Sources/Classes/.gitkeep -------------------------------------------------------------------------------- /Sources/Classes/Extension.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import UIKit 3 | import URLNavigator 4 | 5 | public extension Navigator { 6 | /// Description 7 | /// 8 | /// - Parameters: 9 | /// - navigator: navigator 10 | /// - routers: key: namespace://host/path 11 | static func register( 12 | _ navigator: NavigatorProtocol, 13 | routers: [String: Parameterible.Type?]) 14 | { 15 | routers.forEach { url, param in 16 | navigator.register( 17 | url, 18 | ViewControllerFactory( 19 | parameterible: param)) 20 | } 21 | } 22 | 23 | /// ViewControllerFactory 24 | /// 25 | /// - Parameters: 26 | /// - navigator: navigator 27 | /// - parameterible: parameterible type 28 | /// - Returns: ViewControllerFactory 29 | private static func ViewControllerFactory( 30 | parameterible: Parameterible.Type?) -> ViewControllerFactory 31 | { 32 | return { url, _, context in 33 | guard let url = url.urlValue else { return nil } 34 | guard let scheme = url.scheme, 35 | let host = url.host 36 | else { 37 | print("\n//====================================") 38 | print("|Error: schme and host can't be nil, check your scheme, cannot contain special symbols|") 39 | print("|eg: iosapp://yourhost/page/a?p1=1&p2=2|") 40 | print("//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n") 41 | return nil 42 | } 43 | var parameter: Parameterible? 44 | if url.queryParameters.count != 0 { 45 | parameter = (parameterible ?? DefaultParameter.self).instance(by: url.queryParameters) 46 | } else { 47 | parameter = context as? Parameterible 48 | } 49 | 50 | let controller = instance("\(scheme).\(host)", parameter: parameter) 51 | return controller 52 | } 53 | } 54 | 55 | /// return Navigatorible instance 56 | /// 57 | /// - Parameters: 58 | /// - fullPath: 全路径(eg: URLNavigatorExt_Example.ViewController) 59 | /// - navigator: navigator实例 60 | /// - parameter: 参数 61 | /// - Returns: UIViewController 62 | class func instance( 63 | _ fullPath: String, 64 | parameter: Parameterible?) -> UIViewController? 65 | { 66 | guard let clazz = NSClassFromString("\(fullPath)") as? Navigatorible.Type else { 67 | return nil 68 | } 69 | return clazz.init(parameterible: parameter) as? UIViewController 70 | } 71 | 72 | /// return Parameterible instance 73 | /// 74 | /// - Parameters: 75 | /// - classStr: 参数类型全路径 76 | /// - queryItem: 参数字典 77 | /// - Returns: 参数对象实例 78 | class func instance(_ classStr: String, queryItem: [String: String]) -> Parameterible? { 79 | guard let clazz = NSClassFromString("\(classStr)") as? Parameterible.Type else { 80 | return nil 81 | } 82 | return clazz.instance(by: queryItem) 83 | } 84 | } 85 | 86 | // MARK: - 扩展NavigatorType 87 | 88 | public extension NavigatorProtocol { 89 | var topMostNavigation: UINavigationController? { 90 | UIViewController.topMost?.navigationController 91 | } 92 | 93 | /// pop 94 | /// 95 | /// - Parameter animated: default is true 96 | /// - Returns: Returns the popped controller. 97 | @discardableResult 98 | func pop(animated: Bool = true) -> UIViewController? { 99 | return topMostNavigation?.popViewController(animated: animated) 100 | } 101 | 102 | /// popToRoot 103 | /// 104 | /// - Parameter animated: default is true 105 | /// - Returns: Returns the popped controller. 106 | @discardableResult 107 | func popToRoot(animated: Bool = true) -> [UIViewController]? { 108 | return topMostNavigation?.popToRootViewController(animated: animated) 109 | } 110 | 111 | /// popToViewController 112 | /// - Parameters: 113 | /// - viewController: the target viewcontroller 114 | /// - animated: default is true 115 | /// - Returns: Returns the popped controller. 116 | @discardableResult 117 | func popToViewController( 118 | _ viewController: UIViewController, 119 | animated: Bool = true) -> [UIViewController]? 120 | { 121 | return topMostNavigation?.popToViewController(viewController, animated: animated) 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /Sources/Classes/Navigatorible.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Navigatorible.swift 3 | // Pods 4 | // 5 | // Created by phil on 2019/11/26. 6 | // 7 | 8 | import URLNavigator 9 | 10 | /// Navigatorible 11 | public protocol Navigatorible { 12 | /// 初始化协议 13 | /// 14 | /// - Parameters: 15 | /// - navigator: 导航对象 16 | /// - parameterible: 参数对象 17 | /// - values: 参数对象 18 | init(parameterible: Parameterible?) 19 | } 20 | -------------------------------------------------------------------------------- /Sources/Classes/Parameterible.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Parameterible.swift 3 | // Pods 4 | // 5 | // Created by phil on 2019/11/26. 6 | // 7 | 8 | /// 页面参数 9 | public protocol Parameterible { 10 | static func instance(by queryItem: [String: String]) -> Self? 11 | var queries: [String: String] { get } 12 | } 13 | 14 | /// 页面参数复杂类型 15 | public protocol Typeible { 16 | static func instance(from value: String?) -> Self? 17 | static func list(from value: String?) -> [Self]? 18 | } 19 | 20 | /// 默认参数类型 21 | public struct DefaultParameter: Parameterible { 22 | public var queries: [String : String] { 23 | return self.items 24 | } 25 | public static func instance(by queryItem: [String : String]) -> DefaultParameter? { 26 | let ins = DefaultParameter(items: queryItem) 27 | return ins 28 | } 29 | public let items: [String: String] 30 | } 31 | -------------------------------------------------------------------------------- /Tests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /Tests/Tests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import URLNavigatorExt 3 | 4 | class Tests: XCTestCase { 5 | 6 | override func setUp() { 7 | super.setUp() 8 | // Put setup code here. This method is called before the invocation of each test method in the class. 9 | } 10 | 11 | override func tearDown() { 12 | // Put teardown code here. This method is called after the invocation of each test method in the class. 13 | super.tearDown() 14 | } 15 | 16 | func testExample() { 17 | // This is an example of a functional test case. 18 | XCTAssert(true, "Pass") 19 | } 20 | 21 | func testPerformanceExample() { 22 | // This is an example of a performance test case. 23 | self.measure() { 24 | // Put the code you want to measure the time of here. 25 | } 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /URLNavigatorExt.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # Be sure to run `pod lib lint URLNavigatorExt.podspec' to ensure this is a 3 | # valid spec before submitting. 4 | # 5 | # Any lines starting with a # are optional, but their use is encouraged 6 | # To learn more about a Podspec see https://guides.cocoapods.org/syntax/podspec.html 7 | # 8 | 9 | Pod::Spec.new do |s| 10 | s.name = 'URLNavigatorExt' 11 | s.version = '3.0.0' 12 | s.summary = '使用注解自动生成Router以及Router相关参数' 13 | 14 | s.homepage = 'https://github.com/cdoky/URLNavigatorExt' 15 | s.license = { :type => 'MIT', :file => 'LICENSE' } 16 | s.author = { 'phil' => 'goo.gle@foxmail.com' } 17 | s.source = { :git => 'https://github.com/cdoky/URLNavigatorExt.git', :tag => s.version.to_s } 18 | 19 | s.ios.deployment_target = '9.0' 20 | s.swift_version = "5.0" 21 | 22 | s.source_files = 'Sources/Classes/**/*' 23 | 24 | s.dependency 'URLNavigator', '~> 2.4.0' 25 | end 26 | -------------------------------------------------------------------------------- /URLNavigatorExt.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 4A3D88D4238D0A900048D85F /* Router.generated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A3D88D1238D0A900048D85F /* Router.generated.swift */; }; 11 | 4A3D88DE238D15700048D85F /* RouterExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A3D88DD238D15700048D85F /* RouterExtension.swift */; }; 12 | 4A89CB52246658AF00CCEA9B /* HTTP.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A89CB41246658AF00CCEA9B /* HTTP.swift */; }; 13 | 4A89CB53246658AF00CCEA9B /* GitHub.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A89CB42246658AF00CCEA9B /* GitHub.swift */; }; 14 | 4A89CB54246658AF00CCEA9B /* User.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A89CB44246658AF00CCEA9B /* User.swift */; }; 15 | 4A89CB55246658AF00CCEA9B /* Repo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A89CB45246658AF00CCEA9B /* Repo.swift */; }; 16 | 4A89CB59246658AF00CCEA9B /* RepoCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A89CB4D246658AF00CCEA9B /* RepoCell.swift */; }; 17 | 4A89CB5A246658AF00CCEA9B /* UserCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A89CB4E246658AF00CCEA9B /* UserCell.swift */; }; 18 | 4A89CB5B246658AF00CCEA9B /* UserViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A89CB50246658AF00CCEA9B /* UserViewController.swift */; }; 19 | 4A89CB5C246658AF00CCEA9B /* UserListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A89CB51246658AF00CCEA9B /* UserListViewController.swift */; }; 20 | 4A89CB5D2466592C00CCEA9B /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4A89CB47246658AF00CCEA9B /* LaunchScreen.xib */; }; 21 | 4A89CB5E2466592C00CCEA9B /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4A89CB49246658AF00CCEA9B /* Main.storyboard */; }; 22 | 4A89CB5F2466592C00CCEA9B /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4A89CB4B246658AF00CCEA9B /* Images.xcassets */; }; 23 | 607FACD61AFB9204008FA782 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACD51AFB9204008FA782 /* AppDelegate.swift */; }; 24 | 607FACEC1AFB9204008FA782 /* Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACEB1AFB9204008FA782 /* Tests.swift */; }; 25 | A57D45DB9F475F47A27ECD9C /* Pods_URLNavigatorExtExample.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 21A0ED557C0405B5897B0146 /* Pods_URLNavigatorExtExample.framework */; }; 26 | F37CB8411FAF8F21EF41E0C4 /* Pods_URLNavigatorExt_Tests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 233D763245C80134182BD023 /* Pods_URLNavigatorExt_Tests.framework */; }; 27 | /* End PBXBuildFile section */ 28 | 29 | /* Begin PBXContainerItemProxy section */ 30 | 607FACE61AFB9204008FA782 /* PBXContainerItemProxy */ = { 31 | isa = PBXContainerItemProxy; 32 | containerPortal = 607FACC81AFB9204008FA782 /* Project object */; 33 | proxyType = 1; 34 | remoteGlobalIDString = 607FACCF1AFB9204008FA782; 35 | remoteInfo = URLNavigatorExt; 36 | }; 37 | /* End PBXContainerItemProxy section */ 38 | 39 | /* Begin PBXFileReference section */ 40 | 05A5EEE2331F062188FF2757 /* Pods-URLNavigatorExt_Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-URLNavigatorExt_Example.release.xcconfig"; path = "Target Support Files/Pods-URLNavigatorExt_Example/Pods-URLNavigatorExt_Example.release.xcconfig"; sourceTree = ""; }; 41 | 21A0ED557C0405B5897B0146 /* Pods_URLNavigatorExtExample.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_URLNavigatorExtExample.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 42 | 233D763245C80134182BD023 /* Pods_URLNavigatorExt_Tests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_URLNavigatorExt_Tests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 43 | 42A91B13283F1E4100749F0F /* LICENSE */ = {isa = PBXFileReference; lastKnownFileType = text; path = LICENSE; sourceTree = ""; }; 44 | 42A91B14283F1E4100749F0F /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; 45 | 42A91B15283F1E4100749F0F /* URLNavigatorExt.podspec */ = {isa = PBXFileReference; lastKnownFileType = text; path = URLNavigatorExt.podspec; sourceTree = ""; }; 46 | 4A3D88D1238D0A900048D85F /* Router.generated.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Router.generated.swift; sourceTree = ""; }; 47 | 4A3D88D9238D0AC10048D85F /* Router.swifttemplate */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Router.swifttemplate; sourceTree = ""; }; 48 | 4A3D88DD238D15700048D85F /* RouterExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RouterExtension.swift; sourceTree = ""; }; 49 | 4A89CB41246658AF00CCEA9B /* HTTP.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HTTP.swift; sourceTree = ""; }; 50 | 4A89CB42246658AF00CCEA9B /* GitHub.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GitHub.swift; sourceTree = ""; }; 51 | 4A89CB44246658AF00CCEA9B /* User.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = User.swift; sourceTree = ""; }; 52 | 4A89CB45246658AF00CCEA9B /* Repo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Repo.swift; sourceTree = ""; }; 53 | 4A89CB48246658AF00CCEA9B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; 54 | 4A89CB4A246658AF00CCEA9B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 55 | 4A89CB4B246658AF00CCEA9B /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 56 | 4A89CB4D246658AF00CCEA9B /* RepoCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RepoCell.swift; sourceTree = ""; }; 57 | 4A89CB4E246658AF00CCEA9B /* UserCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserCell.swift; sourceTree = ""; }; 58 | 4A89CB50246658AF00CCEA9B /* UserViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserViewController.swift; sourceTree = ""; }; 59 | 4A89CB51246658AF00CCEA9B /* UserListViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserListViewController.swift; sourceTree = ""; }; 60 | 607FACD01AFB9204008FA782 /* URLNavigatorExtExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = URLNavigatorExtExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 61 | 607FACD41AFB9204008FA782 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 62 | 607FACD51AFB9204008FA782 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 63 | 607FACE51AFB9204008FA782 /* URLNavigatorExt_Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = URLNavigatorExt_Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 64 | 607FACEA1AFB9204008FA782 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 65 | 607FACEB1AFB9204008FA782 /* Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tests.swift; sourceTree = ""; }; 66 | 9674D5DF1330AADD4EC3C9BA /* Pods-URLNavigatorExtExample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-URLNavigatorExtExample.release.xcconfig"; path = "Target Support Files/Pods-URLNavigatorExtExample/Pods-URLNavigatorExtExample.release.xcconfig"; sourceTree = ""; }; 67 | AF1B15A4297CAE860F6095C4 /* Pods-URLNavigatorExt_Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-URLNavigatorExt_Example.debug.xcconfig"; path = "Target Support Files/Pods-URLNavigatorExt_Example/Pods-URLNavigatorExt_Example.debug.xcconfig"; sourceTree = ""; }; 68 | E0059362AA10E97C777101C3 /* Pods-URLNavigatorExtExample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-URLNavigatorExtExample.debug.xcconfig"; path = "Target Support Files/Pods-URLNavigatorExtExample/Pods-URLNavigatorExtExample.debug.xcconfig"; sourceTree = ""; }; 69 | EB81DAC8C082B0DADAEA98EF /* Pods-URLNavigatorExt_Tests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-URLNavigatorExt_Tests.release.xcconfig"; path = "Target Support Files/Pods-URLNavigatorExt_Tests/Pods-URLNavigatorExt_Tests.release.xcconfig"; sourceTree = ""; }; 70 | ED5FA7A8172BEF0F92E2081D /* Pods-URLNavigatorExt_Tests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-URLNavigatorExt_Tests.debug.xcconfig"; path = "Target Support Files/Pods-URLNavigatorExt_Tests/Pods-URLNavigatorExt_Tests.debug.xcconfig"; sourceTree = ""; }; 71 | /* End PBXFileReference section */ 72 | 73 | /* Begin PBXFrameworksBuildPhase section */ 74 | 607FACCD1AFB9204008FA782 /* Frameworks */ = { 75 | isa = PBXFrameworksBuildPhase; 76 | buildActionMask = 2147483647; 77 | files = ( 78 | A57D45DB9F475F47A27ECD9C /* Pods_URLNavigatorExtExample.framework in Frameworks */, 79 | ); 80 | runOnlyForDeploymentPostprocessing = 0; 81 | }; 82 | 607FACE21AFB9204008FA782 /* Frameworks */ = { 83 | isa = PBXFrameworksBuildPhase; 84 | buildActionMask = 2147483647; 85 | files = ( 86 | F37CB8411FAF8F21EF41E0C4 /* Pods_URLNavigatorExt_Tests.framework in Frameworks */, 87 | ); 88 | runOnlyForDeploymentPostprocessing = 0; 89 | }; 90 | /* End PBXFrameworksBuildPhase section */ 91 | 92 | /* Begin PBXGroup section */ 93 | 4A3D88CF238D0A900048D85F /* generated */ = { 94 | isa = PBXGroup; 95 | children = ( 96 | 4A3D88D1238D0A900048D85F /* Router.generated.swift */, 97 | 4A3D88DD238D15700048D85F /* RouterExtension.swift */, 98 | ); 99 | path = generated; 100 | sourceTree = ""; 101 | }; 102 | 4A3D88D6238D0AC10048D85F /* templates */ = { 103 | isa = PBXGroup; 104 | children = ( 105 | 4A3D88D9238D0AC10048D85F /* Router.swifttemplate */, 106 | ); 107 | path = templates; 108 | sourceTree = ""; 109 | }; 110 | 4A89CB40246658AF00CCEA9B /* Networking */ = { 111 | isa = PBXGroup; 112 | children = ( 113 | 4A89CB41246658AF00CCEA9B /* HTTP.swift */, 114 | 4A89CB42246658AF00CCEA9B /* GitHub.swift */, 115 | ); 116 | path = Networking; 117 | sourceTree = ""; 118 | }; 119 | 4A89CB43246658AF00CCEA9B /* Models */ = { 120 | isa = PBXGroup; 121 | children = ( 122 | 4A89CB44246658AF00CCEA9B /* User.swift */, 123 | 4A89CB45246658AF00CCEA9B /* Repo.swift */, 124 | ); 125 | path = Models; 126 | sourceTree = ""; 127 | }; 128 | 4A89CB46246658AF00CCEA9B /* Resources */ = { 129 | isa = PBXGroup; 130 | children = ( 131 | 4A89CB47246658AF00CCEA9B /* LaunchScreen.xib */, 132 | 4A89CB49246658AF00CCEA9B /* Main.storyboard */, 133 | 4A89CB4B246658AF00CCEA9B /* Images.xcassets */, 134 | ); 135 | path = Resources; 136 | sourceTree = ""; 137 | }; 138 | 4A89CB4C246658AF00CCEA9B /* Views */ = { 139 | isa = PBXGroup; 140 | children = ( 141 | 4A89CB4D246658AF00CCEA9B /* RepoCell.swift */, 142 | 4A89CB4E246658AF00CCEA9B /* UserCell.swift */, 143 | ); 144 | path = Views; 145 | sourceTree = ""; 146 | }; 147 | 4A89CB4F246658AF00CCEA9B /* ViewControllers */ = { 148 | isa = PBXGroup; 149 | children = ( 150 | 4A89CB50246658AF00CCEA9B /* UserViewController.swift */, 151 | 4A89CB51246658AF00CCEA9B /* UserListViewController.swift */, 152 | ); 153 | path = ViewControllers; 154 | sourceTree = ""; 155 | }; 156 | 607FACC71AFB9204008FA782 = { 157 | isa = PBXGroup; 158 | children = ( 159 | 607FACF51AFB993E008FA782 /* Podspec Metadata */, 160 | 607FACD21AFB9204008FA782 /* Example for URLNavigatorExt */, 161 | 607FACE81AFB9204008FA782 /* Tests */, 162 | 607FACD11AFB9204008FA782 /* Products */, 163 | DF95AF4BD2551510E46CCDE8 /* Pods */, 164 | B7241F1BB7FCA414B9F5752B /* Frameworks */, 165 | ); 166 | sourceTree = ""; 167 | }; 168 | 607FACD11AFB9204008FA782 /* Products */ = { 169 | isa = PBXGroup; 170 | children = ( 171 | 607FACD01AFB9204008FA782 /* URLNavigatorExtExample.app */, 172 | 607FACE51AFB9204008FA782 /* URLNavigatorExt_Tests.xctest */, 173 | ); 174 | name = Products; 175 | sourceTree = ""; 176 | }; 177 | 607FACD21AFB9204008FA782 /* Example for URLNavigatorExt */ = { 178 | isa = PBXGroup; 179 | children = ( 180 | 4A89CB43246658AF00CCEA9B /* Models */, 181 | 4A89CB40246658AF00CCEA9B /* Networking */, 182 | 4A89CB46246658AF00CCEA9B /* Resources */, 183 | 4A89CB4F246658AF00CCEA9B /* ViewControllers */, 184 | 4A89CB4C246658AF00CCEA9B /* Views */, 185 | 4A3D88D6238D0AC10048D85F /* templates */, 186 | 4A3D88CF238D0A900048D85F /* generated */, 187 | 607FACD51AFB9204008FA782 /* AppDelegate.swift */, 188 | 607FACD31AFB9204008FA782 /* Supporting Files */, 189 | ); 190 | name = "Example for URLNavigatorExt"; 191 | path = URLNavigatorExt; 192 | sourceTree = ""; 193 | }; 194 | 607FACD31AFB9204008FA782 /* Supporting Files */ = { 195 | isa = PBXGroup; 196 | children = ( 197 | 607FACD41AFB9204008FA782 /* Info.plist */, 198 | ); 199 | name = "Supporting Files"; 200 | sourceTree = ""; 201 | }; 202 | 607FACE81AFB9204008FA782 /* Tests */ = { 203 | isa = PBXGroup; 204 | children = ( 205 | 607FACEB1AFB9204008FA782 /* Tests.swift */, 206 | 607FACE91AFB9204008FA782 /* Supporting Files */, 207 | ); 208 | path = Tests; 209 | sourceTree = ""; 210 | }; 211 | 607FACE91AFB9204008FA782 /* Supporting Files */ = { 212 | isa = PBXGroup; 213 | children = ( 214 | 607FACEA1AFB9204008FA782 /* Info.plist */, 215 | ); 216 | name = "Supporting Files"; 217 | sourceTree = ""; 218 | }; 219 | 607FACF51AFB993E008FA782 /* Podspec Metadata */ = { 220 | isa = PBXGroup; 221 | children = ( 222 | 42A91B13283F1E4100749F0F /* LICENSE */, 223 | 42A91B14283F1E4100749F0F /* README.md */, 224 | 42A91B15283F1E4100749F0F /* URLNavigatorExt.podspec */, 225 | ); 226 | name = "Podspec Metadata"; 227 | sourceTree = ""; 228 | }; 229 | B7241F1BB7FCA414B9F5752B /* Frameworks */ = { 230 | isa = PBXGroup; 231 | children = ( 232 | 233D763245C80134182BD023 /* Pods_URLNavigatorExt_Tests.framework */, 233 | 21A0ED557C0405B5897B0146 /* Pods_URLNavigatorExtExample.framework */, 234 | ); 235 | name = Frameworks; 236 | sourceTree = ""; 237 | }; 238 | DF95AF4BD2551510E46CCDE8 /* Pods */ = { 239 | isa = PBXGroup; 240 | children = ( 241 | AF1B15A4297CAE860F6095C4 /* Pods-URLNavigatorExt_Example.debug.xcconfig */, 242 | 05A5EEE2331F062188FF2757 /* Pods-URLNavigatorExt_Example.release.xcconfig */, 243 | ED5FA7A8172BEF0F92E2081D /* Pods-URLNavigatorExt_Tests.debug.xcconfig */, 244 | EB81DAC8C082B0DADAEA98EF /* Pods-URLNavigatorExt_Tests.release.xcconfig */, 245 | E0059362AA10E97C777101C3 /* Pods-URLNavigatorExtExample.debug.xcconfig */, 246 | 9674D5DF1330AADD4EC3C9BA /* Pods-URLNavigatorExtExample.release.xcconfig */, 247 | ); 248 | path = Pods; 249 | sourceTree = ""; 250 | }; 251 | /* End PBXGroup section */ 252 | 253 | /* Begin PBXNativeTarget section */ 254 | 607FACCF1AFB9204008FA782 /* URLNavigatorExtExample */ = { 255 | isa = PBXNativeTarget; 256 | buildConfigurationList = 607FACEF1AFB9204008FA782 /* Build configuration list for PBXNativeTarget "URLNavigatorExtExample" */; 257 | buildPhases = ( 258 | C07543A746CE57508983301A /* [CP] Check Pods Manifest.lock */, 259 | 607FACCC1AFB9204008FA782 /* Sources */, 260 | 607FACCD1AFB9204008FA782 /* Frameworks */, 261 | 607FACCE1AFB9204008FA782 /* Resources */, 262 | 7A36103D5AB8706FB2242003 /* [CP] Embed Pods Frameworks */, 263 | ); 264 | buildRules = ( 265 | ); 266 | dependencies = ( 267 | ); 268 | name = URLNavigatorExtExample; 269 | productName = URLNavigatorExt; 270 | productReference = 607FACD01AFB9204008FA782 /* URLNavigatorExtExample.app */; 271 | productType = "com.apple.product-type.application"; 272 | }; 273 | 607FACE41AFB9204008FA782 /* URLNavigatorExt_Tests */ = { 274 | isa = PBXNativeTarget; 275 | buildConfigurationList = 607FACF21AFB9204008FA782 /* Build configuration list for PBXNativeTarget "URLNavigatorExt_Tests" */; 276 | buildPhases = ( 277 | 655EA01B42523418615CD1C3 /* [CP] Check Pods Manifest.lock */, 278 | 607FACE11AFB9204008FA782 /* Sources */, 279 | 607FACE21AFB9204008FA782 /* Frameworks */, 280 | 607FACE31AFB9204008FA782 /* Resources */, 281 | ); 282 | buildRules = ( 283 | ); 284 | dependencies = ( 285 | 607FACE71AFB9204008FA782 /* PBXTargetDependency */, 286 | ); 287 | name = URLNavigatorExt_Tests; 288 | productName = Tests; 289 | productReference = 607FACE51AFB9204008FA782 /* URLNavigatorExt_Tests.xctest */; 290 | productType = "com.apple.product-type.bundle.unit-test"; 291 | }; 292 | /* End PBXNativeTarget section */ 293 | 294 | /* Begin PBXProject section */ 295 | 607FACC81AFB9204008FA782 /* Project object */ = { 296 | isa = PBXProject; 297 | attributes = { 298 | LastSwiftUpdateCheck = 0830; 299 | LastUpgradeCheck = 0830; 300 | ORGANIZATIONNAME = CocoaPods; 301 | TargetAttributes = { 302 | 607FACCF1AFB9204008FA782 = { 303 | CreatedOnToolsVersion = 6.3.1; 304 | LastSwiftMigration = 0900; 305 | }; 306 | 607FACE41AFB9204008FA782 = { 307 | CreatedOnToolsVersion = 6.3.1; 308 | LastSwiftMigration = 0900; 309 | TestTargetID = 607FACCF1AFB9204008FA782; 310 | }; 311 | }; 312 | }; 313 | buildConfigurationList = 607FACCB1AFB9204008FA782 /* Build configuration list for PBXProject "URLNavigatorExt" */; 314 | compatibilityVersion = "Xcode 3.2"; 315 | developmentRegion = English; 316 | hasScannedForEncodings = 0; 317 | knownRegions = ( 318 | English, 319 | en, 320 | Base, 321 | ); 322 | mainGroup = 607FACC71AFB9204008FA782; 323 | productRefGroup = 607FACD11AFB9204008FA782 /* Products */; 324 | projectDirPath = ""; 325 | projectRoot = ""; 326 | targets = ( 327 | 607FACCF1AFB9204008FA782 /* URLNavigatorExtExample */, 328 | 607FACE41AFB9204008FA782 /* URLNavigatorExt_Tests */, 329 | ); 330 | }; 331 | /* End PBXProject section */ 332 | 333 | /* Begin PBXResourcesBuildPhase section */ 334 | 607FACCE1AFB9204008FA782 /* Resources */ = { 335 | isa = PBXResourcesBuildPhase; 336 | buildActionMask = 2147483647; 337 | files = ( 338 | 4A89CB5D2466592C00CCEA9B /* LaunchScreen.xib in Resources */, 339 | 4A89CB5E2466592C00CCEA9B /* Main.storyboard in Resources */, 340 | 4A89CB5F2466592C00CCEA9B /* Images.xcassets in Resources */, 341 | ); 342 | runOnlyForDeploymentPostprocessing = 0; 343 | }; 344 | 607FACE31AFB9204008FA782 /* Resources */ = { 345 | isa = PBXResourcesBuildPhase; 346 | buildActionMask = 2147483647; 347 | files = ( 348 | ); 349 | runOnlyForDeploymentPostprocessing = 0; 350 | }; 351 | /* End PBXResourcesBuildPhase section */ 352 | 353 | /* Begin PBXShellScriptBuildPhase section */ 354 | 655EA01B42523418615CD1C3 /* [CP] Check Pods Manifest.lock */ = { 355 | isa = PBXShellScriptBuildPhase; 356 | buildActionMask = 2147483647; 357 | files = ( 358 | ); 359 | inputFileListPaths = ( 360 | ); 361 | inputPaths = ( 362 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 363 | "${PODS_ROOT}/Manifest.lock", 364 | ); 365 | name = "[CP] Check Pods Manifest.lock"; 366 | outputFileListPaths = ( 367 | ); 368 | outputPaths = ( 369 | "$(DERIVED_FILE_DIR)/Pods-URLNavigatorExt_Tests-checkManifestLockResult.txt", 370 | ); 371 | runOnlyForDeploymentPostprocessing = 0; 372 | shellPath = /bin/sh; 373 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 374 | showEnvVarsInLog = 0; 375 | }; 376 | 7A36103D5AB8706FB2242003 /* [CP] Embed Pods Frameworks */ = { 377 | isa = PBXShellScriptBuildPhase; 378 | buildActionMask = 2147483647; 379 | files = ( 380 | ); 381 | inputPaths = ( 382 | "${PODS_ROOT}/Target Support Files/Pods-URLNavigatorExtExample/Pods-URLNavigatorExtExample-frameworks.sh", 383 | "${BUILT_PRODUCTS_DIR}/URLNavigator/URLNavigator.framework", 384 | "${BUILT_PRODUCTS_DIR}/URLNavigatorExt/URLNavigatorExt.framework", 385 | ); 386 | name = "[CP] Embed Pods Frameworks"; 387 | outputPaths = ( 388 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/URLNavigator.framework", 389 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/URLNavigatorExt.framework", 390 | ); 391 | runOnlyForDeploymentPostprocessing = 0; 392 | shellPath = /bin/sh; 393 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-URLNavigatorExtExample/Pods-URLNavigatorExtExample-frameworks.sh\"\n"; 394 | showEnvVarsInLog = 0; 395 | }; 396 | C07543A746CE57508983301A /* [CP] Check Pods Manifest.lock */ = { 397 | isa = PBXShellScriptBuildPhase; 398 | buildActionMask = 2147483647; 399 | files = ( 400 | ); 401 | inputFileListPaths = ( 402 | ); 403 | inputPaths = ( 404 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 405 | "${PODS_ROOT}/Manifest.lock", 406 | ); 407 | name = "[CP] Check Pods Manifest.lock"; 408 | outputFileListPaths = ( 409 | ); 410 | outputPaths = ( 411 | "$(DERIVED_FILE_DIR)/Pods-URLNavigatorExtExample-checkManifestLockResult.txt", 412 | ); 413 | runOnlyForDeploymentPostprocessing = 0; 414 | shellPath = /bin/sh; 415 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 416 | showEnvVarsInLog = 0; 417 | }; 418 | /* End PBXShellScriptBuildPhase section */ 419 | 420 | /* Begin PBXSourcesBuildPhase section */ 421 | 607FACCC1AFB9204008FA782 /* Sources */ = { 422 | isa = PBXSourcesBuildPhase; 423 | buildActionMask = 2147483647; 424 | files = ( 425 | 4A89CB55246658AF00CCEA9B /* Repo.swift in Sources */, 426 | 4A89CB5C246658AF00CCEA9B /* UserListViewController.swift in Sources */, 427 | 4A89CB5B246658AF00CCEA9B /* UserViewController.swift in Sources */, 428 | 4A89CB53246658AF00CCEA9B /* GitHub.swift in Sources */, 429 | 4A3D88DE238D15700048D85F /* RouterExtension.swift in Sources */, 430 | 4A89CB54246658AF00CCEA9B /* User.swift in Sources */, 431 | 4A89CB52246658AF00CCEA9B /* HTTP.swift in Sources */, 432 | 4A89CB5A246658AF00CCEA9B /* UserCell.swift in Sources */, 433 | 607FACD61AFB9204008FA782 /* AppDelegate.swift in Sources */, 434 | 4A3D88D4238D0A900048D85F /* Router.generated.swift in Sources */, 435 | 4A89CB59246658AF00CCEA9B /* RepoCell.swift in Sources */, 436 | ); 437 | runOnlyForDeploymentPostprocessing = 0; 438 | }; 439 | 607FACE11AFB9204008FA782 /* Sources */ = { 440 | isa = PBXSourcesBuildPhase; 441 | buildActionMask = 2147483647; 442 | files = ( 443 | 607FACEC1AFB9204008FA782 /* Tests.swift in Sources */, 444 | ); 445 | runOnlyForDeploymentPostprocessing = 0; 446 | }; 447 | /* End PBXSourcesBuildPhase section */ 448 | 449 | /* Begin PBXTargetDependency section */ 450 | 607FACE71AFB9204008FA782 /* PBXTargetDependency */ = { 451 | isa = PBXTargetDependency; 452 | target = 607FACCF1AFB9204008FA782 /* URLNavigatorExtExample */; 453 | targetProxy = 607FACE61AFB9204008FA782 /* PBXContainerItemProxy */; 454 | }; 455 | /* End PBXTargetDependency section */ 456 | 457 | /* Begin PBXVariantGroup section */ 458 | 4A89CB47246658AF00CCEA9B /* LaunchScreen.xib */ = { 459 | isa = PBXVariantGroup; 460 | children = ( 461 | 4A89CB48246658AF00CCEA9B /* Base */, 462 | ); 463 | name = LaunchScreen.xib; 464 | sourceTree = ""; 465 | }; 466 | 4A89CB49246658AF00CCEA9B /* Main.storyboard */ = { 467 | isa = PBXVariantGroup; 468 | children = ( 469 | 4A89CB4A246658AF00CCEA9B /* Base */, 470 | ); 471 | name = Main.storyboard; 472 | sourceTree = ""; 473 | }; 474 | /* End PBXVariantGroup section */ 475 | 476 | /* Begin XCBuildConfiguration section */ 477 | 607FACED1AFB9204008FA782 /* Debug */ = { 478 | isa = XCBuildConfiguration; 479 | buildSettings = { 480 | ALWAYS_SEARCH_USER_PATHS = NO; 481 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 482 | CLANG_CXX_LIBRARY = "libc++"; 483 | CLANG_ENABLE_MODULES = YES; 484 | CLANG_ENABLE_OBJC_ARC = YES; 485 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 486 | CLANG_WARN_BOOL_CONVERSION = YES; 487 | CLANG_WARN_COMMA = YES; 488 | CLANG_WARN_CONSTANT_CONVERSION = YES; 489 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 490 | CLANG_WARN_EMPTY_BODY = YES; 491 | CLANG_WARN_ENUM_CONVERSION = YES; 492 | CLANG_WARN_INFINITE_RECURSION = YES; 493 | CLANG_WARN_INT_CONVERSION = YES; 494 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 495 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 496 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 497 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 498 | CLANG_WARN_STRICT_PROTOTYPES = YES; 499 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 500 | CLANG_WARN_UNREACHABLE_CODE = YES; 501 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 502 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 503 | COPY_PHASE_STRIP = NO; 504 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 505 | ENABLE_STRICT_OBJC_MSGSEND = YES; 506 | ENABLE_TESTABILITY = YES; 507 | GCC_C_LANGUAGE_STANDARD = gnu99; 508 | GCC_DYNAMIC_NO_PIC = NO; 509 | GCC_NO_COMMON_BLOCKS = YES; 510 | GCC_OPTIMIZATION_LEVEL = 0; 511 | GCC_PREPROCESSOR_DEFINITIONS = ( 512 | "DEBUG=1", 513 | "$(inherited)", 514 | ); 515 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 516 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 517 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 518 | GCC_WARN_UNDECLARED_SELECTOR = YES; 519 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 520 | GCC_WARN_UNUSED_FUNCTION = YES; 521 | GCC_WARN_UNUSED_VARIABLE = YES; 522 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 523 | MTL_ENABLE_DEBUG_INFO = YES; 524 | ONLY_ACTIVE_ARCH = YES; 525 | SDKROOT = iphoneos; 526 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 527 | }; 528 | name = Debug; 529 | }; 530 | 607FACEE1AFB9204008FA782 /* Release */ = { 531 | isa = XCBuildConfiguration; 532 | buildSettings = { 533 | ALWAYS_SEARCH_USER_PATHS = NO; 534 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 535 | CLANG_CXX_LIBRARY = "libc++"; 536 | CLANG_ENABLE_MODULES = YES; 537 | CLANG_ENABLE_OBJC_ARC = YES; 538 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 539 | CLANG_WARN_BOOL_CONVERSION = YES; 540 | CLANG_WARN_COMMA = YES; 541 | CLANG_WARN_CONSTANT_CONVERSION = YES; 542 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 543 | CLANG_WARN_EMPTY_BODY = YES; 544 | CLANG_WARN_ENUM_CONVERSION = YES; 545 | CLANG_WARN_INFINITE_RECURSION = YES; 546 | CLANG_WARN_INT_CONVERSION = YES; 547 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 548 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 549 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 550 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 551 | CLANG_WARN_STRICT_PROTOTYPES = YES; 552 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 553 | CLANG_WARN_UNREACHABLE_CODE = YES; 554 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 555 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 556 | COPY_PHASE_STRIP = NO; 557 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 558 | ENABLE_NS_ASSERTIONS = NO; 559 | ENABLE_STRICT_OBJC_MSGSEND = YES; 560 | GCC_C_LANGUAGE_STANDARD = gnu99; 561 | GCC_NO_COMMON_BLOCKS = YES; 562 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 563 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 564 | GCC_WARN_UNDECLARED_SELECTOR = YES; 565 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 566 | GCC_WARN_UNUSED_FUNCTION = YES; 567 | GCC_WARN_UNUSED_VARIABLE = YES; 568 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 569 | MTL_ENABLE_DEBUG_INFO = NO; 570 | SDKROOT = iphoneos; 571 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 572 | VALIDATE_PRODUCT = YES; 573 | }; 574 | name = Release; 575 | }; 576 | 607FACF01AFB9204008FA782 /* Debug */ = { 577 | isa = XCBuildConfiguration; 578 | baseConfigurationReference = E0059362AA10E97C777101C3 /* Pods-URLNavigatorExtExample.debug.xcconfig */; 579 | buildSettings = { 580 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 581 | INFOPLIST_FILE = URLNavigatorExt/Info.plist; 582 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 583 | MODULE_NAME = ExampleApp; 584 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.$(PRODUCT_NAME:rfc1034identifier)"; 585 | PRODUCT_NAME = "$(TARGET_NAME)"; 586 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 587 | SWIFT_VERSION = 4.0; 588 | }; 589 | name = Debug; 590 | }; 591 | 607FACF11AFB9204008FA782 /* Release */ = { 592 | isa = XCBuildConfiguration; 593 | baseConfigurationReference = 9674D5DF1330AADD4EC3C9BA /* Pods-URLNavigatorExtExample.release.xcconfig */; 594 | buildSettings = { 595 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 596 | INFOPLIST_FILE = URLNavigatorExt/Info.plist; 597 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 598 | MODULE_NAME = ExampleApp; 599 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.$(PRODUCT_NAME:rfc1034identifier)"; 600 | PRODUCT_NAME = "$(TARGET_NAME)"; 601 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 602 | SWIFT_VERSION = 4.0; 603 | }; 604 | name = Release; 605 | }; 606 | 607FACF31AFB9204008FA782 /* Debug */ = { 607 | isa = XCBuildConfiguration; 608 | baseConfigurationReference = ED5FA7A8172BEF0F92E2081D /* Pods-URLNavigatorExt_Tests.debug.xcconfig */; 609 | buildSettings = { 610 | FRAMEWORK_SEARCH_PATHS = ( 611 | "$(PLATFORM_DIR)/Developer/Library/Frameworks", 612 | "$(inherited)", 613 | ); 614 | GCC_PREPROCESSOR_DEFINITIONS = ( 615 | "DEBUG=1", 616 | "$(inherited)", 617 | ); 618 | INFOPLIST_FILE = Tests/Info.plist; 619 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 620 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.$(PRODUCT_NAME:rfc1034identifier)"; 621 | PRODUCT_NAME = "$(TARGET_NAME)"; 622 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 623 | SWIFT_VERSION = 4.0; 624 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/URLNavigatorExt_Example.app/URLNavigatorExt_Example"; 625 | }; 626 | name = Debug; 627 | }; 628 | 607FACF41AFB9204008FA782 /* Release */ = { 629 | isa = XCBuildConfiguration; 630 | baseConfigurationReference = EB81DAC8C082B0DADAEA98EF /* Pods-URLNavigatorExt_Tests.release.xcconfig */; 631 | buildSettings = { 632 | FRAMEWORK_SEARCH_PATHS = ( 633 | "$(PLATFORM_DIR)/Developer/Library/Frameworks", 634 | "$(inherited)", 635 | ); 636 | INFOPLIST_FILE = Tests/Info.plist; 637 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 638 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.$(PRODUCT_NAME:rfc1034identifier)"; 639 | PRODUCT_NAME = "$(TARGET_NAME)"; 640 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 641 | SWIFT_VERSION = 4.0; 642 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/URLNavigatorExt_Example.app/URLNavigatorExt_Example"; 643 | }; 644 | name = Release; 645 | }; 646 | /* End XCBuildConfiguration section */ 647 | 648 | /* Begin XCConfigurationList section */ 649 | 607FACCB1AFB9204008FA782 /* Build configuration list for PBXProject "URLNavigatorExt" */ = { 650 | isa = XCConfigurationList; 651 | buildConfigurations = ( 652 | 607FACED1AFB9204008FA782 /* Debug */, 653 | 607FACEE1AFB9204008FA782 /* Release */, 654 | ); 655 | defaultConfigurationIsVisible = 0; 656 | defaultConfigurationName = Release; 657 | }; 658 | 607FACEF1AFB9204008FA782 /* Build configuration list for PBXNativeTarget "URLNavigatorExtExample" */ = { 659 | isa = XCConfigurationList; 660 | buildConfigurations = ( 661 | 607FACF01AFB9204008FA782 /* Debug */, 662 | 607FACF11AFB9204008FA782 /* Release */, 663 | ); 664 | defaultConfigurationIsVisible = 0; 665 | defaultConfigurationName = Release; 666 | }; 667 | 607FACF21AFB9204008FA782 /* Build configuration list for PBXNativeTarget "URLNavigatorExt_Tests" */ = { 668 | isa = XCConfigurationList; 669 | buildConfigurations = ( 670 | 607FACF31AFB9204008FA782 /* Debug */, 671 | 607FACF41AFB9204008FA782 /* Release */, 672 | ); 673 | defaultConfigurationIsVisible = 0; 674 | defaultConfigurationName = Release; 675 | }; 676 | /* End XCConfigurationList section */ 677 | }; 678 | rootObject = 607FACC81AFB9204008FA782 /* Project object */; 679 | } 680 | -------------------------------------------------------------------------------- /URLNavigatorExt.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /URLNavigatorExt.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /URLNavigatorExt.xcodeproj/xcshareddata/xcschemes/URLNavigatorExt-Example.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 51 | 52 | 53 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 76 | 78 | 84 | 85 | 86 | 87 | 93 | 95 | 101 | 102 | 103 | 104 | 106 | 107 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /URLNavigatorExt.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /URLNavigatorExt.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /URLNavigatorExt/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // URLNavigatorExt 4 | // 5 | // Created by goo.gle@foxmail.com on 11/26/2019. 6 | // Copyright (c) 2019 goo.gle@foxmail.com. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import URLNavigator 11 | import URLNavigatorExt 12 | 13 | let navigator = Navigator() 14 | 15 | @UIApplicationMain 16 | class AppDelegate: UIResponder, UIApplicationDelegate { 17 | 18 | var window: UIWindow? 19 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 20 | // Override point for customization after application launch. 21 | Router.registOuterUrl(jsonStr: Router.pageConfigs, navigator: navigator) 22 | let window = UIWindow(frame: UIScreen.main.bounds) 23 | window.makeKeyAndVisible() 24 | window.backgroundColor = .white 25 | 26 | let userListViewController = UserListViewController(navigator: navigator) 27 | window.rootViewController = UINavigationController(rootViewController: userListViewController) 28 | 29 | self.window = window 30 | return true 31 | } 32 | 33 | func applicationWillResignActive(_ application: UIApplication) { 34 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 35 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 36 | } 37 | 38 | func applicationDidEnterBackground(_ application: UIApplication) { 39 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 40 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 41 | } 42 | 43 | func applicationWillEnterForeground(_ application: UIApplication) { 44 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 45 | } 46 | 47 | func applicationDidBecomeActive(_ application: UIApplication) { 48 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 49 | } 50 | 51 | func applicationWillTerminate(_ application: UIApplication) { 52 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 53 | } 54 | 55 | 56 | } 57 | 58 | -------------------------------------------------------------------------------- /URLNavigatorExt/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /URLNavigatorExt/Models/Repo.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Repo.swift 3 | // URLNavigatorExample 4 | // 5 | // Created by Suyeol Jeon on 7/12/16. 6 | // Copyright © 2016 Suyeol Jeon. All rights reserved. 7 | // 8 | 9 | struct Repo: Decodable { 10 | var name: String 11 | var descriptionText: String? 12 | var starCount: Int 13 | var urlString: String 14 | 15 | enum CodingKeys: String, CodingKey { 16 | case name = "name" 17 | case descriptionText = "description" 18 | case starCount = "stargazers_count" 19 | case urlString = "html_url" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /URLNavigatorExt/Models/User.swift: -------------------------------------------------------------------------------- 1 | // 2 | // User.swift 3 | // URLNavigatorExample 4 | // 5 | // Created by Suyeol Jeon on 08/11/2017. 6 | // Copyright © 2017 Suyeol Jeon. All rights reserved. 7 | // 8 | 9 | struct User { 10 | var name: String 11 | var urlString: String 12 | } 13 | -------------------------------------------------------------------------------- /URLNavigatorExt/Networking/GitHub.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GitHub.swift 3 | // URLNavigatorExample 4 | // 5 | // Created by Suyeol Jeon on 7/12/16. 6 | // Copyright © 2016 Suyeol Jeon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | enum GitHub { 12 | static func repos(username: String, completion: @escaping (Result<[Repo]>) -> Void) { 13 | HTTP.request("/users/\(username)/repos?sort=updated") { result in 14 | result 15 | .map { data -> [Repo] in 16 | let repos = try? JSONDecoder().decode([Repo].self, from: data) 17 | return repos ?? [] 18 | } 19 | .apply(completion) 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /URLNavigatorExt/Networking/HTTP.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HTTP.swift 3 | // URLNavigatorExample 4 | // 5 | // Created by Suyeol Jeon on 7/12/16. 6 | // Copyright © 2016 Suyeol Jeon. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | enum Result { 12 | case success(T) 13 | case failure(Error) 14 | 15 | var value: T? { 16 | switch self { 17 | case .success(let value): 18 | return value 19 | 20 | case .failure: 21 | return nil 22 | } 23 | } 24 | 25 | func map(_ selector: (T) throws -> R) rethrows -> Result { 26 | switch self { 27 | case let .success(value): 28 | return .success(try selector(value)) 29 | 30 | case let .failure(error): 31 | return .failure(error) 32 | } 33 | } 34 | 35 | func flatMap(_ selector: (T) throws -> Result) rethrows -> Result { 36 | switch self { 37 | case let .success(value): 38 | return try selector(value) 39 | 40 | case let .failure(error): 41 | return .failure(error) 42 | } 43 | } 44 | 45 | func apply(_ f: (Result) throws -> Void) rethrows -> Void { 46 | try f(self) 47 | } 48 | } 49 | 50 | struct HTTP { 51 | static let baseURLString: String = "https://api.github.com" 52 | 53 | /// Send a simple HTTP GET request 54 | static func request(_ urlString: String, completion: ((Result) -> Void)? = nil) { 55 | guard let url = URL(string: self.baseURLString + urlString) else { return } 56 | let task = URLSession.shared.dataTask(with: url) { data, response, error in 57 | DispatchQueue.main.async { 58 | UIApplication.shared.isNetworkActivityIndicatorVisible = false 59 | } 60 | if let error = error { 61 | DispatchQueue.main.async { completion?(.failure(error)) } 62 | } else if let data = data { 63 | DispatchQueue.main.async { completion?(.success(data)) } 64 | } 65 | } 66 | task.resume() 67 | UIApplication.shared.isNetworkActivityIndicatorVisible = true 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /URLNavigatorExt/Resources/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 25 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /URLNavigatorExt/Resources/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /URLNavigatorExt/Resources/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ios-marketing", 45 | "size" : "1024x1024", 46 | "scale" : "1x" 47 | } 48 | ], 49 | "info" : { 50 | "version" : 1, 51 | "author" : "xcode" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /URLNavigatorExt/ViewControllers/UserListViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // URLNavigatorExample 4 | // 5 | // Created by Suyeol Jeon on 7/12/16. 6 | // Copyright © 2016 Suyeol Jeon. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | import URLNavigator 12 | 13 | class UserListViewController: UIViewController { 14 | 15 | // MARK: Properties 16 | 17 | private let navigator: NavigatorProtocol 18 | let users = [ 19 | User(name: "devxoul", urlString: "\(Scheme.domain)/user/detail?name=devxoul"), 20 | User(name: "apple", urlString: "\(Scheme.domain)/user/detail?name=apple"), 21 | User(name: "google", urlString: "\(Scheme.domain)/user/detail?name=google"), 22 | User(name: "facebook", urlString: "\(Scheme.domain)/user/detail?name=facebook"), 23 | User(name: "alert", urlString: "alert?title=Hello&message=World"), 24 | User(name: "fallback", urlString: "navigator://notMatchable"), 25 | ] 26 | 27 | 28 | // MARK: UI Properties 29 | 30 | let tableView = UITableView() 31 | 32 | 33 | // MARK: Initializing 34 | 35 | init(navigator: NavigatorProtocol) { 36 | self.navigator = navigator 37 | super.init(nibName: nil, bundle: nil) 38 | self.title = "GitHub Users" 39 | } 40 | 41 | required init?(coder aDecoder: NSCoder) { 42 | fatalError("init(coder:) has not been implemented") 43 | } 44 | 45 | 46 | // MARK: View Life Cycle 47 | 48 | override func viewDidLoad() { 49 | super.viewDidLoad() 50 | self.view.addSubview(self.tableView) 51 | self.tableView.dataSource = self 52 | self.tableView.delegate = self 53 | self.tableView.register(UserCell.self, forCellReuseIdentifier: "user") 54 | } 55 | 56 | 57 | // MARK: Layout 58 | 59 | override func viewDidLayoutSubviews() { 60 | super.viewDidLayoutSubviews() 61 | self.tableView.frame = self.view.bounds 62 | } 63 | 64 | } 65 | 66 | 67 | // MARK: - UITableViewDataSource 68 | 69 | extension UserListViewController: UITableViewDataSource { 70 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 71 | return self.users.count 72 | } 73 | 74 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 75 | let cell = tableView.dequeueReusableCell(withIdentifier: "user", for: indexPath) as! UserCell 76 | let user = self.users[indexPath.row] 77 | cell.textLabel?.text = user.name 78 | cell.detailTextLabel?.text = user.urlString 79 | cell.detailTextLabel?.textColor = .gray 80 | cell.accessoryType = .disclosureIndicator 81 | return cell 82 | } 83 | } 84 | 85 | 86 | // MARK: - UITableViewDelegate 87 | 88 | extension UserListViewController: UITableViewDelegate { 89 | func tableView(_ tableView: UITableView, didSelectRowAt indexPath : IndexPath) { 90 | tableView.deselectRow(at: indexPath, animated: false) 91 | 92 | let user = self.users[indexPath.row] 93 | 94 | let alert = UIAlertController(title: "Choose an opening method", message: nil, preferredStyle: .actionSheet) 95 | let openWithURL = UIAlertAction(title: "open by url", style: .default) { (_) in 96 | let isPushed = self.navigator.push(user.urlString) != nil 97 | if isPushed { 98 | print("[Navigator] push: \(user.urlString)") 99 | } else { 100 | print("[Navigator] open: \(user.urlString)") 101 | self.navigator.open(user.urlString) 102 | } 103 | } 104 | let openWithExt = UIAlertAction(title: "open by ext", style: .destructive) { (_) in 105 | let para = Router.PRUser_Detail(name: user.name, sex: .unknow) 106 | let isPushed = self.navigator.push(Router.user_detail, context: para) != nil 107 | if isPushed { 108 | print("[Navigator] push: \(user.urlString)") 109 | } else { 110 | print("[Navigator] open: \(user.urlString)") 111 | self.navigator.open(user.urlString) 112 | } 113 | } 114 | alert.addAction(openWithURL) 115 | alert.addAction(openWithExt) 116 | self.present(alert, animated: true, completion: nil) 117 | 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /URLNavigatorExt/ViewControllers/UserViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UserViewController.swift 3 | // URLNavigatorExample 4 | // 5 | // Created by Suyeol Jeon on 7/12/16. 6 | // Copyright © 2016 Suyeol Jeon. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import URLNavigator 11 | import URLNavigatorExt 12 | 13 | enum Sex: Int { 14 | case unknow = 0 15 | case male = 1 16 | case famale = 2 17 | } 18 | 19 | // sourcery: router: desc="user detail page" 20 | // sourcery: router: name="user_detail" 21 | // sourcery: router: path="user/detail" 22 | // sourcery: router: parameter="code:Int?" 23 | // sourcery: router: parameter="name:String" 24 | // sourcery: router: parameter="sex:Sex?" 25 | final class UserViewController: UIViewController, Navigatorible { 26 | private var parameter: Router.PRUser_Detail? 27 | init(parameterible: Parameterible?) { 28 | super.init(nibName: nil, bundle: nil) 29 | self.parameter = parameterible as? Router.PRUser_Detail 30 | } 31 | 32 | 33 | // MARK: Properties 34 | var repos = [Repo]() 35 | 36 | 37 | // MARK: UI 38 | 39 | let tableView = UITableView() 40 | 41 | 42 | // MARK: Initializing 43 | 44 | init(username: String) { 45 | super.init(nibName: nil, bundle: nil) 46 | self.title = "\(username)'s Repositories" 47 | } 48 | 49 | required init?(coder aDecoder: NSCoder) { 50 | fatalError("init(coder:) has not been implemented") 51 | } 52 | 53 | 54 | // MARK: View Life Cycle 55 | 56 | override func viewDidLoad() { 57 | super.viewDidLoad() 58 | self.view.addSubview(self.tableView) 59 | 60 | self.tableView.dataSource = self 61 | self.tableView.delegate = self 62 | self.tableView.register(RepoCell.self, forCellReuseIdentifier: "cell") 63 | guard let name = self.parameter?.name else { 64 | return 65 | } 66 | GitHub.repos(username: name) { [weak self] result in 67 | guard let `self` = self else { return } 68 | self.repos = (result.value ?? []).sorted { $0.starCount > $1.starCount } 69 | self.tableView.reloadData() 70 | } 71 | } 72 | 73 | override func viewWillAppear(_ animated: Bool) { 74 | super.viewWillAppear(animated) 75 | if self.navigationController?.viewControllers.count ?? 0 > 1 { // pushed 76 | self.navigationItem.leftBarButtonItem = nil 77 | } else if self.presentingViewController != nil { // presented 78 | self.navigationItem.leftBarButtonItem = UIBarButtonItem( 79 | barButtonSystemItem: .done, 80 | target: self, 81 | action: #selector(doneButtonDidTap) 82 | ) 83 | } 84 | } 85 | 86 | 87 | // MARK: Layout 88 | 89 | override func viewDidLayoutSubviews() { 90 | super.viewDidLayoutSubviews() 91 | self.tableView.frame = self.view.bounds 92 | } 93 | 94 | 95 | // MARK: Actions 96 | 97 | @objc func doneButtonDidTap() { 98 | self.dismiss(animated: true, completion: nil) 99 | } 100 | } 101 | 102 | 103 | // MARK: - UITableViewDataSource 104 | 105 | extension UserViewController: UITableViewDataSource { 106 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 107 | return self.repos.count 108 | } 109 | 110 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 111 | let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! RepoCell 112 | let repo = self.repos[indexPath.row] 113 | cell.textLabel?.text = repo.name 114 | cell.detailTextLabel?.text = repo.descriptionText 115 | cell.detailTextLabel?.textColor = .gray 116 | cell.accessoryType = .disclosureIndicator 117 | return cell 118 | } 119 | } 120 | 121 | 122 | // MARK: - UITableViewDelegate 123 | 124 | extension UserViewController: UITableViewDelegate { 125 | func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 126 | tableView.deselectRow(at: indexPath, animated: false) 127 | let repo = self.repos[indexPath.row] 128 | let webViewController = navigator.present(repo.urlString, wrap: nil) 129 | webViewController?.title = "\(self.parameter?.name ?? "")/\(repo.name)" 130 | print("[Navigator] push: \(repo.urlString)") 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /URLNavigatorExt/Views/RepoCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RepoCell.swift 3 | // URLNavigatorExample 4 | // 5 | // Created by Suyeol Jeon on 7/12/16. 6 | // Copyright © 2016 Suyeol Jeon. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | final class RepoCell: UITableViewCell { 12 | 13 | override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { 14 | super.init(style: .subtitle, reuseIdentifier: reuseIdentifier) 15 | } 16 | 17 | required init?(coder aDecoder: NSCoder) { 18 | fatalError("init(coder:) has not been implemented") 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /URLNavigatorExt/Views/UserCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UserCell.swift 3 | // URLNavigatorExample 4 | // 5 | // Created by Suyeol Jeon on 7/13/16. 6 | // Copyright © 2016 Suyeol Jeon. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | final class UserCell: UITableViewCell { 12 | 13 | override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { 14 | super.init(style: .subtitle, reuseIdentifier: reuseIdentifier) 15 | } 16 | 17 | required init?(coder aDecoder: NSCoder) { 18 | fatalError("init(coder:) has not been implemented") 19 | } 20 | 21 | } 22 | 23 | -------------------------------------------------------------------------------- /URLNavigatorExt/generated/Router.generated.swift: -------------------------------------------------------------------------------- 1 | // Generated using Sourcery 1.6.0 — https://github.com/krzysztofzablocki/Sourcery 2 | // DO NOT EDIT 3 | // Create Time: 2022-01-15 17:01:01 4 | import URLNavigatorExt 5 | import URLNavigator 6 | 7 | // swiftlint:disable file_length 8 | // swiftlint:disable type_body_length 9 | public enum Router { 10 | static var urlPages = [String: PageConfig]() 11 | 12 | /// desc: user detail page 13 | /// view: UserViewController 14 | /// parameters: 15 | /// - sex: Sex? 16 | /// - unknow: 0 17 | /// - male: 1 18 | /// - famale: 2 19 | /// - name: String 20 | /// - code: Int? 21 | public static let user_detail = "\(Scheme.domain)/user/detail" 22 | 23 | /// MARK: parameter type dic 24 | static let urlParas: [String: Parameterible.Type?] = [ 25 | Router.user_detail: Router.PRUser_Detail.self 26 | ] 27 | 28 | /// desc: user detail page 29 | /// view: UserViewController 30 | /// path: /user/detail 31 | /// name: user_detail 32 | public struct PRUser_Detail: Parameterible { 33 | var sex: Sex? 34 | var name: String 35 | var code: Int? 36 | public fileprivate (set) var queries: [String : String] = [:] 37 | 38 | init( 39 | name: String, 40 | code: Int? = nil, 41 | sex: Sex? = nil 42 | ) { 43 | self.sex = sex 44 | if let v = sex { 45 | self.queries["sex"] = String(describing: v.rawValue) 46 | } 47 | self.name = name 48 | self.queries["name"] = String(describing: name) 49 | self.code = code 50 | if let v = code { 51 | self.queries["code"] = String(describing: v) 52 | } 53 | } 54 | 55 | public static func instance(by queryItem: [String: String]) -> Self? { 56 | let items = queryItem.map({ ($0.key.lowercased(), $0.value) }) 57 | let dict = [String: String](uniqueKeysWithValues: items) 58 | 59 | let _name: String? = dict["name"] 60 | 61 | var code: Int? 62 | if let value = dict["code"] { 63 | code = Int(value) 64 | } 65 | 66 | var sex: Sex? 67 | if let value = dict["sex"] { 68 | if let _value = Int(value), let _enum = Sex(rawValue: _value) { 69 | sex = _enum 70 | } 71 | } 72 | guard 73 | let name = _name 74 | else { return nil } 75 | return PRUser_Detail( 76 | name: name, 77 | code: code, 78 | sex: sex 79 | ) 80 | } 81 | } 82 | 83 | struct PageConfig { 84 | let desc: String 85 | let url: String 86 | let page: URL 87 | let priority: Int 88 | } 89 | 90 | static let pageConfigs = """ 91 | { 92 | "router": [ 93 | { 94 | "desc": "user detail page", 95 | "path": "/user/detail", 96 | "page": "UserViewController", 97 | "priority": 1 98 | } 99 | ] 100 | } 101 | """ 102 | 103 | static let flutterRouters = """ 104 | { 105 | "router": [ 106 | ] 107 | } 108 | """ 109 | } 110 | -------------------------------------------------------------------------------- /URLNavigatorExt/generated/RouterExtension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RouterExtension.swift 3 | // URLNavigatorExt_Example 4 | // 5 | // Created by phil on 2019/11/26. 6 | // Copyright © 2019 CocoaPods. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import URLNavigatorExt 11 | import URLNavigator 12 | import SafariServices 13 | 14 | public class Scheme { 15 | static var name: String { 16 | return String(NSStringFromClass(Scheme.self).split(separator: ".").first!) 17 | } 18 | 19 | static var domain = "navigator://www.app.com" 20 | } 21 | 22 | extension Router { 23 | public static func registOuterUrl(jsonStr: String, navigator: NavigatorProtocol) { 24 | guard let dict = try? JSONSerialization.jsonObject(with: jsonStr.data(using: .utf8)!) as? [String: Any], 25 | let router = dict?["router"] as? [Any] 26 | else { return } 27 | for kv in router { 28 | if let pairs = kv as? [String: Any], 29 | let url = pairs["path"] as? String, 30 | let page = pairs["page"] as? String { 31 | let priority = pairs["priority"] as? Int ?? 1 32 | if let preConfig = self.urlPages[url], preConfig.priority >= priority && priority < 5 { 33 | continue 34 | } 35 | let config = Router.PageConfig(desc: pairs["desc"] as? String ?? "", url: url, page: URL(string: "\(Scheme.name)://\(page)")!, priority: priority) 36 | 37 | let fullURL = Scheme.domain + url 38 | self.urlPages[url] = config 39 | let para = self.urlParas[fullURL] 40 | navigator.register(url, self.ViewControllerFactory(navigator: navigator, parameterible: para ?? nil)) 41 | navigator.register(fullURL, self.ViewControllerFactory(navigator: navigator, parameterible: para ?? nil)) 42 | } 43 | } 44 | navigator.register("http://", self.webViewControllerFactory) 45 | navigator.register("https://", self.webViewControllerFactory) 46 | } 47 | 48 | /// <#Description#> 49 | /// 50 | /// - Parameters: 51 | /// - url: <#url description#> 52 | /// - values: <#values description#> 53 | /// - context: <#context description#> 54 | /// - Returns: <#return value description#> 55 | private static func webViewControllerFactory(url: URLConvertible, values: [String: Any], context: Any? ) -> UIViewController? { 56 | guard let url = url.urlValue else { return nil } 57 | return SFSafariViewController(url: url) 58 | } 59 | 60 | /// <#Description#> 61 | /// 62 | /// - Parameters: 63 | /// - navigator: <#navigator description#> 64 | /// - parameterible: <#parameterible description#> 65 | /// - Returns: <#return value description#> 66 | private static func ViewControllerFactory(navigator: NavigatorProtocol, parameterible: Parameterible.Type?) -> ViewControllerFactory { 67 | return { url, values, context in 68 | guard let url = url.urlValue, 69 | let ori_scheme = url.scheme, 70 | let ori_host = url.host 71 | else { return nil } 72 | guard let interUrl = self.urlPages[url.path]?.page else { 73 | return nil 74 | } 75 | guard let scheme = interUrl.scheme, 76 | let host = interUrl.host else { 77 | print("\n//=============URLNavigatorExt=======================") 78 | print("|URLNavigatorExt: Error: schme and host can't be nil, pz check your scheme, 不能包含特殊符号|") 79 | print("//<<<<<<<<<<<<<< String { 13 | var temp = self 14 | return "\(String(temp.first!).capitalized)\(String(temp.lowercased().dropFirst()))" 15 | } 16 | 17 | func structSuffix() -> String { 18 | var temp = self.split(separator: "_") 19 | return temp.map{ "\($0)".capitaliz() }.joined(separator: "_") 20 | } 21 | } 22 | 23 | /// 支持返回序列化的自定义参数类型 24 | var type_dict = types.all.reduce([String: Type]()) { dict, type -> [String: Type] in 25 | var dict = dict 26 | dict[type.name] = type 27 | return dict 28 | } 29 | 30 | struct Parameter { 31 | let name: String 32 | let type: String 33 | let isOpt: Bool 34 | let isArray: Bool 35 | let unSupport: Bool 36 | let defaultValue: String? 37 | 38 | /// return: type or [type] or [type]? or type? 39 | var typeFormat: String { 40 | var type = self.type 41 | if self.isArray { 42 | type = "[\(type)]" 43 | } 44 | if self.isOpt { 45 | type = "\(type)?" 46 | } 47 | return type 48 | } 49 | 50 | static func decode(by parameter: Any) -> [Parameter] { 51 | let defaultTypes = ["String", "Int", "Int32", "Int64", 52 | "Int8", "Float", "Double", "Bool", "Boolean"] 53 | guard let queryItems = parameter as? NSArray else { 54 | if let query = parameter as? String { 55 | let items = query.split(separator: ":") 56 | if items.count < 2 { return [] } 57 | let varName = String(items[0]).trimmingCharacters(in: .whitespacesAndNewlines) 58 | var varType = String(items[1]).trimmingCharacters(in: .whitespacesAndNewlines) 59 | var varIsOption = self.isOptional(for: varType) 60 | let varIsArray = self.isArray(for: varType) 61 | var varUnSupport = self.unSupport(for: varType) 62 | if varIsOption && varType.hasSuffix("?") { 63 | varType = "\(varType.dropLast())" 64 | } 65 | if varIsArray { 66 | varType = "\(varType.dropFirst().dropLast())" 67 | } 68 | if varIsArray, !(type_dict[varType]?.inheritedTypes.contains("\(typeible)") ?? false) { 69 | varUnSupport = true 70 | } else if type_dict[varType]?.kind != "enum" && !defaultTypes.contains(varType) { 71 | varUnSupport = true 72 | } 73 | if varUnSupport { 74 | varIsOption = true 75 | } 76 | let defaultValue: String? = items.count >= 3 ? String(items[2]) : nil 77 | return [Parameter(name: varName, type: varType, isOpt: varIsOption, isArray: varIsArray, unSupport: varUnSupport, defaultValue: defaultValue)] 78 | } 79 | return [] 80 | } 81 | return queryItems.flatMap{ self.decode(by: $0) } 82 | } 83 | 84 | static func isOptional(for type: String) -> Bool { 85 | return type.hasSuffix("?") 86 | } 87 | 88 | static func isArray(for type: String) -> Bool { 89 | return type.hasPrefix("[") && (type.hasSuffix("]") || type.hasSuffix("]?")) 90 | } 91 | 92 | /// 回调函数 ()-> 93 | /// 查找 ^[^(|)|-|>|/]+$ 不能包含()->, 94 | /// 如果数量为0,则为回调函数,不支持,否则不是回调函数类型,支持 95 | static func unSupport(for type: String) -> Bool { 96 | let checkReg = try! NSRegularExpression(pattern: #"^[^(|)|-|>|/]+$"#, options: NSRegularExpression.Options.caseInsensitive) 97 | let checked = checkReg.matches(in: type, options: .reportCompletion, range: NSRange.init(location: 0, length: type.count)) 98 | return (checked.count == 0) 99 | } 100 | } 101 | 102 | struct Router { 103 | let desc: String 104 | let name: String 105 | let path: String 106 | let view: String 107 | let module: String 108 | let parameters: [Parameter] 109 | 110 | static func decode(by annotation: NSObject, view: String) -> Router { 111 | let desc = annotation.value(forKey: "desc") as? String ?? "" 112 | let name = annotation.value(forKey: "name") as? String ?? "unknow" 113 | var path = annotation.value(forKey: "path") as? String ?? "unknow" 114 | var module = annotation.value(forKey: "module") as? String ?? "native" 115 | if !path.hasPrefix("/") { 116 | path = "/\(path)" 117 | } 118 | let parameter = annotation.value(forKey: "parameter") ?? nil 119 | return Router(desc: desc, name: name, path: path, view: view, module: module, parameters: parameter == nil ? [] : Parameter.decode(by: parameter)) 120 | } 121 | } 122 | 123 | // new 124 | var routers = [Router]() 125 | 126 | /// 构建router 127 | for type in types.all where type.inheritedTypes.contains("\(routeible)") { 128 | if let annotation = type.annotations["router"], 129 | annotation.value(forKey: "name") != nil, 130 | annotation.value(forKey: "path") != nil { 131 | let router = Router.decode(by: annotation, view: type.name) 132 | routers.append(router) 133 | } 134 | } 135 | 136 | let dateFormatter = DateFormatter.init() 137 | dateFormatter.timeZone = TimeZone(identifier: "Asia/Shanghai") 138 | dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss" 139 | print("// Create Time:", dateFormatter.string(from: Date())) 140 | -%> 141 | import URLNavigatorExt 142 | import URLNavigator 143 | 144 | // swiftlint:disable file_length 145 | // swiftlint:disable type_body_length 146 | public enum Router { 147 | static var urlPages = [String: PageConfig]() 148 | 149 | <%_# generate path -%> 150 | <%_ for router in routers { -%> 151 | /// desc: <%= router.desc %> 152 | /// view: <%= router.view %> 153 | /// parameters: 154 | <%_ if router.parameters.isEmpty { -%> 155 | /// - None 156 | <%_ } else { 157 | for parameter in router.parameters { -%> 158 | /// - <%= parameter.name %>: <%= parameter.typeFormat %> <%= parameter.defaultValue == nil ? "" : "default: \(parameter.defaultValue!)" %> 159 | <%_ if let type = type_dict[parameter.type], 160 | type.kind == "enum", 161 | let cases = type.value(forKey: "cases") as? [EnumCase] { 162 | var rawValue: Int = 0 163 | for _case in cases { 164 | rawValue = Int(_case.rawValue ?? "") ?? rawValue 165 | -%> 166 | /// - <%= _case.name %>: <%= _case.rawValue ?? "\(rawValue)" %> 167 | <%_ 168 | rawValue += 1 169 | } 170 | } -%> 171 | <%_ } 172 | }-%> 173 | public static let <%= router.name %> = "<%= domain %><%= router.path %>" 174 | 175 | <%_ } -%> 176 | <%_# generate parameter types -%> 177 | /// MARK: parameter type dic 178 | static let urlParas: [String: <%= parameterible %>.Type?] = [ 179 | <%_ if routers.isEmpty { -%> 180 | : 181 | <%_ } else { -%> 182 | <%_ loopIndex = 0; 183 | for router in routers { 184 | loopIndex += 1 -%> 185 | Router.<%= router.name %>: <%= router.parameters.isEmpty ? "nil" : "Router.PR\(router.name.structSuffix()).self" %><%= loopIndex == routers.count ? "" : "," %> 186 | <%_ } -%> 187 | <%_ } -%> 188 | ] 189 | 190 | <%_# generate page parameter -%> 191 | <%_ for router in routers where !router.parameters.isEmpty { -%> 192 | /// desc: <%= router.desc %> 193 | /// view: <%= router.view %> 194 | /// path: <%= router.path %> 195 | /// name: <%= router.name %> 196 | public struct PR<%= router.name.structSuffix() %>: <%= parameterible %> { 197 | <%_ for parameter in router.parameters { -%> 198 | var <%= parameter.name %>: <%= parameter.typeFormat %> 199 | <%_ } -%> 200 | public fileprivate (set) var queries: [String : String] = [:] 201 | 202 | init( 203 | <%_ 204 | let requiredList = Array(router.parameters.filter{ !$0.isOpt }.reversed()) 205 | let optionalList = Array(router.parameters.filter{ $0.isOpt }.reversed()) 206 | let sortedParameters = requiredList + optionalList 207 | loopIndex = 0 208 | for parameter in sortedParameters { -%> 209 | <%= parameter.name %>: <%= parameter.typeFormat -%><%= parameter.isOpt ? " = nil" : "" %> <%_ 210 | loopIndex += 1 211 | if loopIndex < sortedParameters.count { 212 | print(",") 213 | } 214 | -%> 215 | <%_ } %> 216 | ) { 217 | <%_ for parameter in router.parameters { -%> 218 | self.<%= parameter.name %> = <%= parameter.name %> 219 | <%_# 构建必选参数 -%> 220 | <%_ if ["Int", "Int8", "Int32", "Int16", "Int64", "Double", "Float", "String"].contains(parameter.typeFormat) { -%> 221 | self.queries["<%= parameter.name %>"] = String(describing: <%= parameter.name %>) 222 | <%_ } -%> 223 | <%_# end if -%> 224 | <%_# 构建可选参数 -%> 225 | <%_ if ["Int", "Int8", "Int32", "Int16", "Int64", "Double", "Float", "String"].map{ $0 + "?"}.contains(parameter.typeFormat) { -%> 226 | if let v = <%= parameter.name %> { 227 | self.queries["<%= parameter.name %>"] = String(describing: v) 228 | } 229 | <%_ } -%> 230 | <%_# end if -%> 231 | <%_# 构建枚举参数 -%> 232 | <%_ if let type = type_dict[parameter.type], type.kind == "enum" { -%> 233 | <%_# 枚举参数必须是String或Int类型 -%> 234 | <%_ if let rawTypeName = type.value(forKey: "rawTypeName"), ("\(rawTypeName)" == "String" || "\(rawTypeName)" == "Int") { -%> 235 | <%_ if parameter.isOpt {-%> 236 | if let v = <%= parameter.name %> { 237 | self.queries["<%= parameter.name %>"] = String(describing: v.rawValue) 238 | } 239 | <%_ } else { -%> 240 | self.queries["<%= parameter.name %>"] = String(describing: <%= parameter.name %>.rawValue) 241 | <%_ } -%> 242 | <%_ } -%> 243 | <%_ } -%> 244 | <%_# end if -%> 245 | <%_ } -%> 246 | <%_# end for -%> 247 | } 248 | 249 | public static func instance(by queryItem: [String: String]) -> Self? { 250 | <%_ if !(sortedParameters.filter{ !$0.unSupport }.isEmpty) { -%> 251 | let items = queryItem.map({ ($0.key.lowercased(), $0.value) }) 252 | let dict = [String: String](uniqueKeysWithValues: items) 253 | <%_ } -%> 254 | <%_ for (i, parameter) in sortedParameters.enumerated() where parameter.unSupport == false { 255 | let varName = i < requiredList.count ? "_\(parameter.name)" : "\(parameter.name)" -%> 256 | 257 | <%_# 构建默认参数 -%> 258 | <%_ if let value = parameter.defaultValue { -%> 259 | var <%= varName %>: <%= parameter.typeFormat %><%= parameter.isOpt ? "" : "?" %> = <%= parameter.type == "String" ? "\"\(value)\"" : value %> 260 | <%_ } else if parameter.type == "String" { -%> 261 | <%_# // do: fixed: error: Segmentation fault: 11 -%> 262 | let <%= varName %>: <%= parameter.typeFormat %><%= parameter.isOpt ? "" : "?" %> = dict["<%= parameter.name.lowercased() %>"] 263 | <%_ continue; } else { -%> 264 | var <%= varName %>: <%= parameter.typeFormat %><%= parameter.isOpt ? "" : "?" %> 265 | <%_ } -%> 266 | <%_ if parameter.isArray && !(type_dict[parameter.type]?.inheritedTypes.contains("\(typeible)") ?? false) { continue } -%> 267 | if let value = dict["<%= parameter.name.lowercased() %>"] { 268 | <%_# begin if type.kind == "enum" -%> 269 | <%_ if let type = type_dict[parameter.type], type.kind == "enum" { -%> 270 | <%_ if "\(type.value(forKey: "rawTypeName") ?? "")" == "String" { -%> 271 | if let _enum = <%= parameter.type %>(rawValue: value) { 272 | <%= varName %> = _enum 273 | } 274 | <%_ } else { -%> 275 | if let _value = Int(value), let _enum = <%= parameter.type %>(rawValue: _value) { 276 | <%= varName %> = _enum 277 | } 278 | <%_ } -%> 279 | <%_ } else if let type = type_dict[parameter.type], type.inheritedTypes.contains("\(typeible)") { -%> 280 | <%_# // do: check parameter type is $typeible -%> 281 | <%_ if parameter.isArray { -%> 282 | <%= varName %> = <%= parameter.type %>.list(from: value) 283 | <%_ } else { -%> 284 | <%= varName %> = <%= parameter.type %>.instance(from: value) 285 | <%_}-%> 286 | <%_ } else if parameter.type == "String" { -%> 287 | <%_# // do: check parameter type is String -%> 288 | <%= varName %> = value 289 | <%_ } else { -%> 290 | <%_# // do: check required type is other $Type($value) -%> 291 | <%= varName %> = <%= parameter.type %>(value) 292 | <%_ } -%> 293 | } 294 | <%_} -%> 295 | <%_# // do: check required parameter -%> 296 | <%_ if !requiredList.isEmpty { -%> 297 | guard 298 | <%_ loopIndex = 0; for parameter in requiredList { loopIndex += 1 -%> 299 | let <%= parameter.name %> = _<%= parameter.name %><%= loopIndex == requiredList.count ? "" : "," %> 300 | <%_ } -%> 301 | else { return nil } 302 | <%_# // return Self() -%> 303 | <%_ } -%> 304 | return PR<%= router.name.structSuffix() %>( 305 | <%_ loopIndex = 0; 306 | for parameter in sortedParameters { 307 | loopIndex += 1 -%> 308 | <%= parameter.name %>: <%= parameter.unSupport ? "nil" : parameter.name %><%= loopIndex == sortedParameters.count ? "" : "," %> 309 | <%_ } -%> 310 | ) 311 | } 312 | } 313 | 314 | <%_ } -%> 315 | struct PageConfig { 316 | let desc: String 317 | let url: String 318 | let page: URL 319 | let priority: Int 320 | } 321 | 322 | static let pageConfigs = """ 323 | { 324 | "router": [ 325 | <%_ loopIndex = 0; 326 | for router in routers where router.module == "native" { 327 | loopIndex += 1 -%> 328 | { 329 | "desc": "<%= router.desc %>", 330 | "path": "<%= router.path %>", 331 | "page": "<%= router.view %>", 332 | "priority": 1 333 | }<%= loopIndex == routers.count ? "" : "," %> 334 | <%_ }-%> 335 | ] 336 | } 337 | """ 338 | 339 | static let flutterRouters = """ 340 | { 341 | "router": [ 342 | <%_ loopIndex = 0; 343 | for router in routers where router.module == "flutter" { 344 | loopIndex += 1 -%> 345 | { 346 | "desc": "<%= router.desc %>", 347 | "path": "<%= router.path %>", 348 | "page": "<%= router.view %>", 349 | "priority": 2 350 | }<%= loopIndex == routers.count ? "" : "," %> 351 | <%_ }-%> 352 | ] 353 | } 354 | """ 355 | } 356 | --------------------------------------------------------------------------------