├── .gitignore ├── Cartfile ├── Cartfile.resolved ├── README.md ├── Router.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata ├── Router ├── AppDelegate.swift ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ ├── Contents.json │ └── DianQK.imageset │ │ ├── Contents.json │ │ └── DianQK@400.png ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── Config.swift ├── HUD.swift ├── Info.plist ├── RootViewController.swift └── ViewControllers │ ├── LoginViewController.swift │ ├── ProfileViewController.swift │ ├── SearchViewController.swift │ └── TimelineViewController.swift └── Source ├── Router.swift └── Routerable.swift /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xcuserstate 23 | 24 | ## Obj-C/Swift specific 25 | *.hmap 26 | *.ipa 27 | *.dSYM.zip 28 | *.dSYM 29 | 30 | ## Playgrounds 31 | timeline.xctimeline 32 | playground.xcworkspace 33 | 34 | # Swift Package Manager 35 | # 36 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 37 | # Packages/ 38 | .build/ 39 | 40 | # CocoaPods 41 | # 42 | # We recommend against adding the Pods directory to your .gitignore. However 43 | # you should judge for yourself, the pros and cons are mentioned at: 44 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 45 | # 46 | Pods/ 47 | 48 | # Carthage 49 | # 50 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 51 | Carthage/Checkouts 52 | 53 | Carthage/Build 54 | 55 | # fastlane 56 | # 57 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 58 | # screenshots whenever they are needed. 59 | # For more information about the recommended setup visit: 60 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md 61 | 62 | fastlane/report.xml 63 | fastlane/Preview.html 64 | fastlane/screenshots 65 | fastlane/test_output 66 | -------------------------------------------------------------------------------- /Cartfile: -------------------------------------------------------------------------------- 1 | github "jasl/RouterX" 2 | github "SwiftyJSON/SwiftyJSON" "master" 3 | github "jdg/MBProgressHUD" ~> 0.9.2 4 | -------------------------------------------------------------------------------- /Cartfile.resolved: -------------------------------------------------------------------------------- 1 | github "jdg/MBProgressHUD" "0.9.2" 2 | github "jasl/RouterX" "0.0.4" 3 | github "SwiftyJSON/SwiftyJSON" "73670b94292b8d8496c398a973f56ac67789ba9f" 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Router -------------------------------------------------------------------------------- /Router.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 267277CB1D5EE04900714A79 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 267277CA1D5EE04900714A79 /* AppDelegate.swift */; }; 11 | 267277D01D5EE04900714A79 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 267277CE1D5EE04900714A79 /* Main.storyboard */; }; 12 | 267277D21D5EE04900714A79 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 267277D11D5EE04900714A79 /* Assets.xcassets */; }; 13 | 267277D51D5EE04900714A79 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 267277D31D5EE04900714A79 /* LaunchScreen.storyboard */; }; 14 | 267277DD1D5EE1FD00714A79 /* RouterX.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 267277DC1D5EE1FD00714A79 /* RouterX.framework */; }; 15 | 267277E01D5EE7B800714A79 /* SwiftyJSON.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 267277DF1D5EE7B800714A79 /* SwiftyJSON.framework */; }; 16 | 267277E41D5EE7F400714A79 /* Router.swift in Sources */ = {isa = PBXBuildFile; fileRef = 267277E21D5EE7F400714A79 /* Router.swift */; }; 17 | 267277E51D5EE7F400714A79 /* Routerable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 267277E31D5EE7F400714A79 /* Routerable.swift */; }; 18 | 26AA78C41D5EE9E60004E583 /* SearchViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26AA78C31D5EE9E60004E583 /* SearchViewController.swift */; }; 19 | 26AA78C61D5EEC3F0004E583 /* Config.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26AA78C51D5EEC3F0004E583 /* Config.swift */; }; 20 | 26AA78C81D5EEFC10004E583 /* RootViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26AA78C71D5EEFC10004E583 /* RootViewController.swift */; }; 21 | 26AA78E51D5EFAD90004E583 /* ProfileViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26AA78E41D5EFAD90004E583 /* ProfileViewController.swift */; }; 22 | 26AA78E71D5EFFF00004E583 /* MBProgressHUD.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 26AA78E61D5EFFF00004E583 /* MBProgressHUD.framework */; }; 23 | 26AA78E91D5F00220004E583 /* HUD.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26AA78E81D5F00220004E583 /* HUD.swift */; }; 24 | 26AA78EB1D5F01910004E583 /* TimelineViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26AA78EA1D5F01910004E583 /* TimelineViewController.swift */; }; 25 | 26AA78ED1D5F03550004E583 /* LoginViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26AA78EC1D5F03550004E583 /* LoginViewController.swift */; }; 26 | /* End PBXBuildFile section */ 27 | 28 | /* Begin PBXFileReference section */ 29 | 267277C71D5EE04900714A79 /* Router.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Router.app; sourceTree = BUILT_PRODUCTS_DIR; }; 30 | 267277CA1D5EE04900714A79 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 31 | 267277CF1D5EE04900714A79 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 32 | 267277D11D5EE04900714A79 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 33 | 267277D41D5EE04900714A79 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 34 | 267277D61D5EE04900714A79 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 35 | 267277DC1D5EE1FD00714A79 /* RouterX.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RouterX.framework; path = Carthage/Build/iOS/RouterX.framework; sourceTree = ""; }; 36 | 267277DF1D5EE7B800714A79 /* SwiftyJSON.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftyJSON.framework; path = Carthage/Build/iOS/SwiftyJSON.framework; sourceTree = ""; }; 37 | 267277E21D5EE7F400714A79 /* Router.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Router.swift; sourceTree = ""; }; 38 | 267277E31D5EE7F400714A79 /* Routerable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Routerable.swift; sourceTree = ""; }; 39 | 26AA78C31D5EE9E60004E583 /* SearchViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SearchViewController.swift; sourceTree = ""; }; 40 | 26AA78C51D5EEC3F0004E583 /* Config.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Config.swift; sourceTree = ""; }; 41 | 26AA78C71D5EEFC10004E583 /* RootViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RootViewController.swift; sourceTree = ""; }; 42 | 26AA78E41D5EFAD90004E583 /* ProfileViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProfileViewController.swift; sourceTree = ""; }; 43 | 26AA78E61D5EFFF00004E583 /* MBProgressHUD.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MBProgressHUD.framework; path = Carthage/Build/iOS/MBProgressHUD.framework; sourceTree = ""; }; 44 | 26AA78E81D5F00220004E583 /* HUD.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HUD.swift; sourceTree = ""; }; 45 | 26AA78EA1D5F01910004E583 /* TimelineViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TimelineViewController.swift; sourceTree = ""; }; 46 | 26AA78EC1D5F03550004E583 /* LoginViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoginViewController.swift; sourceTree = ""; }; 47 | /* End PBXFileReference section */ 48 | 49 | /* Begin PBXFrameworksBuildPhase section */ 50 | 267277C41D5EE04900714A79 /* Frameworks */ = { 51 | isa = PBXFrameworksBuildPhase; 52 | buildActionMask = 2147483647; 53 | files = ( 54 | 26AA78E71D5EFFF00004E583 /* MBProgressHUD.framework in Frameworks */, 55 | 267277E01D5EE7B800714A79 /* SwiftyJSON.framework in Frameworks */, 56 | 267277DD1D5EE1FD00714A79 /* RouterX.framework in Frameworks */, 57 | ); 58 | runOnlyForDeploymentPostprocessing = 0; 59 | }; 60 | /* End PBXFrameworksBuildPhase section */ 61 | 62 | /* Begin PBXGroup section */ 63 | 267277BE1D5EE04900714A79 = { 64 | isa = PBXGroup; 65 | children = ( 66 | 267277E11D5EE7E000714A79 /* Source */, 67 | 267277DF1D5EE7B800714A79 /* SwiftyJSON.framework */, 68 | 26AA78E61D5EFFF00004E583 /* MBProgressHUD.framework */, 69 | 267277DC1D5EE1FD00714A79 /* RouterX.framework */, 70 | 267277C91D5EE04900714A79 /* Router */, 71 | 267277C81D5EE04900714A79 /* Products */, 72 | ); 73 | sourceTree = ""; 74 | }; 75 | 267277C81D5EE04900714A79 /* Products */ = { 76 | isa = PBXGroup; 77 | children = ( 78 | 267277C71D5EE04900714A79 /* Router.app */, 79 | ); 80 | name = Products; 81 | sourceTree = ""; 82 | }; 83 | 267277C91D5EE04900714A79 /* Router */ = { 84 | isa = PBXGroup; 85 | children = ( 86 | 26AA78C21D5EE9D10004E583 /* ViewControllers */, 87 | 267277CA1D5EE04900714A79 /* AppDelegate.swift */, 88 | 26AA78C71D5EEFC10004E583 /* RootViewController.swift */, 89 | 267277CE1D5EE04900714A79 /* Main.storyboard */, 90 | 267277D11D5EE04900714A79 /* Assets.xcassets */, 91 | 267277D31D5EE04900714A79 /* LaunchScreen.storyboard */, 92 | 267277D61D5EE04900714A79 /* Info.plist */, 93 | 26AA78C51D5EEC3F0004E583 /* Config.swift */, 94 | 26AA78E81D5F00220004E583 /* HUD.swift */, 95 | ); 96 | path = Router; 97 | sourceTree = ""; 98 | }; 99 | 267277E11D5EE7E000714A79 /* Source */ = { 100 | isa = PBXGroup; 101 | children = ( 102 | 267277E21D5EE7F400714A79 /* Router.swift */, 103 | 267277E31D5EE7F400714A79 /* Routerable.swift */, 104 | ); 105 | path = Source; 106 | sourceTree = ""; 107 | }; 108 | 26AA78C21D5EE9D10004E583 /* ViewControllers */ = { 109 | isa = PBXGroup; 110 | children = ( 111 | 26AA78C31D5EE9E60004E583 /* SearchViewController.swift */, 112 | 26AA78E41D5EFAD90004E583 /* ProfileViewController.swift */, 113 | 26AA78EA1D5F01910004E583 /* TimelineViewController.swift */, 114 | 26AA78EC1D5F03550004E583 /* LoginViewController.swift */, 115 | ); 116 | path = ViewControllers; 117 | sourceTree = ""; 118 | }; 119 | /* End PBXGroup section */ 120 | 121 | /* Begin PBXNativeTarget section */ 122 | 267277C61D5EE04900714A79 /* Router */ = { 123 | isa = PBXNativeTarget; 124 | buildConfigurationList = 267277D91D5EE04900714A79 /* Build configuration list for PBXNativeTarget "Router" */; 125 | buildPhases = ( 126 | 267277C31D5EE04900714A79 /* Sources */, 127 | 267277C41D5EE04900714A79 /* Frameworks */, 128 | 267277C51D5EE04900714A79 /* Resources */, 129 | 267277DE1D5EE20100714A79 /* ShellScript */, 130 | ); 131 | buildRules = ( 132 | ); 133 | dependencies = ( 134 | ); 135 | name = Router; 136 | productName = Router; 137 | productReference = 267277C71D5EE04900714A79 /* Router.app */; 138 | productType = "com.apple.product-type.application"; 139 | }; 140 | /* End PBXNativeTarget section */ 141 | 142 | /* Begin PBXProject section */ 143 | 267277BF1D5EE04900714A79 /* Project object */ = { 144 | isa = PBXProject; 145 | attributes = { 146 | LastSwiftUpdateCheck = 0730; 147 | LastUpgradeCheck = 0730; 148 | ORGANIZATIONNAME = T; 149 | TargetAttributes = { 150 | 267277C61D5EE04900714A79 = { 151 | CreatedOnToolsVersion = 7.3.1; 152 | }; 153 | }; 154 | }; 155 | buildConfigurationList = 267277C21D5EE04900714A79 /* Build configuration list for PBXProject "Router" */; 156 | compatibilityVersion = "Xcode 3.2"; 157 | developmentRegion = English; 158 | hasScannedForEncodings = 0; 159 | knownRegions = ( 160 | en, 161 | Base, 162 | ); 163 | mainGroup = 267277BE1D5EE04900714A79; 164 | productRefGroup = 267277C81D5EE04900714A79 /* Products */; 165 | projectDirPath = ""; 166 | projectRoot = ""; 167 | targets = ( 168 | 267277C61D5EE04900714A79 /* Router */, 169 | ); 170 | }; 171 | /* End PBXProject section */ 172 | 173 | /* Begin PBXResourcesBuildPhase section */ 174 | 267277C51D5EE04900714A79 /* Resources */ = { 175 | isa = PBXResourcesBuildPhase; 176 | buildActionMask = 2147483647; 177 | files = ( 178 | 267277D51D5EE04900714A79 /* LaunchScreen.storyboard in Resources */, 179 | 267277D21D5EE04900714A79 /* Assets.xcassets in Resources */, 180 | 267277D01D5EE04900714A79 /* Main.storyboard in Resources */, 181 | ); 182 | runOnlyForDeploymentPostprocessing = 0; 183 | }; 184 | /* End PBXResourcesBuildPhase section */ 185 | 186 | /* Begin PBXShellScriptBuildPhase section */ 187 | 267277DE1D5EE20100714A79 /* ShellScript */ = { 188 | isa = PBXShellScriptBuildPhase; 189 | buildActionMask = 2147483647; 190 | files = ( 191 | ); 192 | inputPaths = ( 193 | "$(SRCROOT)/Carthage/Build/iOS/RouterX.framework", 194 | "$(SRCROOT)/Carthage/Build/iOS/SwiftyJSON.framework", 195 | "$(SRCROOT)/Carthage/Build/iOS/MBProgressHUD.framework", 196 | ); 197 | outputPaths = ( 198 | ); 199 | runOnlyForDeploymentPostprocessing = 0; 200 | shellPath = /bin/sh; 201 | shellScript = "/usr/local/bin/carthage copy-frameworks"; 202 | }; 203 | /* End PBXShellScriptBuildPhase section */ 204 | 205 | /* Begin PBXSourcesBuildPhase section */ 206 | 267277C31D5EE04900714A79 /* Sources */ = { 207 | isa = PBXSourcesBuildPhase; 208 | buildActionMask = 2147483647; 209 | files = ( 210 | 26AA78C41D5EE9E60004E583 /* SearchViewController.swift in Sources */, 211 | 267277E51D5EE7F400714A79 /* Routerable.swift in Sources */, 212 | 26AA78C81D5EEFC10004E583 /* RootViewController.swift in Sources */, 213 | 267277E41D5EE7F400714A79 /* Router.swift in Sources */, 214 | 26AA78E51D5EFAD90004E583 /* ProfileViewController.swift in Sources */, 215 | 26AA78E91D5F00220004E583 /* HUD.swift in Sources */, 216 | 267277CB1D5EE04900714A79 /* AppDelegate.swift in Sources */, 217 | 26AA78C61D5EEC3F0004E583 /* Config.swift in Sources */, 218 | 26AA78ED1D5F03550004E583 /* LoginViewController.swift in Sources */, 219 | 26AA78EB1D5F01910004E583 /* TimelineViewController.swift in Sources */, 220 | ); 221 | runOnlyForDeploymentPostprocessing = 0; 222 | }; 223 | /* End PBXSourcesBuildPhase section */ 224 | 225 | /* Begin PBXVariantGroup section */ 226 | 267277CE1D5EE04900714A79 /* Main.storyboard */ = { 227 | isa = PBXVariantGroup; 228 | children = ( 229 | 267277CF1D5EE04900714A79 /* Base */, 230 | ); 231 | name = Main.storyboard; 232 | sourceTree = ""; 233 | }; 234 | 267277D31D5EE04900714A79 /* LaunchScreen.storyboard */ = { 235 | isa = PBXVariantGroup; 236 | children = ( 237 | 267277D41D5EE04900714A79 /* Base */, 238 | ); 239 | name = LaunchScreen.storyboard; 240 | sourceTree = ""; 241 | }; 242 | /* End PBXVariantGroup section */ 243 | 244 | /* Begin XCBuildConfiguration section */ 245 | 267277D71D5EE04900714A79 /* Debug */ = { 246 | isa = XCBuildConfiguration; 247 | buildSettings = { 248 | ALWAYS_SEARCH_USER_PATHS = NO; 249 | CLANG_ANALYZER_NONNULL = YES; 250 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 251 | CLANG_CXX_LIBRARY = "libc++"; 252 | CLANG_ENABLE_MODULES = YES; 253 | CLANG_ENABLE_OBJC_ARC = YES; 254 | CLANG_WARN_BOOL_CONVERSION = YES; 255 | CLANG_WARN_CONSTANT_CONVERSION = YES; 256 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 257 | CLANG_WARN_EMPTY_BODY = YES; 258 | CLANG_WARN_ENUM_CONVERSION = YES; 259 | CLANG_WARN_INT_CONVERSION = YES; 260 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 261 | CLANG_WARN_UNREACHABLE_CODE = YES; 262 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 263 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 264 | COPY_PHASE_STRIP = NO; 265 | DEBUG_INFORMATION_FORMAT = dwarf; 266 | ENABLE_STRICT_OBJC_MSGSEND = YES; 267 | ENABLE_TESTABILITY = YES; 268 | GCC_C_LANGUAGE_STANDARD = gnu99; 269 | GCC_DYNAMIC_NO_PIC = NO; 270 | GCC_NO_COMMON_BLOCKS = YES; 271 | GCC_OPTIMIZATION_LEVEL = 0; 272 | GCC_PREPROCESSOR_DEFINITIONS = ( 273 | "DEBUG=1", 274 | "$(inherited)", 275 | ); 276 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 277 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 278 | GCC_WARN_UNDECLARED_SELECTOR = YES; 279 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 280 | GCC_WARN_UNUSED_FUNCTION = YES; 281 | GCC_WARN_UNUSED_VARIABLE = YES; 282 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 283 | MTL_ENABLE_DEBUG_INFO = YES; 284 | ONLY_ACTIVE_ARCH = YES; 285 | SDKROOT = iphoneos; 286 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 287 | TARGETED_DEVICE_FAMILY = "1,2"; 288 | }; 289 | name = Debug; 290 | }; 291 | 267277D81D5EE04900714A79 /* Release */ = { 292 | isa = XCBuildConfiguration; 293 | buildSettings = { 294 | ALWAYS_SEARCH_USER_PATHS = NO; 295 | CLANG_ANALYZER_NONNULL = YES; 296 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 297 | CLANG_CXX_LIBRARY = "libc++"; 298 | CLANG_ENABLE_MODULES = YES; 299 | CLANG_ENABLE_OBJC_ARC = YES; 300 | CLANG_WARN_BOOL_CONVERSION = YES; 301 | CLANG_WARN_CONSTANT_CONVERSION = YES; 302 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 303 | CLANG_WARN_EMPTY_BODY = YES; 304 | CLANG_WARN_ENUM_CONVERSION = YES; 305 | CLANG_WARN_INT_CONVERSION = YES; 306 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 307 | CLANG_WARN_UNREACHABLE_CODE = YES; 308 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 309 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 310 | COPY_PHASE_STRIP = NO; 311 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 312 | ENABLE_NS_ASSERTIONS = NO; 313 | ENABLE_STRICT_OBJC_MSGSEND = YES; 314 | GCC_C_LANGUAGE_STANDARD = gnu99; 315 | GCC_NO_COMMON_BLOCKS = YES; 316 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 317 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 318 | GCC_WARN_UNDECLARED_SELECTOR = YES; 319 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 320 | GCC_WARN_UNUSED_FUNCTION = YES; 321 | GCC_WARN_UNUSED_VARIABLE = YES; 322 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 323 | MTL_ENABLE_DEBUG_INFO = NO; 324 | SDKROOT = iphoneos; 325 | TARGETED_DEVICE_FAMILY = "1,2"; 326 | VALIDATE_PRODUCT = YES; 327 | }; 328 | name = Release; 329 | }; 330 | 267277DA1D5EE04900714A79 /* Debug */ = { 331 | isa = XCBuildConfiguration; 332 | buildSettings = { 333 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 334 | FRAMEWORK_SEARCH_PATHS = ( 335 | "$(inherited)", 336 | "$(PROJECT_DIR)/Carthage/Build/iOS", 337 | ); 338 | INFOPLIST_FILE = Router/Info.plist; 339 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 340 | PRODUCT_BUNDLE_IDENTIFIER = gg.swift.t.Router; 341 | PRODUCT_NAME = "$(TARGET_NAME)"; 342 | }; 343 | name = Debug; 344 | }; 345 | 267277DB1D5EE04900714A79 /* Release */ = { 346 | isa = XCBuildConfiguration; 347 | buildSettings = { 348 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 349 | FRAMEWORK_SEARCH_PATHS = ( 350 | "$(inherited)", 351 | "$(PROJECT_DIR)/Carthage/Build/iOS", 352 | ); 353 | INFOPLIST_FILE = Router/Info.plist; 354 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 355 | PRODUCT_BUNDLE_IDENTIFIER = gg.swift.t.Router; 356 | PRODUCT_NAME = "$(TARGET_NAME)"; 357 | }; 358 | name = Release; 359 | }; 360 | /* End XCBuildConfiguration section */ 361 | 362 | /* Begin XCConfigurationList section */ 363 | 267277C21D5EE04900714A79 /* Build configuration list for PBXProject "Router" */ = { 364 | isa = XCConfigurationList; 365 | buildConfigurations = ( 366 | 267277D71D5EE04900714A79 /* Debug */, 367 | 267277D81D5EE04900714A79 /* Release */, 368 | ); 369 | defaultConfigurationIsVisible = 0; 370 | defaultConfigurationName = Release; 371 | }; 372 | 267277D91D5EE04900714A79 /* Build configuration list for PBXNativeTarget "Router" */ = { 373 | isa = XCConfigurationList; 374 | buildConfigurations = ( 375 | 267277DA1D5EE04900714A79 /* Debug */, 376 | 267277DB1D5EE04900714A79 /* Release */, 377 | ); 378 | defaultConfigurationIsVisible = 0; 379 | defaultConfigurationName = Release; 380 | }; 381 | /* End XCConfigurationList section */ 382 | }; 383 | rootObject = 267277BF1D5EE04900714A79 /* Project object */; 384 | } 385 | -------------------------------------------------------------------------------- /Router.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Router/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // Router 4 | // 5 | // Created by DianQK on 8/13/16. 6 | // Copyright © 2016 T. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { 17 | Router.register(path: Path.search("")) 18 | Router.register(path: Path.profile) 19 | Router.register(path: Path.login) 20 | Router.register(path: Path.timeline) 21 | return true 22 | } 23 | 24 | func application(app: UIApplication, openURL url: NSURL, options: [String : AnyObject]) -> Bool { 25 | Router.open(url: url) 26 | return true 27 | } 28 | 29 | } 30 | 31 | -------------------------------------------------------------------------------- /Router/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "ipad", 35 | "size" : "29x29", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "ipad", 40 | "size" : "29x29", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "40x40", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "40x40", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "76x76", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "76x76", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "83.5x83.5", 66 | "scale" : "2x" 67 | } 68 | ], 69 | "info" : { 70 | "version" : 1, 71 | "author" : "xcode" 72 | } 73 | } -------------------------------------------------------------------------------- /Router/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Router/Assets.xcassets/DianQK.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "DianQK@400.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Router/Assets.xcassets/DianQK.imageset/DianQK@400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dianqk/Router/00ecf0506b8e5c606e3c6128b417758dc3acac12/Router/Assets.xcassets/DianQK.imageset/DianQK@400.png -------------------------------------------------------------------------------- /Router/Base.lproj/LaunchScreen.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 | -------------------------------------------------------------------------------- /Router/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 | 32 | 33 | 34 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | -------------------------------------------------------------------------------- /Router/Config.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Config.swift 3 | // Router 4 | // 5 | // Created by DianQK on 8/13/16. 6 | // Copyright © 2016 T. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import RouterX 11 | import SwiftyJSON 12 | 13 | private let host = "router://qing.com" 14 | 15 | enum Path { 16 | case search(String) 17 | case profile 18 | case login 19 | case timeline 20 | 21 | var url: NSURL { 22 | switch self { 23 | case .search(let text): 24 | return NSURL(string: host + "/search" + "/" + text)! 25 | case .profile: 26 | return NSURL(string: host + "/profile")! 27 | case .login: 28 | return NSURL(string: host + "/login")! 29 | case .timeline: 30 | return NSURL(string: host + "/timeline")! 31 | } 32 | } 33 | 34 | var pattern: String { 35 | switch self { 36 | case .search: 37 | return "/search/:text" 38 | case .profile: 39 | return "/profile" 40 | case .login: 41 | return "/login" 42 | case .timeline: 43 | return "/timeline" 44 | } 45 | } 46 | 47 | var handler: MatchRouteHandler { 48 | return { (url, parameters, _) in 49 | switch self { 50 | case .search: 51 | let search = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("SearchViewController") as! SearchViewController 52 | search.get(url, sender: JSON(parameters)) 53 | case .profile: 54 | let profile = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("ProfileViewController") as! ProfileViewController 55 | profile.get(url, sender: JSON(parameters)) 56 | case .login: 57 | let login = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("LoginViewController") as! LoginViewController 58 | login.get(url, sender: JSON(parameters)) 59 | case .timeline: 60 | let timeline = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("TimelineViewController") as! TimelineViewController 61 | timeline.get(url, sender: JSON(parameters)) 62 | } 63 | } 64 | 65 | } 66 | } 67 | 68 | extension Router { 69 | static func open(path path: Path) { 70 | open(url: path.url) 71 | } 72 | 73 | static func register(path path: Path) { 74 | register(pattern: path.pattern, handler: path.handler) 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /Router/HUD.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HUD.swift 3 | // Router 4 | // 5 | // Created by DianQK on 8/13/16. 6 | // Copyright © 2016 T. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import MBProgressHUD 11 | 12 | struct HUD { 13 | static func show(_ text: String) { 14 | let hud = MBProgressHUD.showHUDAddedTo(UIApplication.sharedApplication().keyWindow, animated: true) 15 | hud.mode = MBProgressHUDMode.Text 16 | hud.labelText = text 17 | hud.removeFromSuperViewOnHide = true 18 | hud.userInteractionEnabled = false 19 | hud.hide(true, afterDelay: 1) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Router/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 | CFBundleURLTypes 22 | 23 | 24 | CFBundleTypeRole 25 | Editor 26 | CFBundleURLSchemes 27 | 28 | router 29 | 30 | 31 | 32 | CFBundleVersion 33 | 1 34 | LSRequiresIPhoneOS 35 | 36 | UILaunchStoryboardName 37 | LaunchScreen 38 | UIMainStoryboardFile 39 | Main 40 | UIRequiredDeviceCapabilities 41 | 42 | armv7 43 | 44 | UISupportedInterfaceOrientations 45 | 46 | UIInterfaceOrientationPortrait 47 | UIInterfaceOrientationLandscapeLeft 48 | UIInterfaceOrientationLandscapeRight 49 | 50 | UISupportedInterfaceOrientations~ipad 51 | 52 | UIInterfaceOrientationPortrait 53 | UIInterfaceOrientationPortraitUpsideDown 54 | UIInterfaceOrientationLandscapeLeft 55 | UIInterfaceOrientationLandscapeRight 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /Router/RootViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RootViewController.swift 3 | // Router 4 | // 5 | // Created by DianQK on 8/13/16. 6 | // Copyright © 2016 T. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class RootViewController: UITableViewController { 12 | 13 | @IBOutlet weak var searchTextField: UITextField! 14 | 15 | @IBOutlet weak var searchButton: UIButton! 16 | 17 | @IBAction func search(sender: AnyObject) { 18 | guard let searchText = searchTextField.text else { 19 | print("=。= 并没有搜索内容") 20 | return 21 | } 22 | Router.open(path: Path.search(searchText)) 23 | } 24 | 25 | @IBAction func showProfile(sender: AnyObject) { 26 | Router.open(path: Path.profile) 27 | } 28 | @IBAction func login(sender: AnyObject) { 29 | Router.open(path: Path.login) 30 | } 31 | 32 | @IBAction func showTimeline(sender: AnyObject) { 33 | Router.open(path: Path.timeline) 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /Router/ViewControllers/LoginViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LoginViewController.swift 3 | // Router 4 | // 5 | // Created by DianQK on 8/13/16. 6 | // Copyright © 2016 T. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import SwiftyJSON 11 | 12 | class LoginViewController: UIViewController { 13 | 14 | @IBAction func cancel(sender: AnyObject) { 15 | Router.topViewControler?.dismissViewControllerAnimated(true, completion: nil) 16 | } 17 | } 18 | 19 | extension LoginViewController: Routerable { 20 | var routingPattern: String { 21 | return "/login" 22 | } 23 | 24 | func get(url: NSURL, sender: JSON?) { 25 | Router.topViewControler?.showDetailViewController(self, sender: nil) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Router/ViewControllers/ProfileViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ProfileViewController.swift 3 | // Router 4 | // 5 | // Created by DianQK on 8/13/16. 6 | // Copyright © 2016 T. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ProfileViewController: UIViewController { 12 | 13 | @IBAction func back(sender: AnyObject) { 14 | navigationController?.popViewControllerAnimated(true) 15 | } 16 | } 17 | 18 | extension ProfileViewController: Routerable { 19 | var routingPattern: String { 20 | return "/profile" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Router/ViewControllers/SearchViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SearchViewController.swift 3 | // Router 4 | // 5 | // Created by DianQK on 8/13/16. 6 | // Copyright © 2016 T. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import SwiftyJSON 11 | 12 | class SearchViewController: UIViewController { 13 | 14 | @IBOutlet private weak var searchBar: UISearchBar! 15 | 16 | private var _searchText: String? 17 | 18 | override func viewDidLoad() { 19 | super.viewDidLoad() 20 | searchBar.text = _searchText 21 | HUD.show("ssss") 22 | } 23 | 24 | } 25 | 26 | extension SearchViewController: UISearchBarDelegate { 27 | 28 | func searchBarCancelButtonClicked(searchBar: UISearchBar) { 29 | Router.topViewControler?.dismissViewControllerAnimated(true, completion: nil) 30 | } 31 | 32 | } 33 | 34 | extension SearchViewController: Routerable { 35 | var routingPattern: String { 36 | return "/search/:text" 37 | } 38 | 39 | var routingIdentifier: String? { 40 | if let searchText = searchBar?.text { 41 | return "/search/" + searchText 42 | } 43 | return nil 44 | } 45 | 46 | func get(url: NSURL, sender: JSON?) { 47 | guard let searchText = sender?["text"].string else { 48 | return 49 | } 50 | 51 | if let topRouter = Router.topRouter where topRouter.routingIdentifier == routingIdentifier { 52 | print("打开了完全一样的搜索页面") 53 | return 54 | } 55 | 56 | if let topRouter = Router.topRouter where topRouter.routingPattern == searchText { 57 | print("仍然打开了搜索页面,这里不展示新页面,更新搜索内容") 58 | // topRouter.post(NSURL(string: "")!, sender: JSON(["text": searchText])) 59 | return 60 | } 61 | _searchText = searchText 62 | Router.topViewControler?.showDetailViewController(self, sender: nil) 63 | } 64 | 65 | func post(url: NSURL, sender: JSON?) { 66 | searchBar.text = sender?["text"].string 67 | } 68 | } -------------------------------------------------------------------------------- /Router/ViewControllers/TimelineViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TimelineViewController.swift 3 | // Router 4 | // 5 | // Created by DianQK on 8/13/16. 6 | // Copyright © 2016 T. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import SwiftyJSON 11 | 12 | class TimelineViewController: UIViewController { 13 | 14 | } 15 | 16 | extension TimelineViewController: Routerable { 17 | var routingPattern: String { 18 | return "/timeline" 19 | } 20 | 21 | func get(url: NSURL, sender: JSON?) { 22 | HUD.show("亲,还没登录哦") 23 | Router.open(path: Path.login) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Source/Router.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GGRouter.swift 3 | // GGQ 4 | // 5 | // Created by DianQK on 5/8/16. 6 | // Copyright © 2016 org.dianqk. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import SwiftyJSON 11 | import RouterX 12 | 13 | func handleURL(url: NSURL) -> JSON { 14 | guard let query = url.query else { 15 | return JSON([:]) 16 | } 17 | return JSON(query: query) 18 | } 19 | 20 | struct Router { 21 | private init() {} 22 | 23 | static let manager: RouterX.Router = RouterX.Router { (url, context) in 24 | print(url) 25 | } 26 | 27 | static var topRouter: Routerable? { 28 | return RouterManager.topRouterable() 29 | } 30 | 31 | static func register(pattern pattern: String, handler: MatchRouteHandler) { 32 | manager.registerRoutingPattern(pattern, handler: handler) 33 | } 34 | 35 | static func open(url url: NSURL) { 36 | manager.matchURLAndDoHandler(url) 37 | } 38 | 39 | static var topViewControler: UIViewController? { 40 | return RouterManager.topViewController() 41 | } 42 | } 43 | 44 | extension JSON { 45 | init(query: String) { 46 | let querys = query.componentsSeparatedByString("&") 47 | var parameters: [String: String] = [:] 48 | for query in querys { 49 | let parameter = query.componentsSeparatedByString("=") 50 | guard parameter.count >= 2 else { 51 | continue 52 | } 53 | parameters[parameter[0]] = parameter[1].stringByRemovingPercentEncoding 54 | } 55 | self.init(parameters) 56 | } 57 | } 58 | 59 | typealias RouterManager = UIApplication 60 | 61 | extension RouterManager { 62 | 63 | class func sharedRouterManager() -> RouterManager { 64 | return sharedApplication() 65 | } 66 | 67 | class func topRouterable() -> Routerable? { 68 | if let topRouterable = topViewController() as? Routerable { 69 | return topRouterable 70 | } else { 71 | // Warning("\(topViewController()) not conform Routerable") 72 | } 73 | return nil 74 | } 75 | 76 | class func findRouterable(base: UIViewController? = UIApplication.sharedApplication().windows.first?.rootViewController, routingPattern: String) -> Routerable? { 77 | if let vc = base as? Routerable where vc.routingPattern == routingPattern { 78 | return vc 79 | } 80 | if let nav = base as? UINavigationController { 81 | if let vc = findRouterable(nav.viewControllers, routingPattern: routingPattern) { 82 | return vc 83 | } 84 | } 85 | if let tab = base as? UITabBarController, vcs = tab.viewControllers { 86 | if let vc = findRouterable(vcs, routingPattern: routingPattern) { 87 | return vc 88 | } 89 | } 90 | if let presented = base?.presentedViewController { 91 | if let vc = findRouterable(presented, routingPattern: routingPattern) { 92 | return vc 93 | } 94 | } 95 | return nil 96 | } 97 | 98 | private class func findRouterable(bases: [UIViewController], routingPattern: String) -> Routerable? { 99 | for base in bases { 100 | if let vc = base as? Routerable where vc.routingPattern == routingPattern { 101 | return vc 102 | } 103 | if let nav = base as? UINavigationController { 104 | if let vc = findRouterable(nav.viewControllers, routingPattern: routingPattern) { 105 | return vc 106 | } 107 | } 108 | if let tab = base as? UITabBarController, let vcs = tab.viewControllers { 109 | if let vc = findRouterable(vcs, routingPattern: routingPattern) { 110 | return vc 111 | } 112 | } 113 | if let presented = base.presentedViewController { 114 | if let vc = findRouterable(presented, routingPattern: routingPattern) { 115 | return vc 116 | } 117 | } 118 | } 119 | return nil 120 | } 121 | 122 | func neverCareResultOpenURL(url: NSURL) { 123 | openURL(url) 124 | } 125 | 126 | } 127 | 128 | #if DEBUG 129 | extension UIViewController: Routerable { 130 | 131 | var routingPattern: String { 132 | return "404" 133 | } 134 | 135 | var routingIdentifier: String? { 136 | return nil 137 | } 138 | 139 | } 140 | #endif 141 | 142 | extension UIApplication { 143 | class func topViewController(base: UIViewController? = UIApplication.sharedApplication().keyWindow?.rootViewController) -> UIViewController? { 144 | if let nav = base as? UINavigationController { 145 | return topViewController(nav.visibleViewController) 146 | } 147 | if let tab = base as? UITabBarController { 148 | if let selected = tab.selectedViewController { 149 | return topViewController(selected) 150 | } 151 | } 152 | if let presented = base?.presentedViewController { 153 | return topViewController(presented) 154 | } 155 | return base 156 | } 157 | } 158 | 159 | extension UINavigationController { 160 | func popTo(routingPattern routingPattern: String) -> Bool { 161 | let vc = viewControllers.flatMap { vc -> UIViewController? in 162 | if let routerable = vc as? Routerable where routerable.routingPattern == routingPattern { 163 | return vc 164 | } else { 165 | return nil 166 | } 167 | }.first 168 | if let vc = vc { 169 | popToViewController(vc, animated: true) 170 | return true 171 | } else { 172 | return false 173 | } 174 | } 175 | /** 176 | 移除堆栈的 viewController ,移除 routingPattern 和 last 之间的 viewController 177 | 178 | - parameter routingPattern: 待移除到参数 viewController 179 | 180 | - returns: 是否移除成功 181 | */ 182 | func remove(to routingPattern: String) -> Bool { 183 | let vc = viewControllers.flatMap { vc -> UIViewController? in 184 | if let routerable = vc as? Routerable where routerable.routingPattern == routingPattern { 185 | return vc 186 | } else { 187 | return nil 188 | } 189 | }.first 190 | // TODO: 越界以及未找到的处理 191 | if let vc = vc, index = viewControllers.indexOf(vc) { 192 | let range = index + 1 ..< viewControllers.endIndex - 1 193 | viewControllers.removeRange(range) 194 | return true 195 | } else { 196 | return false 197 | } 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /Source/Routerable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Routerable.swift 3 | // GGQ 4 | // 5 | // Created by DianQK on 16/4/22. 6 | // Copyright © 2016年 org.dianqk. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import SwiftyJSON 11 | 12 | struct Body { 13 | static var json = JSON.null 14 | } 15 | 16 | protocol Routerable { 17 | /// unique path , 用于判断是否为完全相同页面 18 | var routingIdentifier: String? { get } 19 | /// path , 用于判断是否为相同页面 20 | var routingPattern: String { get } 21 | /// GET 方法,一般用来展示新页面 22 | func get(url: NSURL, sender: JSON?) 23 | /// POST 方法,一般用来更新数据 24 | func post(url: NSURL, sender: JSON?) 25 | } 26 | 27 | extension Routerable { 28 | func post(url: NSURL, sender: JSON?) { 29 | // Warning("未实现 POST") 30 | } 31 | 32 | func get(url: NSURL, sender: JSON?) { 33 | // Warning("未实现 GET") 34 | } 35 | 36 | var routingIdentifier: String? { 37 | return nil 38 | } 39 | } 40 | 41 | extension Routerable where Self: UIViewController { 42 | 43 | var routingIdentifier: String? { 44 | return routingPattern 45 | } 46 | 47 | func get(url: NSURL, sender: JSON?) { 48 | guard RouterManager.topRouterable()?.routingPattern != routingPattern else { 49 | HUD.show("多次打开 \(url)") 50 | return 51 | } 52 | hidesBottomBarWhenPushed = true 53 | RouterManager.topViewController()?.showViewController(self, sender: nil) 54 | } 55 | } 56 | --------------------------------------------------------------------------------