├── .gitignore ├── Demo ├── Demo.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── Demo.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── Demo │ ├── Account.swift │ ├── AppDelegate.swift │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ └── Contents.json │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── FastViewController.swift │ ├── Info.plist │ ├── LiveViewController.swift │ ├── LoginViewController.swift │ ├── NeedLoginViewController.swift │ ├── NoneViewController.swift │ ├── Router │ │ ├── Plugins │ │ │ ├── RouterAccountPlugin.swift │ │ │ ├── RouterLaunchPlugin.swift │ │ │ └── RouterSinglePlugin.swift │ │ ├── Router.swift │ │ ├── RouterOpener.swift │ │ └── RouterType.swift │ ├── Utils │ │ ├── Notification.swift │ │ └── Notificationable.swift │ └── ViewController.swift ├── Podfile └── Podfile.lock ├── LICENSE ├── Podfile ├── Podfile.lock ├── README.md ├── README_CN.md ├── Router.podspec ├── Router.xcodeproj ├── project.pbxproj └── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ └── IDEWorkspaceChecks.plist ├── Router.xcworkspace ├── contents.xcworkspacedata └── xcshareddata │ └── IDEWorkspaceChecks.plist └── Sources ├── Context.swift ├── Info.plist ├── Plugin.swift ├── PrivacyInfo.xcprivacy ├── Protocol.swift ├── Provider.swift └── Router.h /.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 | *.xccheckout 23 | *.xcscmblueprint 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | *.ipa 28 | *.dSYM.zip 29 | *.dSYM 30 | 31 | ## Playgrounds 32 | timeline.xctimeline 33 | playground.xcworkspace 34 | 35 | # Swift Package Manager 36 | # 37 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 38 | # Packages/ 39 | # Package.pins 40 | # Package.resolved 41 | .build/ 42 | 43 | # CocoaPods 44 | # 45 | # We recommend against adding the Pods directory to your .gitignore. However 46 | # you should judge for yourself, the pros and cons are mentioned at: 47 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 48 | # 49 | Pods/ 50 | 51 | # Carthage 52 | # 53 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 54 | # Carthage/Checkouts 55 | 56 | Carthage/Build 57 | 58 | # fastlane 59 | # 60 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 61 | # screenshots whenever they are needed. 62 | # For more information about the recommended setup visit: 63 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 64 | 65 | fastlane/report.xml 66 | fastlane/Preview.html 67 | fastlane/screenshots/**/*.png 68 | fastlane/test_output 69 | -------------------------------------------------------------------------------- /Demo/Demo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 54; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 6A595E95EAE9E20D30D1A8D6 /* Pods_Demo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DF996BB2479B7EE857452E0D /* Pods_Demo.framework */; }; 11 | C9170B802274475200052A4E /* LoginViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9170B7F2274475200052A4E /* LoginViewController.swift */; }; 12 | C9170B822274483F00052A4E /* Account.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9170B812274483F00052A4E /* Account.swift */; }; 13 | C9170B8422744F6300052A4E /* NeedLoginViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9170B8322744F6300052A4E /* NeedLoginViewController.swift */; }; 14 | C9D028B222607F8200B5E061 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9D028B122607F8200B5E061 /* AppDelegate.swift */; }; 15 | C9D028B422607F8200B5E061 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9D028B322607F8200B5E061 /* ViewController.swift */; }; 16 | C9D028B722607F8200B5E061 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C9D028B522607F8200B5E061 /* Main.storyboard */; }; 17 | C9D028B922607F8200B5E061 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C9D028B822607F8200B5E061 /* Assets.xcassets */; }; 18 | C9D028BC22607F8200B5E061 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C9D028BA22607F8200B5E061 /* LaunchScreen.storyboard */; }; 19 | C9D028E0226088F600B5E061 /* RouterOpener.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9D028DE226088F600B5E061 /* RouterOpener.swift */; }; 20 | C9D028E52260890900B5E061 /* RouterLaunchPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9D028E22260890900B5E061 /* RouterLaunchPlugin.swift */; }; 21 | C9D028E62260890900B5E061 /* RouterSinglePlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9D028E32260890900B5E061 /* RouterSinglePlugin.swift */; }; 22 | C9D028E72260890900B5E061 /* RouterAccountPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9D028E42260890900B5E061 /* RouterAccountPlugin.swift */; }; 23 | C9D028EB2260892B00B5E061 /* Notification.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9D028E92260892B00B5E061 /* Notification.swift */; }; 24 | C9D028EC2260892B00B5E061 /* Notificationable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9D028EA2260892B00B5E061 /* Notificationable.swift */; }; 25 | C9D028F02260893500B5E061 /* FastViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9D028ED2260893500B5E061 /* FastViewController.swift */; }; 26 | C9D028F12260893500B5E061 /* LiveViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9D028EE2260893500B5E061 /* LiveViewController.swift */; }; 27 | C9D028F22260893500B5E061 /* NoneViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9D028EF2260893500B5E061 /* NoneViewController.swift */; }; 28 | C9D028F42260902E00B5E061 /* Router.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9D028F32260902E00B5E061 /* Router.swift */; }; 29 | C9D028F62260932E00B5E061 /* RouterType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9D028F52260932E00B5E061 /* RouterType.swift */; }; 30 | /* End PBXBuildFile section */ 31 | 32 | /* Begin PBXFileReference section */ 33 | 4FD45B063BA419F7B2949502 /* Pods-Demo.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Demo.debug.xcconfig"; path = "Target Support Files/Pods-Demo/Pods-Demo.debug.xcconfig"; sourceTree = ""; }; 34 | C9170B7F2274475200052A4E /* LoginViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginViewController.swift; sourceTree = ""; }; 35 | C9170B812274483F00052A4E /* Account.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Account.swift; sourceTree = ""; }; 36 | C9170B8322744F6300052A4E /* NeedLoginViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NeedLoginViewController.swift; sourceTree = ""; }; 37 | C9D028AE22607F8200B5E061 /* Demo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Demo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 38 | C9D028B122607F8200B5E061 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 39 | C9D028B322607F8200B5E061 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 40 | C9D028B622607F8200B5E061 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 41 | C9D028B822607F8200B5E061 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 42 | C9D028BB22607F8200B5E061 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 43 | C9D028BD22607F8200B5E061 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 44 | C9D028DE226088F600B5E061 /* RouterOpener.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RouterOpener.swift; sourceTree = ""; }; 45 | C9D028E22260890900B5E061 /* RouterLaunchPlugin.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RouterLaunchPlugin.swift; sourceTree = ""; }; 46 | C9D028E32260890900B5E061 /* RouterSinglePlugin.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RouterSinglePlugin.swift; sourceTree = ""; }; 47 | C9D028E42260890900B5E061 /* RouterAccountPlugin.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RouterAccountPlugin.swift; sourceTree = ""; }; 48 | C9D028E92260892B00B5E061 /* Notification.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Notification.swift; sourceTree = ""; }; 49 | C9D028EA2260892B00B5E061 /* Notificationable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Notificationable.swift; sourceTree = ""; }; 50 | C9D028ED2260893500B5E061 /* FastViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FastViewController.swift; sourceTree = ""; }; 51 | C9D028EE2260893500B5E061 /* LiveViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LiveViewController.swift; sourceTree = ""; }; 52 | C9D028EF2260893500B5E061 /* NoneViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NoneViewController.swift; sourceTree = ""; }; 53 | C9D028F32260902E00B5E061 /* Router.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Router.swift; sourceTree = ""; }; 54 | C9D028F52260932E00B5E061 /* RouterType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RouterType.swift; sourceTree = ""; }; 55 | CEB7AEFC933E088EF7C01816 /* Pods-Demo.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Demo.release.xcconfig"; path = "Target Support Files/Pods-Demo/Pods-Demo.release.xcconfig"; sourceTree = ""; }; 56 | DF996BB2479B7EE857452E0D /* Pods_Demo.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Demo.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 57 | /* End PBXFileReference section */ 58 | 59 | /* Begin PBXFrameworksBuildPhase section */ 60 | C9D028AB22607F8200B5E061 /* Frameworks */ = { 61 | isa = PBXFrameworksBuildPhase; 62 | buildActionMask = 2147483647; 63 | files = ( 64 | 6A595E95EAE9E20D30D1A8D6 /* Pods_Demo.framework in Frameworks */, 65 | ); 66 | runOnlyForDeploymentPostprocessing = 0; 67 | }; 68 | /* End PBXFrameworksBuildPhase section */ 69 | 70 | /* Begin PBXGroup section */ 71 | 3E4CDB657E061C16E3075E06 /* Pods */ = { 72 | isa = PBXGroup; 73 | children = ( 74 | 4FD45B063BA419F7B2949502 /* Pods-Demo.debug.xcconfig */, 75 | CEB7AEFC933E088EF7C01816 /* Pods-Demo.release.xcconfig */, 76 | ); 77 | path = Pods; 78 | sourceTree = ""; 79 | }; 80 | C9170B7E227446BB00052A4E /* Router */ = { 81 | isa = PBXGroup; 82 | children = ( 83 | C9D028F32260902E00B5E061 /* Router.swift */, 84 | C9D028F52260932E00B5E061 /* RouterType.swift */, 85 | C9D028DE226088F600B5E061 /* RouterOpener.swift */, 86 | C9D028DD226088B300B5E061 /* Plugins */, 87 | ); 88 | path = Router; 89 | sourceTree = ""; 90 | }; 91 | C9D028A522607F8200B5E061 = { 92 | isa = PBXGroup; 93 | children = ( 94 | C9D028B022607F8200B5E061 /* Demo */, 95 | C9D028AF22607F8200B5E061 /* Products */, 96 | 3E4CDB657E061C16E3075E06 /* Pods */, 97 | EA37C84C061630303B9B0626 /* Frameworks */, 98 | ); 99 | sourceTree = ""; 100 | }; 101 | C9D028AF22607F8200B5E061 /* Products */ = { 102 | isa = PBXGroup; 103 | children = ( 104 | C9D028AE22607F8200B5E061 /* Demo.app */, 105 | ); 106 | name = Products; 107 | sourceTree = ""; 108 | }; 109 | C9D028B022607F8200B5E061 /* Demo */ = { 110 | isa = PBXGroup; 111 | children = ( 112 | C9170B7E227446BB00052A4E /* Router */, 113 | C9D028E82260891C00B5E061 /* Utils */, 114 | C9D028B122607F8200B5E061 /* AppDelegate.swift */, 115 | C9D028B322607F8200B5E061 /* ViewController.swift */, 116 | C9D028ED2260893500B5E061 /* FastViewController.swift */, 117 | C9D028EE2260893500B5E061 /* LiveViewController.swift */, 118 | C9D028EF2260893500B5E061 /* NoneViewController.swift */, 119 | C9170B8322744F6300052A4E /* NeedLoginViewController.swift */, 120 | C9170B7F2274475200052A4E /* LoginViewController.swift */, 121 | C9D028B522607F8200B5E061 /* Main.storyboard */, 122 | C9D028B822607F8200B5E061 /* Assets.xcassets */, 123 | C9D028BA22607F8200B5E061 /* LaunchScreen.storyboard */, 124 | C9D028BD22607F8200B5E061 /* Info.plist */, 125 | C9170B812274483F00052A4E /* Account.swift */, 126 | ); 127 | path = Demo; 128 | sourceTree = ""; 129 | }; 130 | C9D028DD226088B300B5E061 /* Plugins */ = { 131 | isa = PBXGroup; 132 | children = ( 133 | C9D028E42260890900B5E061 /* RouterAccountPlugin.swift */, 134 | C9D028E22260890900B5E061 /* RouterLaunchPlugin.swift */, 135 | C9D028E32260890900B5E061 /* RouterSinglePlugin.swift */, 136 | ); 137 | path = Plugins; 138 | sourceTree = ""; 139 | }; 140 | C9D028E82260891C00B5E061 /* Utils */ = { 141 | isa = PBXGroup; 142 | children = ( 143 | C9D028EA2260892B00B5E061 /* Notificationable.swift */, 144 | C9D028E92260892B00B5E061 /* Notification.swift */, 145 | ); 146 | path = Utils; 147 | sourceTree = ""; 148 | }; 149 | EA37C84C061630303B9B0626 /* Frameworks */ = { 150 | isa = PBXGroup; 151 | children = ( 152 | DF996BB2479B7EE857452E0D /* Pods_Demo.framework */, 153 | ); 154 | name = Frameworks; 155 | sourceTree = ""; 156 | }; 157 | /* End PBXGroup section */ 158 | 159 | /* Begin PBXNativeTarget section */ 160 | C9D028AD22607F8200B5E061 /* Demo */ = { 161 | isa = PBXNativeTarget; 162 | buildConfigurationList = C9D028C022607F8200B5E061 /* Build configuration list for PBXNativeTarget "Demo" */; 163 | buildPhases = ( 164 | 9D62DF0D51785BD3712034FB /* [CP] Check Pods Manifest.lock */, 165 | C9D028AA22607F8200B5E061 /* Sources */, 166 | C9D028AB22607F8200B5E061 /* Frameworks */, 167 | C9D028AC22607F8200B5E061 /* Resources */, 168 | 398883A394CE113360F5C960 /* [CP] Embed Pods Frameworks */, 169 | ); 170 | buildRules = ( 171 | ); 172 | dependencies = ( 173 | ); 174 | name = Demo; 175 | productName = Demo; 176 | productReference = C9D028AE22607F8200B5E061 /* Demo.app */; 177 | productType = "com.apple.product-type.application"; 178 | }; 179 | /* End PBXNativeTarget section */ 180 | 181 | /* Begin PBXProject section */ 182 | C9D028A622607F8200B5E061 /* Project object */ = { 183 | isa = PBXProject; 184 | attributes = { 185 | LastSwiftUpdateCheck = 1020; 186 | LastUpgradeCheck = 1020; 187 | ORGANIZATIONNAME = swift; 188 | TargetAttributes = { 189 | C9D028AD22607F8200B5E061 = { 190 | CreatedOnToolsVersion = 10.2; 191 | }; 192 | }; 193 | }; 194 | buildConfigurationList = C9D028A922607F8200B5E061 /* Build configuration list for PBXProject "Demo" */; 195 | compatibilityVersion = "Xcode 9.3"; 196 | developmentRegion = en; 197 | hasScannedForEncodings = 0; 198 | knownRegions = ( 199 | en, 200 | Base, 201 | ); 202 | mainGroup = C9D028A522607F8200B5E061; 203 | productRefGroup = C9D028AF22607F8200B5E061 /* Products */; 204 | projectDirPath = ""; 205 | projectRoot = ""; 206 | targets = ( 207 | C9D028AD22607F8200B5E061 /* Demo */, 208 | ); 209 | }; 210 | /* End PBXProject section */ 211 | 212 | /* Begin PBXResourcesBuildPhase section */ 213 | C9D028AC22607F8200B5E061 /* Resources */ = { 214 | isa = PBXResourcesBuildPhase; 215 | buildActionMask = 2147483647; 216 | files = ( 217 | C9D028BC22607F8200B5E061 /* LaunchScreen.storyboard in Resources */, 218 | C9D028B922607F8200B5E061 /* Assets.xcassets in Resources */, 219 | C9D028B722607F8200B5E061 /* Main.storyboard in Resources */, 220 | ); 221 | runOnlyForDeploymentPostprocessing = 0; 222 | }; 223 | /* End PBXResourcesBuildPhase section */ 224 | 225 | /* Begin PBXShellScriptBuildPhase section */ 226 | 398883A394CE113360F5C960 /* [CP] Embed Pods Frameworks */ = { 227 | isa = PBXShellScriptBuildPhase; 228 | buildActionMask = 2147483647; 229 | files = ( 230 | ); 231 | inputFileListPaths = ( 232 | "${PODS_ROOT}/Target Support Files/Pods-Demo/Pods-Demo-frameworks-${CONFIGURATION}-input-files.xcfilelist", 233 | ); 234 | name = "[CP] Embed Pods Frameworks"; 235 | outputFileListPaths = ( 236 | "${PODS_ROOT}/Target Support Files/Pods-Demo/Pods-Demo-frameworks-${CONFIGURATION}-output-files.xcfilelist", 237 | ); 238 | runOnlyForDeploymentPostprocessing = 0; 239 | shellPath = /bin/sh; 240 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Demo/Pods-Demo-frameworks.sh\"\n"; 241 | showEnvVarsInLog = 0; 242 | }; 243 | 9D62DF0D51785BD3712034FB /* [CP] Check Pods Manifest.lock */ = { 244 | isa = PBXShellScriptBuildPhase; 245 | buildActionMask = 2147483647; 246 | files = ( 247 | ); 248 | inputFileListPaths = ( 249 | ); 250 | inputPaths = ( 251 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 252 | "${PODS_ROOT}/Manifest.lock", 253 | ); 254 | name = "[CP] Check Pods Manifest.lock"; 255 | outputFileListPaths = ( 256 | ); 257 | outputPaths = ( 258 | "$(DERIVED_FILE_DIR)/Pods-Demo-checkManifestLockResult.txt", 259 | ); 260 | runOnlyForDeploymentPostprocessing = 0; 261 | shellPath = /bin/sh; 262 | 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"; 263 | showEnvVarsInLog = 0; 264 | }; 265 | /* End PBXShellScriptBuildPhase section */ 266 | 267 | /* Begin PBXSourcesBuildPhase section */ 268 | C9D028AA22607F8200B5E061 /* Sources */ = { 269 | isa = PBXSourcesBuildPhase; 270 | buildActionMask = 2147483647; 271 | files = ( 272 | C9170B802274475200052A4E /* LoginViewController.swift in Sources */, 273 | C9D028F62260932E00B5E061 /* RouterType.swift in Sources */, 274 | C9D028B422607F8200B5E061 /* ViewController.swift in Sources */, 275 | C9D028E52260890900B5E061 /* RouterLaunchPlugin.swift in Sources */, 276 | C9D028F12260893500B5E061 /* LiveViewController.swift in Sources */, 277 | C9170B822274483F00052A4E /* Account.swift in Sources */, 278 | C9D028E62260890900B5E061 /* RouterSinglePlugin.swift in Sources */, 279 | C9D028B222607F8200B5E061 /* AppDelegate.swift in Sources */, 280 | C9D028F02260893500B5E061 /* FastViewController.swift in Sources */, 281 | C9D028EB2260892B00B5E061 /* Notification.swift in Sources */, 282 | C9D028EC2260892B00B5E061 /* Notificationable.swift in Sources */, 283 | C9D028E0226088F600B5E061 /* RouterOpener.swift in Sources */, 284 | C9170B8422744F6300052A4E /* NeedLoginViewController.swift in Sources */, 285 | C9D028E72260890900B5E061 /* RouterAccountPlugin.swift in Sources */, 286 | C9D028F22260893500B5E061 /* NoneViewController.swift in Sources */, 287 | C9D028F42260902E00B5E061 /* Router.swift in Sources */, 288 | ); 289 | runOnlyForDeploymentPostprocessing = 0; 290 | }; 291 | /* End PBXSourcesBuildPhase section */ 292 | 293 | /* Begin PBXVariantGroup section */ 294 | C9D028B522607F8200B5E061 /* Main.storyboard */ = { 295 | isa = PBXVariantGroup; 296 | children = ( 297 | C9D028B622607F8200B5E061 /* Base */, 298 | ); 299 | name = Main.storyboard; 300 | sourceTree = ""; 301 | }; 302 | C9D028BA22607F8200B5E061 /* LaunchScreen.storyboard */ = { 303 | isa = PBXVariantGroup; 304 | children = ( 305 | C9D028BB22607F8200B5E061 /* Base */, 306 | ); 307 | name = LaunchScreen.storyboard; 308 | sourceTree = ""; 309 | }; 310 | /* End PBXVariantGroup section */ 311 | 312 | /* Begin XCBuildConfiguration section */ 313 | C9D028BE22607F8200B5E061 /* Debug */ = { 314 | isa = XCBuildConfiguration; 315 | buildSettings = { 316 | ALWAYS_SEARCH_USER_PATHS = NO; 317 | CLANG_ANALYZER_NONNULL = YES; 318 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 319 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 320 | CLANG_CXX_LIBRARY = "libc++"; 321 | CLANG_ENABLE_MODULES = YES; 322 | CLANG_ENABLE_OBJC_ARC = YES; 323 | CLANG_ENABLE_OBJC_WEAK = YES; 324 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 325 | CLANG_WARN_BOOL_CONVERSION = YES; 326 | CLANG_WARN_COMMA = YES; 327 | CLANG_WARN_CONSTANT_CONVERSION = YES; 328 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 329 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 330 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 331 | CLANG_WARN_EMPTY_BODY = YES; 332 | CLANG_WARN_ENUM_CONVERSION = YES; 333 | CLANG_WARN_INFINITE_RECURSION = YES; 334 | CLANG_WARN_INT_CONVERSION = YES; 335 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 336 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 337 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 338 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 339 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 340 | CLANG_WARN_STRICT_PROTOTYPES = YES; 341 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 342 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 343 | CLANG_WARN_UNREACHABLE_CODE = YES; 344 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 345 | CODE_SIGN_IDENTITY = "iPhone Developer"; 346 | COPY_PHASE_STRIP = NO; 347 | DEBUG_INFORMATION_FORMAT = dwarf; 348 | ENABLE_STRICT_OBJC_MSGSEND = YES; 349 | ENABLE_TESTABILITY = YES; 350 | GCC_C_LANGUAGE_STANDARD = gnu11; 351 | GCC_DYNAMIC_NO_PIC = NO; 352 | GCC_NO_COMMON_BLOCKS = YES; 353 | GCC_OPTIMIZATION_LEVEL = 0; 354 | GCC_PREPROCESSOR_DEFINITIONS = ( 355 | "DEBUG=1", 356 | "$(inherited)", 357 | ); 358 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 359 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 360 | GCC_WARN_UNDECLARED_SELECTOR = YES; 361 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 362 | GCC_WARN_UNUSED_FUNCTION = YES; 363 | GCC_WARN_UNUSED_VARIABLE = YES; 364 | IPHONEOS_DEPLOYMENT_TARGET = 12.2; 365 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 366 | MTL_FAST_MATH = YES; 367 | ONLY_ACTIVE_ARCH = YES; 368 | SDKROOT = iphoneos; 369 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 370 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 371 | }; 372 | name = Debug; 373 | }; 374 | C9D028BF22607F8200B5E061 /* Release */ = { 375 | isa = XCBuildConfiguration; 376 | buildSettings = { 377 | ALWAYS_SEARCH_USER_PATHS = NO; 378 | CLANG_ANALYZER_NONNULL = YES; 379 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 380 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 381 | CLANG_CXX_LIBRARY = "libc++"; 382 | CLANG_ENABLE_MODULES = YES; 383 | CLANG_ENABLE_OBJC_ARC = YES; 384 | CLANG_ENABLE_OBJC_WEAK = YES; 385 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 386 | CLANG_WARN_BOOL_CONVERSION = YES; 387 | CLANG_WARN_COMMA = YES; 388 | CLANG_WARN_CONSTANT_CONVERSION = YES; 389 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 390 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 391 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 392 | CLANG_WARN_EMPTY_BODY = YES; 393 | CLANG_WARN_ENUM_CONVERSION = YES; 394 | CLANG_WARN_INFINITE_RECURSION = YES; 395 | CLANG_WARN_INT_CONVERSION = YES; 396 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 397 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 398 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 399 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 400 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 401 | CLANG_WARN_STRICT_PROTOTYPES = YES; 402 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 403 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 404 | CLANG_WARN_UNREACHABLE_CODE = YES; 405 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 406 | CODE_SIGN_IDENTITY = "iPhone Developer"; 407 | COPY_PHASE_STRIP = NO; 408 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 409 | ENABLE_NS_ASSERTIONS = NO; 410 | ENABLE_STRICT_OBJC_MSGSEND = YES; 411 | GCC_C_LANGUAGE_STANDARD = gnu11; 412 | GCC_NO_COMMON_BLOCKS = YES; 413 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 414 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 415 | GCC_WARN_UNDECLARED_SELECTOR = YES; 416 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 417 | GCC_WARN_UNUSED_FUNCTION = YES; 418 | GCC_WARN_UNUSED_VARIABLE = YES; 419 | IPHONEOS_DEPLOYMENT_TARGET = 12.2; 420 | MTL_ENABLE_DEBUG_INFO = NO; 421 | MTL_FAST_MATH = YES; 422 | SDKROOT = iphoneos; 423 | SWIFT_COMPILATION_MODE = wholemodule; 424 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 425 | VALIDATE_PRODUCT = YES; 426 | }; 427 | name = Release; 428 | }; 429 | C9D028C122607F8200B5E061 /* Debug */ = { 430 | isa = XCBuildConfiguration; 431 | baseConfigurationReference = 4FD45B063BA419F7B2949502 /* Pods-Demo.debug.xcconfig */; 432 | buildSettings = { 433 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 434 | CODE_SIGN_IDENTITY = "Apple Development"; 435 | CODE_SIGN_STYLE = Automatic; 436 | DEVELOPMENT_TEAM = ""; 437 | INFOPLIST_FILE = Demo/Info.plist; 438 | IPHONEOS_DEPLOYMENT_TARGET = 12.2; 439 | LD_RUNPATH_SEARCH_PATHS = ( 440 | "$(inherited)", 441 | "@executable_path/Frameworks", 442 | ); 443 | PRODUCT_BUNDLE_IDENTIFIER = com.lee.router.demo; 444 | PRODUCT_NAME = "$(TARGET_NAME)"; 445 | PROVISIONING_PROFILE_SPECIFIER = ""; 446 | SWIFT_VERSION = 5.0; 447 | TARGETED_DEVICE_FAMILY = "1,2"; 448 | }; 449 | name = Debug; 450 | }; 451 | C9D028C222607F8200B5E061 /* Release */ = { 452 | isa = XCBuildConfiguration; 453 | baseConfigurationReference = CEB7AEFC933E088EF7C01816 /* Pods-Demo.release.xcconfig */; 454 | buildSettings = { 455 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 456 | CODE_SIGN_IDENTITY = "Apple Development"; 457 | CODE_SIGN_STYLE = Automatic; 458 | DEVELOPMENT_TEAM = ""; 459 | INFOPLIST_FILE = Demo/Info.plist; 460 | IPHONEOS_DEPLOYMENT_TARGET = 12.2; 461 | LD_RUNPATH_SEARCH_PATHS = ( 462 | "$(inherited)", 463 | "@executable_path/Frameworks", 464 | ); 465 | PRODUCT_BUNDLE_IDENTIFIER = com.lee.router.demo; 466 | PRODUCT_NAME = "$(TARGET_NAME)"; 467 | PROVISIONING_PROFILE_SPECIFIER = ""; 468 | SWIFT_VERSION = 5.0; 469 | TARGETED_DEVICE_FAMILY = "1,2"; 470 | }; 471 | name = Release; 472 | }; 473 | /* End XCBuildConfiguration section */ 474 | 475 | /* Begin XCConfigurationList section */ 476 | C9D028A922607F8200B5E061 /* Build configuration list for PBXProject "Demo" */ = { 477 | isa = XCConfigurationList; 478 | buildConfigurations = ( 479 | C9D028BE22607F8200B5E061 /* Debug */, 480 | C9D028BF22607F8200B5E061 /* Release */, 481 | ); 482 | defaultConfigurationIsVisible = 0; 483 | defaultConfigurationName = Release; 484 | }; 485 | C9D028C022607F8200B5E061 /* Build configuration list for PBXNativeTarget "Demo" */ = { 486 | isa = XCConfigurationList; 487 | buildConfigurations = ( 488 | C9D028C122607F8200B5E061 /* Debug */, 489 | C9D028C222607F8200B5E061 /* Release */, 490 | ); 491 | defaultConfigurationIsVisible = 0; 492 | defaultConfigurationName = Release; 493 | }; 494 | /* End XCConfigurationList section */ 495 | }; 496 | rootObject = C9D028A622607F8200B5E061 /* Project object */; 497 | } 498 | -------------------------------------------------------------------------------- /Demo/Demo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Demo/Demo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Demo/Demo.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Demo/Demo.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Demo/Demo/Account.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Account.swift 3 | // Demo 4 | // 5 | // Created by 李响 on 2019/4/27. 6 | // Copyright © 2019 swift. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | enum Account { 12 | 13 | static var isLogin: Bool { 14 | return info != nil 15 | } 16 | 17 | static var info: User? 18 | } 19 | 20 | struct User { 21 | let id: Int 22 | let name: String 23 | let avatar: String 24 | /* ... */ 25 | } 26 | -------------------------------------------------------------------------------- /Demo/Demo/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // Demo 4 | // 5 | // Created by 李响 on 2019/4/12. 6 | // Copyright © 2019 swift. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Router 11 | 12 | @UIApplicationMain 13 | class AppDelegate: UIResponder, UIApplicationDelegate { 14 | 15 | static var shared: AppDelegate { 16 | return UIApplication.shared.delegate as! AppDelegate 17 | } 18 | 19 | var window: UIWindow? 20 | var isLaunched: Bool = false 21 | 22 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 23 | 24 | DispatchQueue.main.asyncAfter(deadline: .now() + 1) { 25 | self.isLaunched = true 26 | NotificationCenter.Delegate.post(.launched) 27 | } 28 | 29 | return true 30 | } 31 | 32 | func applicationWillResignActive(_ application: UIApplication) { 33 | // 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. 34 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 35 | } 36 | 37 | func applicationDidEnterBackground(_ application: UIApplication) { 38 | // 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. 39 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 40 | } 41 | 42 | func applicationWillEnterForeground(_ application: UIApplication) { 43 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 44 | } 45 | 46 | func applicationDidBecomeActive(_ application: UIApplication) { 47 | // 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. 48 | } 49 | 50 | func applicationWillTerminate(_ application: UIApplication) { 51 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 52 | } 53 | 54 | 55 | } 56 | 57 | -------------------------------------------------------------------------------- /Demo/Demo/Assets.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" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /Demo/Demo/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Demo/Demo/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 | -------------------------------------------------------------------------------- /Demo/Demo/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 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /Demo/Demo/FastViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FastViewController.swift 3 | // Demo 4 | // 5 | // Created by 李响 on 2019/4/10. 6 | // Copyright © 2019 swift. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class FastViewController: UIViewController { 12 | 13 | override func viewDidLoad() { 14 | super.viewDidLoad() 15 | 16 | view.backgroundColor = #colorLiteral(red: 0.5568627715, green: 0.3529411852, blue: 0.9686274529, alpha: 1) 17 | 18 | let tap = UITapGestureRecognizer(target: self, action: #selector(tapAction)) 19 | view.addGestureRecognizer(tap) 20 | } 21 | 22 | @objc func tapAction() { 23 | close() 24 | } 25 | } 26 | 27 | extension FastViewController: RouterSingleable { 28 | 29 | var single: RouterSingleType { 30 | return .fast 31 | } 32 | 33 | func close(will single: RouterSingleType, completion: @escaping (Bool) -> Void) { 34 | switch single { 35 | case .fast: 36 | completion(false) 37 | 38 | default: 39 | close { 40 | completion(true) 41 | } 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Demo/Demo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDisplayName 8 | Router 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 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 | 48 | UISupportedInterfaceOrientations~ipad 49 | 50 | UIInterfaceOrientationPortrait 51 | UIInterfaceOrientationPortraitUpsideDown 52 | UIInterfaceOrientationLandscapeLeft 53 | UIInterfaceOrientationLandscapeRight 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /Demo/Demo/LiveViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LiveViewController.swift 3 | // Demo 4 | // 5 | // Created by 李响 on 2019/4/10. 6 | // Copyright © 2019 swift. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class LiveViewController: UIViewController { 12 | 13 | var id: String = "" 14 | 15 | override func viewDidLoad() { 16 | super.viewDidLoad() 17 | 18 | view.backgroundColor = #colorLiteral(red: 0.2549019754, green: 0.2745098174, blue: 0.3019607961, alpha: 1) 19 | 20 | let tap = UITapGestureRecognizer(target: self, action: #selector(tapAction)) 21 | view.addGestureRecognizer(tap) 22 | } 23 | 24 | @objc func tapAction() { 25 | close() 26 | } 27 | } 28 | 29 | extension LiveViewController: RouterSingleable { 30 | 31 | var single: RouterSingleType { 32 | return .live 33 | } 34 | 35 | func close(will single: RouterSingleType, completion: @escaping (Bool) -> Void) { 36 | close { 37 | completion(true) 38 | } 39 | } 40 | 41 | func open(_ completion: @escaping () -> Void) { 42 | guard let controller = UIViewController.topMost else { 43 | return 44 | } 45 | controller.present(self, animated: true, completion: completion) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Demo/Demo/LoginViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LoginViewController.swift 3 | // Demo 4 | // 5 | // Created by 李响 on 2019/4/27. 6 | // Copyright © 2019 swift. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import SnapKit 11 | 12 | class LoginViewController: UIViewController { 13 | 14 | lazy var loginButton: UIButton = { 15 | $0.setTitle("Login", for: .normal) 16 | $0.setTitleColor(.black, for: .normal) 17 | $0.addTarget(self, action: #selector(loginAction), for: .touchUpInside) 18 | return $0 19 | } ( UIButton() ) 20 | 21 | lazy var cancelButton: UIButton = { 22 | $0.setTitle("Cancel", for: .normal) 23 | $0.setTitleColor(.red, for: .normal) 24 | $0.addTarget(self, action: #selector(cancelAction), for: .touchUpInside) 25 | return $0 26 | } ( UIButton() ) 27 | 28 | private var completion: ((Bool) -> Void)? 29 | 30 | init(_ completion: @escaping ((Bool) -> Void)) { 31 | self.completion = completion 32 | super.init(nibName: nil, bundle: nil) 33 | } 34 | 35 | required init?(coder aDecoder: NSCoder) { 36 | super.init(coder: aDecoder) 37 | } 38 | 39 | override func viewDidLoad() { 40 | super.viewDidLoad() 41 | setup() 42 | setupLayout() 43 | } 44 | 45 | private func setup() { 46 | view.backgroundColor = .white 47 | view.addSubview(loginButton) 48 | view.addSubview(cancelButton) 49 | } 50 | 51 | private func setupLayout() { 52 | loginButton.snp.makeConstraints { (make) in 53 | make.center.equalToSuperview() 54 | } 55 | 56 | cancelButton.snp.makeConstraints { (make) in 57 | make.centerX.equalToSuperview() 58 | make.top.equalTo(loginButton.snp.bottom).offset(40) 59 | } 60 | } 61 | } 62 | 63 | extension LoginViewController { 64 | 65 | @objc private func loginAction(_ sender: UIButton) { 66 | // 假装登录成功 存储用户信息 67 | Account.info = User(id: 1994, name: "LEE", avatar: "XXXX") 68 | 69 | dismiss(animated: true) { 70 | self.completion?(true) 71 | } 72 | } 73 | 74 | @objc private func cancelAction(_ sender: UIButton) { 75 | dismiss(animated: true) { 76 | self.completion?(false) 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /Demo/Demo/NeedLoginViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NeedLoginViewController.swift 3 | // Demo 4 | // 5 | // Created by 李响 on 2019/4/27. 6 | // Copyright © 2019 swift. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class NeedLoginViewController: UIViewController { 12 | 13 | override func viewDidLoad() { 14 | super.viewDidLoad() 15 | 16 | view.backgroundColor = #colorLiteral(red: 1, green: 0.3137254902, blue: 0.3137254902, alpha: 1) 17 | 18 | let tap = UITapGestureRecognizer(target: self, action: #selector(tapAction)) 19 | view.addGestureRecognizer(tap) 20 | } 21 | 22 | @objc func tapAction() { 23 | close() 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Demo/Demo/NoneViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NoneViewController.swift 3 | // Demo 4 | // 5 | // Created by 李响 on 2019/4/10. 6 | // Copyright © 2019 swift. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class NoneViewController: UIViewController { 12 | 13 | override func viewDidLoad() { 14 | super.viewDidLoad() 15 | 16 | view.backgroundColor = #colorLiteral(red: 0.8039215803, green: 0.8039215803, blue: 0.8039215803, alpha: 1) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Demo/Demo/Router/Plugins/RouterAccountPlugin.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RouterAccountPlugin.swift 3 | // Demo 4 | // 5 | // Created by 李响 on 2019/4/10. 6 | // Copyright © 2019 swift. All rights reserved. 7 | // 8 | 9 | import Router 10 | 11 | class RouterAccountPlugin: Plugin { 12 | 13 | /* 14 | 在准备打开阶段 拦截需要登录的类型, 并根据登录状态处理是否需要打开登录页面 15 | 登录成功后可执行回调继续打开流程 16 | */ 17 | override func prepare(open type: RouterType, completion: @escaping (Bool) -> Void) { 18 | guard type == .open_needlogin else { 19 | completion(true) 20 | return 21 | } 22 | guard !Account.isLogin else { 23 | completion(true) 24 | return 25 | } 26 | guard let root = AppDelegate.shared.window?.rootViewController else { 27 | completion(false) 28 | return 29 | } 30 | let controller = LoginViewController.init { (result) in 31 | completion(result) 32 | } 33 | root.present(controller, animated: true) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Demo/Demo/Router/Plugins/RouterLaunchPlugin.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RouterLaunchPlugin.swift 3 | // Demo 4 | // 5 | // Created by 李响 on 2019/4/10. 6 | // Copyright © 2019 swift. All rights reserved. 7 | // 8 | 9 | import Router 10 | 11 | class RouterLaunchPlugin: Plugin { 12 | 13 | private var completion: ((Bool) -> Void)? 14 | 15 | override init() { 16 | super.init() 17 | 18 | NotificationCenter.Delegate.add( 19 | .launched, 20 | observer: self, 21 | selector: #selector(launched) 22 | ) 23 | } 24 | 25 | @objc private func launched() { 26 | completion?(true) 27 | completion = nil 28 | } 29 | 30 | /* 31 | 在准备打开阶段 处理启动状态 32 | 通常在APP启动后 我们需要进行一些必要的操作 例如: 加载必要的配置数据等等, 33 | 这些操作没完成之前就打开了某些页面 是非常危险的. 34 | 这里演示的就是根据启动完成状态延后打开操作, 常见的情景为 外部通过OpenURL启动APP打开某一页面. 35 | */ 36 | override func prepare(open type: RouterType, completion: @escaping (Bool) -> Void) { 37 | guard !AppDelegate.shared.isLaunched else { 38 | completion(true) 39 | return 40 | } 41 | self.completion?(false) 42 | self.completion = completion 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Demo/Demo/Router/Plugins/RouterSinglePlugin.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RouterSinglePlugin.swift 3 | // Route 4 | // 5 | // Created by 李响 on 2019/4/10. 6 | // Copyright © 2019 swift. All rights reserved. 7 | // 8 | 9 | import Router 10 | 11 | protocol RouterSingleable: Routerable { 12 | 13 | /// 当前类型 14 | var single: RouterSingleType { get } 15 | 16 | /// 关闭 17 | /// 18 | /// - Parameters: 19 | /// - single: 即将准备打开的类型 20 | /// - completion: 关闭完成回调 21 | func close(will single: RouterSingleType, completion: @escaping (Bool) -> Void) 22 | } 23 | 24 | class RouterSinglePlugin: Plugin { 25 | 26 | /* 27 | 在准备打开阶段 拦截单一特性页面 28 | (在一些业务场景中 最多只能有一个打开, 不能同时打开的页面的这种特性 简称单一特性) 29 | 30 | 判断当前要打开的类型是否为单一特性类型. 31 | 判断当前是否有相同单一特性的页面已经打开. 32 | 告知原来已经打开的页面即将要打开一个新的页面 请求关闭处理. 33 | */ 34 | override func prepare(open type: RouterType, completion: @escaping (Bool) -> Void) { 35 | guard let single = make(type) else { 36 | completion(true) 37 | return 38 | } 39 | guard let current = current?.object else { 40 | completion(true) 41 | return 42 | } 43 | current.close(will: single, completion: completion) 44 | } 45 | 46 | /* 47 | 在即将打开的方法中记录新的单一特性页面, 以供下一次打开时进行操作 48 | */ 49 | override func will(open type: RouterType, controller: Routerable) { 50 | guard let controller = controller as? Singleable else { 51 | return 52 | } 53 | current = WeakWrapper(controller) 54 | } 55 | } 56 | 57 | extension RouterSinglePlugin { 58 | 59 | typealias Singleable = RouterSingleable 60 | private static var currentWrapper: WeakWrapper? 61 | 62 | private var current: WeakWrapper? { 63 | get { return RouterSinglePlugin.currentWrapper } 64 | set { RouterSinglePlugin.currentWrapper = newValue } 65 | } 66 | 67 | private class WeakWrapper { 68 | weak var object: Singleable? 69 | init(_ object: Singleable?) { 70 | self.object = object 71 | } 72 | } 73 | } 74 | 75 | extension RouterSinglePlugin { 76 | 77 | func make(_ url: RouterType) -> RouterSingleType? { 78 | switch url { 79 | case .open_live: return .live 80 | case .open_fast: return .fast 81 | default: return nil 82 | } 83 | } 84 | } 85 | 86 | enum RouterSingleType { 87 | case live 88 | case fast 89 | } 90 | -------------------------------------------------------------------------------- /Demo/Demo/Router/Router.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Router.swift 3 | // Demo 4 | // 5 | // Created by 李响 on 2019/4/12. 6 | // Copyright © 2019 swift. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | import Router 12 | 13 | enum Router { 14 | 15 | // 初始化Router 并传入需要的插件 16 | 17 | static let router = Provider( 18 | [RouterLaunchPlugin(), 19 | RouterAccountPlugin(), 20 | RouterSinglePlugin()] 21 | ) 22 | } 23 | 24 | extension Router { 25 | 26 | /// 打开 27 | /// 28 | /// - Parameters: 29 | /// - url: url 30 | /// - context: context 31 | /// - Returns: true or false 32 | @discardableResult 33 | static func open(_ url: URLConvertible, 34 | completion: ((Bool) -> Void)? = .none) -> Bool { 35 | return router.open(url, completion: completion) 36 | } 37 | 38 | /// 获取视图控制器 39 | /// 40 | /// - Parameters: 41 | /// - url: url 42 | /// - context: context 43 | /// - Returns: 视图控制器 44 | static func viewController(_ url: URLConvertible) -> UIViewController? { 45 | return router.viewController(url) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Demo/Demo/Router/RouterOpener.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RouterOpener.swift 3 | // Route 4 | // 5 | // Created by 李响 on 2019/4/10. 6 | // Copyright © 2019 swift. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | import SafariServices 12 | import Router 13 | 14 | extension RouterType { 15 | 16 | // 根据类型或URL 返回对应的视图控制器 17 | func controller(url: URLConvertible, values: [String: Any]) -> Routerable? { 18 | switch self { 19 | case .open_http, .open_https: 20 | guard let url = url.urlValue else { return nil } 21 | return SFSafariViewController(url: url) 22 | 23 | case .open_none: 24 | return NoneViewController() 25 | 26 | case .open_live: 27 | guard let id = url.queryParameters["id"] else { return nil } 28 | let controller = LiveViewController() 29 | controller.id = id 30 | return controller 31 | 32 | case .open_fast: 33 | return FastViewController() 34 | 35 | case .open_needlogin: 36 | return NeedLoginViewController() 37 | } 38 | } 39 | 40 | // 根据类型或URL 返回对应的处理 注: 当上面方法因无需要打开的控制器返回时 才会执行. 41 | func handle(url: URLConvertible, values: [String : Any], completion: @escaping (Bool) -> Void) { 42 | completion(true) 43 | } 44 | } 45 | 46 | // 所有需要支持 Router 的视图控制器都需要实现 Routerable 协议 47 | // Routerable 协议默认实现了通用的打开关闭处理逻辑 如无法满足 可重写 48 | 49 | extension NoneViewController: Routerable { } 50 | extension NeedLoginViewController: Routerable { } 51 | 52 | extension SFSafariViewController: Routerable { 53 | 54 | public func open(_ completion: @escaping () -> Void = {}) { 55 | guard let controller = UIViewController.topMost else { 56 | return 57 | } 58 | 59 | controller.present(self, animated: true, completion: completion) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Demo/Demo/Router/RouterType.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RouterType.swift 3 | // Demo 4 | // 5 | // Created by 李响 on 2019/4/10. 6 | // Copyright © 2019 swift. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | import Router 12 | 13 | private let schemes = "router" 14 | 15 | enum RouterType { 16 | case open_http 17 | case open_https 18 | case open_none 19 | case open_live 20 | case open_fast 21 | case open_needlogin 22 | } 23 | 24 | extension RouterType: RouterTypeable { 25 | 26 | // 所有类型注册时使用的URL模板 27 | var pattern: String { 28 | switch self { 29 | case .open_http: return "http://" 30 | case .open_https: return "https://" 31 | case .open_none: return schemes + "://open/none" 32 | case .open_fast: return schemes + "://open/fast" 33 | case .open_live: return schemes + "://open/live" 34 | case .open_needlogin: return schemes + "://open/needlogin" 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Demo/Demo/Utils/Notification.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | extension NotificationCenter { 4 | 5 | enum Delegate: Notificationable { 6 | enum defaultKeys: String { 7 | case launched 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Demo/Demo/Utils/Notificationable.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | protocol Notificationable { 4 | associatedtype defaultKeys: RawRepresentable 5 | } 6 | 7 | extension Notificationable where defaultKeys.RawValue == String { 8 | 9 | static func post(_ name: defaultKeys, object: Any? = nil, userInfo: [AnyHashable: Any]? = nil) { 10 | 11 | NotificationCenter.default.post( 12 | name: conversion(name), 13 | object: object, 14 | userInfo: userInfo 15 | ) 16 | } 17 | 18 | static func add(_ name: defaultKeys, observer: Any, selector: Selector, object: Any? = nil) { 19 | NotificationCenter.default.addObserver( 20 | observer, 21 | selector: selector, 22 | name: conversion(name), 23 | object: object 24 | ) 25 | } 26 | 27 | static func add(_ name: defaultKeys, 28 | _ object: Any? = nil, 29 | queue: OperationQueue = .main, 30 | using block: @escaping (Notification) -> Void) { 31 | NotificationCenter.default.addObserver( 32 | forName: conversion(name), 33 | object: object, 34 | queue: .main, 35 | using: block 36 | ) 37 | } 38 | 39 | static func remove(_ name: defaultKeys, observer: Any, object: Any? = nil) { 40 | NotificationCenter.default.removeObserver( 41 | observer, 42 | name: conversion(name), 43 | object: object 44 | ) 45 | } 46 | 47 | static private func conversion(_ key: defaultKeys) -> NSNotification.Name { 48 | return NSNotification.Name("\(self).\(key.rawValue)") 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Demo/Demo/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // Demo 4 | // 5 | // Created by 李响 on 2019/4/12. 6 | // Copyright © 2019 swift. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ViewController: UIViewController { 12 | 13 | @IBOutlet weak var tableView: UITableView! 14 | 15 | private var list: [String] = [ 16 | "打开http/https链接", 17 | "打开一个普通的页面", 18 | "打开一个需要登录的页面", 19 | "打开一个单一特性的页面 4s后打开另一个", 20 | "打开一个单一特性的页面 4s后打开新页面" 21 | ] 22 | 23 | override func viewDidLoad() { 24 | super.viewDidLoad() 25 | 26 | setup() 27 | } 28 | 29 | private func setup() { 30 | tableView.delegate = self 31 | tableView.dataSource = self 32 | } 33 | } 34 | 35 | extension ViewController: UITableViewDelegate { 36 | 37 | func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { 38 | return 50 39 | } 40 | } 41 | 42 | extension ViewController: UITableViewDataSource { 43 | 44 | func numberOfSections(in tableView: UITableView) -> Int { 45 | return 1 46 | } 47 | 48 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 49 | return list.count 50 | } 51 | 52 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 53 | let cell = tableView.dequeueReusableCell( 54 | withIdentifier: "cell", 55 | for: indexPath 56 | ) 57 | cell.textLabel?.text = list[indexPath.row] 58 | return cell 59 | } 60 | 61 | func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 62 | tableView.deselectRow(at: indexPath, animated: true) 63 | 64 | switch indexPath.row { 65 | case 0: // 打开http/https链接 66 | Router.open("https://www.baidu.com") 67 | 68 | case 1: // 打开一个普通的页面 69 | Router.open("router://open/none") 70 | 71 | case 2: // 打开一个需要登录的页面 72 | Router.open("router://open/needlogin") 73 | 74 | case 3: // 打开一个单一特性的页面 75 | /* 76 | LiveViewController 和 FastViewController 在业务场景中为单一特性 (最多只能有一个打开, 不能同时打开 简称单一特性) 77 | 这里模拟 LiveViewController 打开后 因某种原因要打开 FastViewController 78 | 演示的逻辑为 关闭 LiveViewController 后再打开 FastViewController 79 | */ 80 | Router.open("router://open/live?id=1") 81 | 82 | DispatchQueue.main.asyncAfter(deadline: .now() + 4) { 83 | Router.open("router://open/fast") 84 | } 85 | 86 | case 4: 87 | /* 88 | LiveViewController 在业务场景中为单一特性 (最多只能有一个打开, 不能同时打开 简称单一特性) 89 | 这里模拟 LiveViewController 打开后 因某种原因要打开另一个 LiveViewController 90 | (比如点击某个推送通知 触发了打开一个新的 LiveViewController) 91 | 演示的逻辑为 关闭 LiveViewController 后再打开新的 LiveViewController 92 | */ 93 | Router.open("router://open/live?id=1") 94 | 95 | DispatchQueue.main.asyncAfter(deadline: .now() + 4) { 96 | Router.open("router://open/live?id=1") 97 | } 98 | 99 | default: 100 | break 101 | } 102 | } 103 | } 104 | 105 | -------------------------------------------------------------------------------- /Demo/Podfile: -------------------------------------------------------------------------------- 1 | platform :ios, '12.2' 2 | inhibit_all_warnings! 3 | 4 | target 'Demo' do 5 | use_frameworks! 6 | 7 | pod 'Router', :path => "../" 8 | pod 'SnapKit' , '~>5.0.0' 9 | 10 | end 11 | -------------------------------------------------------------------------------- /Demo/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Router (1.2.0): 3 | - Router/Privacy (= 1.2.0) 4 | - URLNavigator (= 2.5.1) 5 | - Router/Privacy (1.2.0): 6 | - URLNavigator (= 2.5.1) 7 | - SnapKit (5.0.1) 8 | - URLNavigator (2.5.1) 9 | 10 | DEPENDENCIES: 11 | - Router (from `../`) 12 | - SnapKit (~> 5.0.0) 13 | 14 | SPEC REPOS: 15 | trunk: 16 | - SnapKit 17 | - URLNavigator 18 | 19 | EXTERNAL SOURCES: 20 | Router: 21 | :path: "../" 22 | 23 | SPEC CHECKSUMS: 24 | Router: 549a4a08e2a2b3090052be9f9259e43e2c80424c 25 | SnapKit: 97b92857e3df3a0c71833cce143274bf6ef8e5eb 26 | URLNavigator: e9c0426ba6e6ac57f34d018bbf3df840797f984d 27 | 28 | PODFILE CHECKSUM: d658b085fbfbd40a614dbf87b6d4bd19177d8727 29 | 30 | COCOAPODS: 1.15.2 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 LEE 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | 2 | platform :ios, '11.0' 3 | inhibit_all_warnings! 4 | 5 | target 'Router' do 6 | use_frameworks! 7 | 8 | pod 'URLNavigator' , '2.5.1' 9 | 10 | end 11 | -------------------------------------------------------------------------------- /Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - URLNavigator (2.5.1) 3 | 4 | DEPENDENCIES: 5 | - URLNavigator (= 2.5.1) 6 | 7 | SPEC REPOS: 8 | trunk: 9 | - URLNavigator 10 | 11 | SPEC CHECKSUMS: 12 | URLNavigator: e9c0426ba6e6ac57f34d018bbf3df840797f984d 13 | 14 | PODFILE CHECKSUM: 4c39aa2b43555aca7ecdab26f2e3b36bbca6fbdf 15 | 16 | COCOAPODS: 1.15.2 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Router 2 | 3 | ![Swift](https://img.shields.io/badge/Swift-5.0-orange.svg) 4 | 5 | [URLNavigator](https://github.com/devxoul/URLNavigator) abstract routing component written in Swift 6 | 7 | ## Version 2.0 -> [Apis](https://github.com/lixiang1994/Apis) 8 | 9 | ## [天朝子民](README_CN.md) 10 | 11 | ## Features 12 | 13 | - [x] Support for different processing based on plugin mechanism. 14 | - [x] Configuration is independent and easy to manage. 15 | - [x] Good business scalability. 16 | - [x] Safer page management. 17 | - [x] Support for asynchronous completion of callbacks. 18 | 19 | 20 | ## Installation 21 | 22 | Router officially supports CocoaPods only. 23 | 24 | **CocoaPods - Podfile** 25 | 26 | ```ruby 27 | source 'https://github.com/lixiang1994/Specs' 28 | 29 | pod 'Router' 30 | ``` 31 | 32 | ## Usage 33 | 34 | First make sure to import the framework: 35 | 36 | ```swift 37 | import Router 38 | ``` 39 | 40 | Here are some usage examples. All devices are also available as simulators: 41 | 42 | ### Create router 43 | 44 | ```swift 45 | let router = Provider( 46 | [RouterXXXXXXPlugin(), 47 | RouterXXXXXXPlugin(), 48 | RouterXXXXXXPlugin()] 49 | ) 50 | ``` 51 | 52 | ### RouterType 53 | 54 | ```swift 55 | enum RouterType: RouterTypeable { 56 | case open_http 57 | case open_https 58 | case open_xxxx 59 | /* ... */ 60 | } 61 | 62 | extension RouterType { 63 | 64 | var pattern: String { 65 | switch self { 66 | case .open_http: return "http://" 67 | case .open_https: return "https://" 68 | case .open_xxxx: return "xxxx://open/xxxx" 69 | /* ... */ 70 | } 71 | } 72 | 73 | func controller(url: URLConvertible, values: [String: Any]) -> Routerable? { 74 | switch self { 75 | case .open_http, .open_https: 76 | guard let url = url.urlValue else { return nil } 77 | return SFSafariViewController(url: url) 78 | 79 | case .open_xxxx: 80 | return XXXXViewController() 81 | /* ... */ 82 | } 83 | } 84 | 85 | func handle(url: URLConvertible, values: [String : Any], completion: @escaping (Bool) -> Void) { 86 | /* ... */ 87 | completion(true) 88 | } 89 | } 90 | 91 | 92 | extension XXXXViewController: Routerable { } 93 | extension SFSafariViewController: Routerable { } 94 | ``` 95 | 96 | ### Custom plugins 97 | 98 | ```swift 99 | class RouterXXXXPlugin: Plugin { 100 | 101 | override func should(open type: RouterType) -> Bool { 102 | /* ... */ 103 | return true 104 | } 105 | 106 | override func prepare(open type: RouterType, completion: @escaping (Bool) -> Void) { 107 | /* ... */ 108 | completion(true) 109 | } 110 | 111 | override func will(open type: RouterType, controller: Routerable) { 112 | /* ... */ 113 | } 114 | 115 | override func did(open type: RouterType, controller: Routerable) { 116 | /* ... */ 117 | } 118 | } 119 | ``` 120 | 121 | ### Open 122 | 123 | ```swift 124 | // Open page based on type 125 | router.open(.open_xxxx) 126 | 127 | // Open page based on url 128 | router.open("http://xxxxxxxx") 129 | 130 | // Result callback 131 | router.open("http://xxxxxxxx") { (result) in 132 | // Success or failure 133 | } 134 | 135 | ``` 136 | 137 | ## Contributing 138 | 139 | If you have the need for a specific feature that you want implemented or if you experienced a bug, please open an issue. 140 | If you extended the functionality of Router yourself and want others to use it too, please submit a pull request. 141 | 142 | 143 | ## License 144 | 145 | Router is under MIT license. See the [LICENSE](LICENSE) file for more info. 146 | -------------------------------------------------------------------------------- /README_CN.md: -------------------------------------------------------------------------------- 1 | # Router 2 | 3 | ![Swift](https://img.shields.io/badge/Swift-5.0-orange.svg) 4 | 5 | [URLNavigator](https://github.com/devxoul/URLNavigator) 抽象路由组件 6 | 7 | ## 2.0版本移至 [Apis](https://github.com/lixiang1994/Apis) 8 | 9 | ## Features 10 | 11 | - [x] 支持基于插件机制的不同处理 如登录拦截等. 12 | - [x] 配置独立且易于管理. 13 | - [x] 良好的业务可扩展性. 14 | - [x] 安全的页面管理. 15 | - [x] 支持异步完成结果回调. 16 | 17 | 18 | ## 安装 19 | 20 | Router 仅支持CocoaPods. 21 | 22 | **CocoaPods - Podfile** 23 | 24 | ```ruby 25 | source 'https://github.com/lixiang1994/Specs' 26 | 27 | pod 'Router' 28 | ``` 29 | 30 | ## 使用 31 | 32 | 首先导入framework: 33 | 34 | ```swift 35 | import Router 36 | ``` 37 | 38 | 下面是一些简单示例. 支持所有设备和模拟器: 39 | 40 | ### 创建 router 41 | 42 | ```swift 43 | let router = Provider( 44 | [RouterXXXXXXPlugin(), 45 | RouterXXXXXXPlugin(), 46 | RouterXXXXXXPlugin()] 47 | ) 48 | ``` 49 | 50 | ### RouterType 51 | 52 | ```swift 53 | // 可以通过枚举声明所有类型 54 | enum RouterType: RouterTypeable { 55 | case open_http 56 | case open_https 57 | case open_xxxx 58 | /* ... */ 59 | } 60 | 61 | extension RouterType { 62 | 63 | var pattern: String { 64 | switch self { 65 | case .open_http: return "http://" 66 | case .open_https: return "https://" 67 | case .open_xxxx: return "xxxx://open/xxxx" 68 | /* ... */ 69 | } 70 | } 71 | 72 | // 视图控制器获取 当路由打开某一URL, 会在这个方法内获取对应的视图控制器 73 | func controller(url: URLConvertible, values: [String: Any]) -> Routerable? { 74 | switch self { 75 | case .open_http, .open_https: 76 | guard let url = url.urlValue else { return nil } 77 | return SFSafariViewController(url: url) 78 | 79 | case .open_xxxx: 80 | return XXXXViewController() 81 | /* ... */ 82 | } 83 | } 84 | 85 | // 打开处理 当路由打开某一URL 例如非打开页面类型业务时 上面的方法可以返回空, 则会执行下面方法处理相关的业务 86 | func handle(url: URLConvertible, values: [String : Any], completion: @escaping (Bool) -> Void) { 87 | /* ... */ 88 | completion(true) 89 | } 90 | } 91 | 92 | // 每个支持路由的视图控制器需要实现 Routerable 协议 93 | extension XXXXViewController: Routerable { } 94 | extension SFSafariViewController: Routerable { } 95 | ``` 96 | 97 | ### 自定义插件 98 | 99 | ```swift 100 | // 继承自 Plugin , 重写需要的方法 你可以在整个打开过程中做一切你想做的事情 101 | class RouterXXXXPlugin: Plugin { 102 | 103 | // 能否打开 104 | override func should(open type: RouterType) -> Bool { 105 | /* ... */ 106 | return true 107 | } 108 | 109 | // 准备打开时 110 | override func prepare(open type: RouterType, completion: @escaping (Bool) -> Void) { 111 | /* ... */ 112 | completion(true) 113 | } 114 | 115 | // 即将打开 116 | override func will(open type: RouterType, controller: Routerable) { 117 | /* ... */ 118 | } 119 | 120 | // 已经打开 121 | override func did(open type: RouterType, controller: Routerable) { 122 | /* ... */ 123 | } 124 | } 125 | ``` 126 | 127 | ### 打开 128 | 129 | ```swift 130 | // 根据类型打开页面 131 | router.open(.open_xxxx) 132 | 133 | // 根据URL打开页面 134 | router.open("http://xxxxxxxx") 135 | 136 | // 打开结果回调 打开过程中可能由于各种原因导致打开失败 例如: 这个页面需要登录 但是当前没有登录之类的 137 | router.open("http://xxxxxxxx") { (result) in 138 | // 成功或失败 139 | } 140 | ``` 141 | 142 | ## 贡献 143 | 144 | 如果你需要实现特定功能或遇到错误,请打开issue。 如果你自己扩展了Router的功能并希望其他人也使用它,请提交拉取请求。 145 | 146 | 147 | ## 协议 148 | 149 | Router 使用 MIT 协议. 有关更多信息,请参阅 [LICENSE](LICENSE) 文件. 150 | -------------------------------------------------------------------------------- /Router.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | 3 | s.name = "Router" 4 | s.version = "1.2.1" 5 | s.summary = "基于URLNavigator抽象的路由组件 支持任意类型配置 插件机制" 6 | 7 | s.homepage = "https://github.com/lixiang1994/Router" 8 | 9 | s.license = { :type => "MIT", :file => "LICENSE" } 10 | 11 | s.author = { "LEE" => "18611401994@163.com" } 12 | 13 | s.platform = :ios, "11.0" 14 | 15 | s.source = { :git => "https://github.com/lixiang1994/Router.git", :tag => s.version } 16 | 17 | s.source_files = "Sources/**/*.swift" 18 | 19 | s.requires_arc = true 20 | 21 | s.frameworks = "UIKit", "Foundation" 22 | 23 | s.swift_version = "5.0" 24 | 25 | s.dependency "URLNavigator", "2.5.1" 26 | 27 | 28 | s.subspec 'Privacy' do |ss| 29 | ss.resource_bundles = { 30 | s.name => 'Sources/PrivacyInfo.xcprivacy' 31 | } 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /Router.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 4A0C4CA6D15A356505695BCF /* Pods_Router.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 433DDBBFBEDCE5F52EB5DA5E /* Pods_Router.framework */; }; 11 | C9170B7D2274337000052A4E /* Plugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9170B7C2274337000052A4E /* Plugin.swift */; }; 12 | C9D0289522607AD100B5E061 /* Router.h in Headers */ = {isa = PBXBuildFile; fileRef = C9D0289322607AD100B5E061 /* Router.h */; settings = {ATTRIBUTES = (Public, ); }; }; 13 | C9D0289C22607C9300B5E061 /* Provider.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9D0289B22607C9300B5E061 /* Provider.swift */; }; 14 | C9D0289E22607D3F00B5E061 /* Context.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9D0289D22607D3F00B5E061 /* Context.swift */; }; 15 | C9D028A022607DB100B5E061 /* Protocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9D0289F22607DB100B5E061 /* Protocol.swift */; }; 16 | /* End PBXBuildFile section */ 17 | 18 | /* Begin PBXFileReference section */ 19 | 1914FB7D28826DD9E48B39CC /* Pods-Router.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Router.release.xcconfig"; path = "Target Support Files/Pods-Router/Pods-Router.release.xcconfig"; sourceTree = ""; }; 20 | 21744AB499310C2ECAEBB625 /* Pods-Router.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Router.debug.xcconfig"; path = "Target Support Files/Pods-Router/Pods-Router.debug.xcconfig"; sourceTree = ""; }; 21 | 433DDBBFBEDCE5F52EB5DA5E /* Pods_Router.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Router.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 22 | C9170B7C2274337000052A4E /* Plugin.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Plugin.swift; sourceTree = ""; }; 23 | C9D0289022607AD100B5E061 /* Router.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Router.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 24 | C9D0289322607AD100B5E061 /* Router.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Router.h; sourceTree = ""; }; 25 | C9D0289422607AD100B5E061 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 26 | C9D0289B22607C9300B5E061 /* Provider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Provider.swift; sourceTree = ""; }; 27 | C9D0289D22607D3F00B5E061 /* Context.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Context.swift; sourceTree = ""; }; 28 | C9D0289F22607DB100B5E061 /* Protocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Protocol.swift; sourceTree = ""; }; 29 | /* End PBXFileReference section */ 30 | 31 | /* Begin PBXFrameworksBuildPhase section */ 32 | C9D0288D22607AD100B5E061 /* Frameworks */ = { 33 | isa = PBXFrameworksBuildPhase; 34 | buildActionMask = 2147483647; 35 | files = ( 36 | 4A0C4CA6D15A356505695BCF /* Pods_Router.framework in Frameworks */, 37 | ); 38 | runOnlyForDeploymentPostprocessing = 0; 39 | }; 40 | /* End PBXFrameworksBuildPhase section */ 41 | 42 | /* Begin PBXGroup section */ 43 | 16AF8AD01863515273703E24 /* Pods */ = { 44 | isa = PBXGroup; 45 | children = ( 46 | 21744AB499310C2ECAEBB625 /* Pods-Router.debug.xcconfig */, 47 | 1914FB7D28826DD9E48B39CC /* Pods-Router.release.xcconfig */, 48 | ); 49 | path = Pods; 50 | sourceTree = ""; 51 | }; 52 | 335179C6A7819FE55E984E38 /* Frameworks */ = { 53 | isa = PBXGroup; 54 | children = ( 55 | 433DDBBFBEDCE5F52EB5DA5E /* Pods_Router.framework */, 56 | ); 57 | name = Frameworks; 58 | sourceTree = ""; 59 | }; 60 | C9D0288622607AD100B5E061 = { 61 | isa = PBXGroup; 62 | children = ( 63 | C9D0289222607AD100B5E061 /* Sources */, 64 | C9D0289122607AD100B5E061 /* Products */, 65 | 16AF8AD01863515273703E24 /* Pods */, 66 | 335179C6A7819FE55E984E38 /* Frameworks */, 67 | ); 68 | sourceTree = ""; 69 | }; 70 | C9D0289122607AD100B5E061 /* Products */ = { 71 | isa = PBXGroup; 72 | children = ( 73 | C9D0289022607AD100B5E061 /* Router.framework */, 74 | ); 75 | name = Products; 76 | sourceTree = ""; 77 | }; 78 | C9D0289222607AD100B5E061 /* Sources */ = { 79 | isa = PBXGroup; 80 | children = ( 81 | C9D0289322607AD100B5E061 /* Router.h */, 82 | C9D0289422607AD100B5E061 /* Info.plist */, 83 | C9D0289B22607C9300B5E061 /* Provider.swift */, 84 | C9D0289D22607D3F00B5E061 /* Context.swift */, 85 | C9D0289F22607DB100B5E061 /* Protocol.swift */, 86 | C9170B7C2274337000052A4E /* Plugin.swift */, 87 | ); 88 | path = Sources; 89 | sourceTree = ""; 90 | }; 91 | /* End PBXGroup section */ 92 | 93 | /* Begin PBXHeadersBuildPhase section */ 94 | C9D0288B22607AD100B5E061 /* Headers */ = { 95 | isa = PBXHeadersBuildPhase; 96 | buildActionMask = 2147483647; 97 | files = ( 98 | C9D0289522607AD100B5E061 /* Router.h in Headers */, 99 | ); 100 | runOnlyForDeploymentPostprocessing = 0; 101 | }; 102 | /* End PBXHeadersBuildPhase section */ 103 | 104 | /* Begin PBXNativeTarget section */ 105 | C9D0288F22607AD100B5E061 /* Router */ = { 106 | isa = PBXNativeTarget; 107 | buildConfigurationList = C9D0289822607AD100B5E061 /* Build configuration list for PBXNativeTarget "Router" */; 108 | buildPhases = ( 109 | AB82C90F7CD49468A07CA3C8 /* [CP] Check Pods Manifest.lock */, 110 | C9D0288B22607AD100B5E061 /* Headers */, 111 | C9D0288C22607AD100B5E061 /* Sources */, 112 | C9D0288D22607AD100B5E061 /* Frameworks */, 113 | C9D0288E22607AD100B5E061 /* Resources */, 114 | ); 115 | buildRules = ( 116 | ); 117 | dependencies = ( 118 | ); 119 | name = Router; 120 | productName = SwiftRouter; 121 | productReference = C9D0289022607AD100B5E061 /* Router.framework */; 122 | productType = "com.apple.product-type.framework"; 123 | }; 124 | /* End PBXNativeTarget section */ 125 | 126 | /* Begin PBXProject section */ 127 | C9D0288722607AD100B5E061 /* Project object */ = { 128 | isa = PBXProject; 129 | attributes = { 130 | LastUpgradeCheck = 1020; 131 | ORGANIZATIONNAME = swift; 132 | TargetAttributes = { 133 | C9D0288F22607AD100B5E061 = { 134 | CreatedOnToolsVersion = 10.2; 135 | LastSwiftMigration = 1020; 136 | }; 137 | }; 138 | }; 139 | buildConfigurationList = C9D0288A22607AD100B5E061 /* Build configuration list for PBXProject "Router" */; 140 | compatibilityVersion = "Xcode 9.3"; 141 | developmentRegion = en; 142 | hasScannedForEncodings = 0; 143 | knownRegions = ( 144 | en, 145 | ); 146 | mainGroup = C9D0288622607AD100B5E061; 147 | productRefGroup = C9D0289122607AD100B5E061 /* Products */; 148 | projectDirPath = ""; 149 | projectRoot = ""; 150 | targets = ( 151 | C9D0288F22607AD100B5E061 /* Router */, 152 | ); 153 | }; 154 | /* End PBXProject section */ 155 | 156 | /* Begin PBXResourcesBuildPhase section */ 157 | C9D0288E22607AD100B5E061 /* Resources */ = { 158 | isa = PBXResourcesBuildPhase; 159 | buildActionMask = 2147483647; 160 | files = ( 161 | ); 162 | runOnlyForDeploymentPostprocessing = 0; 163 | }; 164 | /* End PBXResourcesBuildPhase section */ 165 | 166 | /* Begin PBXShellScriptBuildPhase section */ 167 | AB82C90F7CD49468A07CA3C8 /* [CP] Check Pods Manifest.lock */ = { 168 | isa = PBXShellScriptBuildPhase; 169 | buildActionMask = 2147483647; 170 | files = ( 171 | ); 172 | inputFileListPaths = ( 173 | ); 174 | inputPaths = ( 175 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 176 | "${PODS_ROOT}/Manifest.lock", 177 | ); 178 | name = "[CP] Check Pods Manifest.lock"; 179 | outputFileListPaths = ( 180 | ); 181 | outputPaths = ( 182 | "$(DERIVED_FILE_DIR)/Pods-Router-checkManifestLockResult.txt", 183 | ); 184 | runOnlyForDeploymentPostprocessing = 0; 185 | shellPath = /bin/sh; 186 | 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"; 187 | showEnvVarsInLog = 0; 188 | }; 189 | /* End PBXShellScriptBuildPhase section */ 190 | 191 | /* Begin PBXSourcesBuildPhase section */ 192 | C9D0288C22607AD100B5E061 /* Sources */ = { 193 | isa = PBXSourcesBuildPhase; 194 | buildActionMask = 2147483647; 195 | files = ( 196 | C9170B7D2274337000052A4E /* Plugin.swift in Sources */, 197 | C9D0289C22607C9300B5E061 /* Provider.swift in Sources */, 198 | C9D0289E22607D3F00B5E061 /* Context.swift in Sources */, 199 | C9D028A022607DB100B5E061 /* Protocol.swift in Sources */, 200 | ); 201 | runOnlyForDeploymentPostprocessing = 0; 202 | }; 203 | /* End PBXSourcesBuildPhase section */ 204 | 205 | /* Begin XCBuildConfiguration section */ 206 | C9D0289622607AD100B5E061 /* Debug */ = { 207 | isa = XCBuildConfiguration; 208 | buildSettings = { 209 | ALWAYS_SEARCH_USER_PATHS = NO; 210 | CLANG_ANALYZER_NONNULL = YES; 211 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 212 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 213 | CLANG_CXX_LIBRARY = "libc++"; 214 | CLANG_ENABLE_MODULES = YES; 215 | CLANG_ENABLE_OBJC_ARC = YES; 216 | CLANG_ENABLE_OBJC_WEAK = YES; 217 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 218 | CLANG_WARN_BOOL_CONVERSION = YES; 219 | CLANG_WARN_COMMA = YES; 220 | CLANG_WARN_CONSTANT_CONVERSION = YES; 221 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 222 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 223 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 224 | CLANG_WARN_EMPTY_BODY = YES; 225 | CLANG_WARN_ENUM_CONVERSION = YES; 226 | CLANG_WARN_INFINITE_RECURSION = YES; 227 | CLANG_WARN_INT_CONVERSION = YES; 228 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 229 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 230 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 231 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 232 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 233 | CLANG_WARN_STRICT_PROTOTYPES = YES; 234 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 235 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 236 | CLANG_WARN_UNREACHABLE_CODE = YES; 237 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 238 | CODE_SIGN_IDENTITY = "iPhone Developer"; 239 | COPY_PHASE_STRIP = NO; 240 | CURRENT_PROJECT_VERSION = 1; 241 | DEBUG_INFORMATION_FORMAT = dwarf; 242 | ENABLE_STRICT_OBJC_MSGSEND = YES; 243 | ENABLE_TESTABILITY = YES; 244 | GCC_C_LANGUAGE_STANDARD = gnu11; 245 | GCC_DYNAMIC_NO_PIC = NO; 246 | GCC_NO_COMMON_BLOCKS = YES; 247 | GCC_OPTIMIZATION_LEVEL = 0; 248 | GCC_PREPROCESSOR_DEFINITIONS = ( 249 | "DEBUG=1", 250 | "$(inherited)", 251 | ); 252 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 253 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 254 | GCC_WARN_UNDECLARED_SELECTOR = YES; 255 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 256 | GCC_WARN_UNUSED_FUNCTION = YES; 257 | GCC_WARN_UNUSED_VARIABLE = YES; 258 | IPHONEOS_DEPLOYMENT_TARGET = 12.2; 259 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 260 | MTL_FAST_MATH = YES; 261 | ONLY_ACTIVE_ARCH = YES; 262 | SDKROOT = iphoneos; 263 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 264 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 265 | VERSIONING_SYSTEM = "apple-generic"; 266 | VERSION_INFO_PREFIX = ""; 267 | }; 268 | name = Debug; 269 | }; 270 | C9D0289722607AD100B5E061 /* Release */ = { 271 | isa = XCBuildConfiguration; 272 | buildSettings = { 273 | ALWAYS_SEARCH_USER_PATHS = NO; 274 | CLANG_ANALYZER_NONNULL = YES; 275 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 276 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 277 | CLANG_CXX_LIBRARY = "libc++"; 278 | CLANG_ENABLE_MODULES = YES; 279 | CLANG_ENABLE_OBJC_ARC = YES; 280 | CLANG_ENABLE_OBJC_WEAK = YES; 281 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 282 | CLANG_WARN_BOOL_CONVERSION = YES; 283 | CLANG_WARN_COMMA = YES; 284 | CLANG_WARN_CONSTANT_CONVERSION = YES; 285 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 286 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 287 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 288 | CLANG_WARN_EMPTY_BODY = YES; 289 | CLANG_WARN_ENUM_CONVERSION = YES; 290 | CLANG_WARN_INFINITE_RECURSION = YES; 291 | CLANG_WARN_INT_CONVERSION = YES; 292 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 293 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 294 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 295 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 296 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 297 | CLANG_WARN_STRICT_PROTOTYPES = YES; 298 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 299 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 300 | CLANG_WARN_UNREACHABLE_CODE = YES; 301 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 302 | CODE_SIGN_IDENTITY = "iPhone Developer"; 303 | COPY_PHASE_STRIP = NO; 304 | CURRENT_PROJECT_VERSION = 1; 305 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 306 | ENABLE_NS_ASSERTIONS = NO; 307 | ENABLE_STRICT_OBJC_MSGSEND = YES; 308 | GCC_C_LANGUAGE_STANDARD = gnu11; 309 | GCC_NO_COMMON_BLOCKS = YES; 310 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 311 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 312 | GCC_WARN_UNDECLARED_SELECTOR = YES; 313 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 314 | GCC_WARN_UNUSED_FUNCTION = YES; 315 | GCC_WARN_UNUSED_VARIABLE = YES; 316 | IPHONEOS_DEPLOYMENT_TARGET = 12.2; 317 | MTL_ENABLE_DEBUG_INFO = NO; 318 | MTL_FAST_MATH = YES; 319 | SDKROOT = iphoneos; 320 | SWIFT_COMPILATION_MODE = wholemodule; 321 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 322 | VALIDATE_PRODUCT = YES; 323 | VERSIONING_SYSTEM = "apple-generic"; 324 | VERSION_INFO_PREFIX = ""; 325 | }; 326 | name = Release; 327 | }; 328 | C9D0289922607AD100B5E061 /* Debug */ = { 329 | isa = XCBuildConfiguration; 330 | baseConfigurationReference = 21744AB499310C2ECAEBB625 /* Pods-Router.debug.xcconfig */; 331 | buildSettings = { 332 | CLANG_ENABLE_MODULES = YES; 333 | CODE_SIGN_IDENTITY = ""; 334 | CODE_SIGN_STYLE = Automatic; 335 | DEFINES_MODULE = YES; 336 | DEPLOYMENT_POSTPROCESSING = NO; 337 | DEVELOPMENT_TEAM = 683UGRW72Z; 338 | DYLIB_COMPATIBILITY_VERSION = 1; 339 | DYLIB_CURRENT_VERSION = 1; 340 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 341 | INFOPLIST_FILE = Sources/Info.plist; 342 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 343 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 344 | LD_RUNPATH_SEARCH_PATHS = ( 345 | "$(inherited)", 346 | "@executable_path/Frameworks", 347 | "@loader_path/Frameworks", 348 | ); 349 | OTHER_CFLAGS = ( 350 | "$(inherited)", 351 | "-isystem", 352 | "\"${PODS_CONFIGURATION_BUILD_DIR}/URLNavigator/URLNavigator.framework/Headers\"", 353 | "-iframework", 354 | "\"${PODS_CONFIGURATION_BUILD_DIR}/URLNavigator\"", 355 | ); 356 | OTHER_CPLUSPLUSFLAGS = "$(OTHER_CFLAGS)"; 357 | PRODUCT_BUNDLE_IDENTIFIER = com.lee.router; 358 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 359 | SKIP_INSTALL = YES; 360 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 361 | SWIFT_VERSION = 5.0; 362 | TARGETED_DEVICE_FAMILY = "1,2"; 363 | }; 364 | name = Debug; 365 | }; 366 | C9D0289A22607AD100B5E061 /* Release */ = { 367 | isa = XCBuildConfiguration; 368 | baseConfigurationReference = 1914FB7D28826DD9E48B39CC /* Pods-Router.release.xcconfig */; 369 | buildSettings = { 370 | CLANG_ENABLE_MODULES = YES; 371 | CODE_SIGN_IDENTITY = ""; 372 | CODE_SIGN_STYLE = Automatic; 373 | DEFINES_MODULE = YES; 374 | DEPLOYMENT_POSTPROCESSING = NO; 375 | DEVELOPMENT_TEAM = 683UGRW72Z; 376 | DYLIB_COMPATIBILITY_VERSION = 1; 377 | DYLIB_CURRENT_VERSION = 1; 378 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 379 | INFOPLIST_FILE = Sources/Info.plist; 380 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 381 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 382 | LD_RUNPATH_SEARCH_PATHS = ( 383 | "$(inherited)", 384 | "@executable_path/Frameworks", 385 | "@loader_path/Frameworks", 386 | ); 387 | OTHER_CFLAGS = ( 388 | "$(inherited)", 389 | "-isystem", 390 | "\"${PODS_CONFIGURATION_BUILD_DIR}/URLNavigator/URLNavigator.framework/Headers\"", 391 | "-iframework", 392 | "\"${PODS_CONFIGURATION_BUILD_DIR}/URLNavigator\"", 393 | ); 394 | OTHER_CPLUSPLUSFLAGS = "$(OTHER_CFLAGS)"; 395 | PRODUCT_BUNDLE_IDENTIFIER = com.lee.router; 396 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 397 | SKIP_INSTALL = YES; 398 | SWIFT_VERSION = 5.0; 399 | TARGETED_DEVICE_FAMILY = "1,2"; 400 | }; 401 | name = Release; 402 | }; 403 | /* End XCBuildConfiguration section */ 404 | 405 | /* Begin XCConfigurationList section */ 406 | C9D0288A22607AD100B5E061 /* Build configuration list for PBXProject "Router" */ = { 407 | isa = XCConfigurationList; 408 | buildConfigurations = ( 409 | C9D0289622607AD100B5E061 /* Debug */, 410 | C9D0289722607AD100B5E061 /* Release */, 411 | ); 412 | defaultConfigurationIsVisible = 0; 413 | defaultConfigurationName = Release; 414 | }; 415 | C9D0289822607AD100B5E061 /* Build configuration list for PBXNativeTarget "Router" */ = { 416 | isa = XCConfigurationList; 417 | buildConfigurations = ( 418 | C9D0289922607AD100B5E061 /* Debug */, 419 | C9D0289A22607AD100B5E061 /* Release */, 420 | ); 421 | defaultConfigurationIsVisible = 0; 422 | defaultConfigurationName = Release; 423 | }; 424 | /* End XCConfigurationList section */ 425 | }; 426 | rootObject = C9D0288722607AD100B5E061 /* Project object */; 427 | } 428 | -------------------------------------------------------------------------------- /Router.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Router.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Router.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Router.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Sources/Context.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Context.swift 3 | // ┌─┐ ┌───────┐ ┌───────┐ 4 | // │ │ │ ┌─────┘ │ ┌─────┘ 5 | // │ │ │ └─────┐ │ └─────┐ 6 | // │ │ │ ┌─────┘ │ ┌─────┘ 7 | // │ └─────┐│ └─────┐ │ └─────┐ 8 | // └───────┘└───────┘ └───────┘ 9 | // 10 | // Created by lee on 2019/4/1. 11 | // Copyright © 2019年 lee. All rights reserved. 12 | // 13 | 14 | import Foundation 15 | 16 | struct Context { 17 | let callback: (Bool) -> Void 18 | 19 | init(_ completion: @escaping (Bool) -> Void = { _ in }) { 20 | callback = completion 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Sources/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | 22 | 23 | -------------------------------------------------------------------------------- /Sources/Plugin.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Plugin.swift 3 | // ┌─┐ ┌───────┐ ┌───────┐ 4 | // │ │ │ ┌─────┘ │ ┌─────┘ 5 | // │ │ │ └─────┐ │ └─────┐ 6 | // │ │ │ ┌─────┘ │ ┌─────┘ 7 | // │ └─────┐│ └─────┐ │ └─────┐ 8 | // └───────┘└───────┘ └───────┘ 9 | // 10 | // Created by lee on 2019/4/27. 11 | // Copyright © 2019年 lee. All rights reserved. 12 | // 13 | import Foundation 14 | 15 | open class Plugin: RouterPluginable { 16 | 17 | public init() { 18 | 19 | } 20 | 21 | open func should(open type: T) -> Bool { 22 | return true 23 | } 24 | 25 | open func prepare(open type: T, completion: @escaping (Bool) -> Void) { 26 | completion(true) 27 | } 28 | 29 | open func will(open type: T, controller: Routerable) { 30 | } 31 | 32 | open func did(open type: T, controller: Routerable) { 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Sources/PrivacyInfo.xcprivacy: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NSPrivacyTracking 6 | 7 | NSPrivacyAccessedAPITypes 8 | 9 | NSPrivacyTrackingDomains 10 | 11 | NSPrivacyCollectedDataTypes 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /Sources/Protocol.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Protocol.swift 3 | // ┌─┐ ┌───────┐ ┌───────┐ 4 | // │ │ │ ┌─────┘ │ ┌─────┘ 5 | // │ │ │ └─────┐ │ └─────┐ 6 | // │ │ │ ┌─────┘ │ ┌─────┘ 7 | // │ └─────┐│ └─────┐ │ └─────┐ 8 | // └───────┘└───────┘ └───────┘ 9 | // 10 | // Created by lee on 2019/4/1. 11 | // Copyright © 2019年 lee. All rights reserved. 12 | // 13 | 14 | import Foundation 15 | import UIKit 16 | 17 | public protocol RouterPluginable { 18 | 19 | associatedtype T 20 | 21 | /// 是否可以打开 22 | /// 23 | /// - Parameter url: 类型 24 | /// - Returns: true or false 25 | func should(open type: T) -> Bool 26 | 27 | /// 准备打开 28 | /// 29 | /// - Parameters: 30 | /// - url: 类型 31 | /// - completion: 准备完成回调 (无论结果如何必须回调) 32 | func prepare(open type: T, completion: @escaping (Bool) -> Void) 33 | 34 | /// 即将打开 35 | /// 36 | /// - Parameters: 37 | /// - type: 类型 38 | /// - controller: 视图控制器 39 | func will(open type: T, controller: Routerable) 40 | 41 | /// 已经打开 42 | /// 43 | /// - Parameters: 44 | /// - type: 类型 45 | /// - controller: 视图控制器 46 | func did(open type: T, controller: Routerable) 47 | } 48 | 49 | public protocol RouterTypeable: CaseIterable { 50 | 51 | /// 模板 用于注册 例如: xxx://open/ 52 | var pattern: String { get } 53 | 54 | /// 打开控制器 55 | /// 56 | /// - Parameters: 57 | /// - url: url 58 | /// - values: 参数值 59 | /// - Returns: 实现了 Routerable 协议的视图控制器 如果返回为空则会调用下面打开处理方法 60 | func controller(url: URLConvertible, values: [String: Any]) -> Routerable? 61 | 62 | /// 打开处理 (当无控制器时执行) 63 | /// 64 | /// - Parameters: 65 | /// - url: url 66 | /// - values: 参数值 67 | /// - completion: 处理完成结果回调 *必须调用 68 | func handle(url: URLConvertible, values: [String: Any], completion: @escaping (Bool) -> Void) 69 | } 70 | 71 | public protocol Routerable: UIViewController { 72 | 73 | /// 打开 74 | /// 75 | /// - Parameter completion: 打开完成回调 76 | func open(with completion: @escaping () -> Void) 77 | 78 | /// 关闭 79 | /// 80 | /// - Parameters: 81 | /// - completion: 关闭完成回调 82 | func close(with completion: @escaping () -> Void) 83 | } 84 | 85 | public extension URL { 86 | 87 | func appending(_ params: [String: String]) -> String { 88 | return absoluteString.appending(params) 89 | } 90 | } 91 | 92 | public extension String { 93 | 94 | func appending(_ params: [String: String]) -> String { 95 | return appending(self, params) 96 | } 97 | 98 | func appending(_ url: String, _ params: [String: String]) -> String { 99 | guard var components = URLComponents(string: url) else { 100 | return url 101 | } 102 | 103 | let query = components.percentEncodedQuery ?? "" 104 | let temp = params.compactMap({ 105 | guard !$0.isEmpty, !$1.isEmpty else { return nil } 106 | guard let _ = Foundation.URL(string: $1) else { 107 | let encoded = $1.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? $1 108 | return "\($0)=\(encoded)" 109 | } 110 | 111 | let string = "?!@#$^&%*+,:;='\"`<>()[]{}/\\| " 112 | let character = CharacterSet(charactersIn: string).inverted 113 | let encoded = $1.addingPercentEncoding(withAllowedCharacters: character) ?? $1 114 | return "\($0)=\(encoded)" 115 | }).joined(separator: "&") 116 | components.percentEncodedQuery = query.isEmpty ? temp : query + "&" + temp 117 | return components.url?.absoluteString ?? url 118 | } 119 | } 120 | 121 | extension Routerable { 122 | 123 | public func open(with completion: @escaping () -> Void = {}) { 124 | guard let controller = UIViewController.topMost else { 125 | return 126 | } 127 | 128 | if let navigation = controller as? UINavigationController { 129 | CATransaction.begin() 130 | CATransaction.setCompletionBlock(completion) 131 | navigation.pushViewController(self, animated: true) 132 | CATransaction.commit() 133 | 134 | } else if let navigation = controller.navigationController { 135 | CATransaction.begin() 136 | CATransaction.setCompletionBlock(completion) 137 | navigation.pushViewController(self, animated: true) 138 | CATransaction.commit() 139 | 140 | } else { 141 | let navigation = UINavigationController(rootViewController: self) 142 | controller.present(navigation, animated: true, completion: completion) 143 | } 144 | } 145 | 146 | public func close(with completion: @escaping () -> Void = {}) { 147 | guard 148 | let navigation = navigationController, 149 | navigation.viewControllers.first != self else { 150 | let presenting = presentingViewController ?? self 151 | presenting.dismiss(animated: true, completion: completion) 152 | return 153 | } 154 | guard presentedViewController == nil else { 155 | dismiss(animated: true) { [weak self] in self?.close(with: completion) } 156 | return 157 | } 158 | 159 | func parents(_ controller: UIViewController) -> [UIViewController] { 160 | guard let parent = controller.parent else { 161 | return [controller] 162 | } 163 | return [controller] + parents(parent) 164 | } 165 | 166 | CATransaction.begin() 167 | CATransaction.setCompletionBlock(completion) 168 | if let top = navigation.topViewController, parents(self).contains(top) { 169 | navigation.popViewController(animated: true) 170 | 171 | } else { 172 | let temp = navigation.viewControllers.filter { !parents(self).contains($0) } 173 | navigation.setViewControllers(temp, animated: true) 174 | } 175 | CATransaction.commit() 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /Sources/Provider.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Provider.swift 3 | // ┌─┐ ┌───────┐ ┌───────┐ 4 | // │ │ │ ┌─────┘ │ ┌─────┘ 5 | // │ │ │ └─────┐ │ └─────┐ 6 | // │ │ │ ┌─────┘ │ ┌─────┘ 7 | // │ └─────┐│ └─────┐ │ └─────┐ 8 | // └───────┘└───────┘ └───────┘ 9 | // 10 | // Created by lee on 2019/4/1. 11 | // Copyright © 2019年 lee. All rights reserved. 12 | // 13 | 14 | import Foundation 15 | import UIKit 16 | import URLNavigator 17 | 18 | public typealias URLConvertible = URLNavigator.URLConvertible 19 | 20 | public class Provider { 21 | 22 | typealias ViewControllerFactory = (_ url: URLConvertible, _ values: [String: Any], _ context: Any?) -> Routerable? 23 | 24 | private let navigator: Navigator 25 | private let plugins: [Plugin] 26 | 27 | public init(navigator: Navigator = Navigator(), _ plugins: [Plugin]) { 28 | self.navigator = navigator 29 | self.plugins = plugins 30 | 31 | // 注册处理 32 | T.allCases.forEach { registers($0) } 33 | } 34 | } 35 | 36 | extension Provider { 37 | 38 | /// 打开 39 | /// 40 | /// - Parameters: 41 | /// - url: url 42 | /// - completion: 打开完成回调 43 | /// - Returns: true or false 44 | @discardableResult 45 | public func open(_ url: URLConvertible, 46 | completion: ((Bool) -> Void)? = .none) -> Bool { 47 | return navigator.open(url, context: Context(completion ?? { _ in })) 48 | } 49 | 50 | /// 获取视图控制器 51 | /// 52 | /// - Parameters: 53 | /// - url: url 54 | /// - context: context 55 | /// - Returns: 视图控制器 56 | public func viewController(_ url: URLConvertible, _ context: Any? = nil) -> Routerable? { 57 | return navigator.viewController(for: url, context: context) as? Routerable 58 | } 59 | } 60 | 61 | extension Provider { 62 | 63 | private func handle(_ url: T, _ factory: @escaping URLOpenHandlerFactory) { 64 | navigator.handle(url.pattern) { (url, values, context) -> Bool in 65 | return factory(url, values, context) 66 | } 67 | } 68 | 69 | private func register(_ url: T, _ factory: @escaping ViewControllerFactory) { 70 | navigator.register(url.pattern) { (url, values, context) -> UIViewController? in 71 | return factory(url, values, context) 72 | } 73 | } 74 | } 75 | 76 | extension Provider { 77 | 78 | private func registers(_ type: T) { 79 | self.register(type) { (url, values, context) -> Routerable? in 80 | return type.controller(url: url, values: values) 81 | } 82 | self.handle(type) { [weak self] (url, values, context) -> Bool in 83 | guard let self = self else { return false } 84 | let context = context as? Context 85 | 86 | if self.plugins.isEmpty { 87 | if let controller = self.viewController(url, context) { 88 | controller.open { 89 | context?.callback(true) 90 | } 91 | 92 | } else { 93 | type.handle(url: url, values: values) { (result) in 94 | context?.callback(result) 95 | } 96 | } 97 | 98 | } else { 99 | guard self.plugins.contains(where: { $0.should(open: type) }) else { 100 | return false 101 | } 102 | 103 | var result = true 104 | let total = self.plugins.count 105 | var count = 0 106 | let group = DispatchGroup() 107 | self.plugins.forEach { p in 108 | group.enter() 109 | p.prepare(open: type) { 110 | // 防止插件多次回调 111 | defer { count += 1 } 112 | guard count < total else { return } 113 | 114 | result = $0 ? result : false 115 | group.leave() 116 | } 117 | } 118 | 119 | group.notify(queue: .main) { [weak self] in 120 | guard let self = self else { 121 | context?.callback(false) 122 | return 123 | } 124 | guard result else { 125 | context?.callback(false) 126 | return 127 | } 128 | 129 | if let controller = self.viewController(url, context) { 130 | self.plugins.forEach { 131 | $0.will(open: type, controller: controller) 132 | } 133 | 134 | controller.open { [weak self] in 135 | guard let self = self else { return } 136 | self.plugins.forEach { 137 | $0.did(open: type, controller: controller) 138 | } 139 | context?.callback(true) 140 | } 141 | 142 | } else { 143 | type.handle(url: url, values: values) { (result) in 144 | context?.callback(result) 145 | } 146 | } 147 | } 148 | } 149 | return true 150 | } 151 | } 152 | } 153 | 154 | -------------------------------------------------------------------------------- /Sources/Router.h: -------------------------------------------------------------------------------- 1 | // 2 | // Router.h 3 | // Router 4 | // 5 | // Created by lee on 2019/4/1. 6 | // Copyright © 2019 lee. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for SwiftRouter. 12 | FOUNDATION_EXPORT double SwiftRouterVersionNumber; 13 | 14 | //! Project version string for SwiftRouter. 15 | FOUNDATION_EXPORT const unsigned char SwiftRouterVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | --------------------------------------------------------------------------------