├── .gitattributes ├── .gitignore ├── .jazzy.yaml ├── .swiftformat ├── Example ├── .gitignore ├── Example.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── Podfile ├── Podfile.lock ├── Submodule │ ├── LICENSE │ ├── Source │ │ └── SubViewController.swift │ ├── Submodule.podspec │ └── UserProfileViewController.swift ├── shell.xcodeproj │ ├── project.pbxproj │ └── xcshareddata │ │ └── xcschemes │ │ └── shell.xcscheme └── shell │ ├── AppDelegate.swift │ ├── Assets.xcassets │ ├── AccentColor.colorset │ │ └── Contents.json │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── Contents.json │ ├── Base.lproj │ └── LaunchScreen.storyboard │ ├── ErrorDetailsViewController.swift │ ├── Info.plist │ ├── MainViewController.swift │ ├── RouteErrorHandler.swift │ └── SceneDelegate.swift ├── LICENSE ├── Package.swift ├── README.md ├── Source ├── Core │ ├── PathComponent.swift │ ├── Routable.swift │ ├── Route.swift │ ├── RouteError.swift │ ├── RouteErrorHandling.swift │ ├── RouteInterceptor.swift │ ├── RouteRequest.swift │ ├── RouterParameters.swift │ ├── UIApplication[Router].swift │ ├── UIViewControllerRouter.swift │ ├── URLComponentsConvertible.swift │ └── URLRouter.swift └── Web │ └── WebInterceptor.swift ├── Tests └── UIRouterTests │ └── UIRouterTests.swift ├── UIRouter.podspec ├── docs ├── Classes.html ├── Classes │ ├── RouteRequest.html │ ├── UIViewControllerRouter.html │ ├── URLRouter.html │ └── WebInterceptor.html ├── Enums.html ├── Enums │ ├── PathComponent.html │ └── RouteError.html ├── Extensions.html ├── Extensions │ ├── Array.html │ ├── RouterParameters.html │ ├── String.html │ ├── UIApplication.html │ ├── URL.html │ └── URLComponents.html ├── Protocols.html ├── Protocols │ ├── Routable.html │ ├── RouteBase.html │ ├── RouteErrorHandling.html │ ├── RouteInterceptor.html │ └── URLComponentsConvertible.html ├── Typealiases.html ├── badge.svg ├── css │ ├── highlight.css │ └── jazzy.css ├── docsets │ ├── UIRouter.docset │ │ └── Contents │ │ │ ├── Info.plist │ │ │ └── Resources │ │ │ ├── Documents │ │ │ ├── Classes.html │ │ │ ├── Classes │ │ │ │ ├── RouteRequest.html │ │ │ │ ├── UIViewControllerRouter.html │ │ │ │ ├── URLRouter.html │ │ │ │ └── WebInterceptor.html │ │ │ ├── Enums.html │ │ │ ├── Enums │ │ │ │ ├── PathComponent.html │ │ │ │ └── RouteError.html │ │ │ ├── Extensions.html │ │ │ ├── Extensions │ │ │ │ ├── Array.html │ │ │ │ ├── RouterParameters.html │ │ │ │ ├── String.html │ │ │ │ ├── UIApplication.html │ │ │ │ ├── URL.html │ │ │ │ └── URLComponents.html │ │ │ ├── Protocols.html │ │ │ ├── Protocols │ │ │ │ ├── Routable.html │ │ │ │ ├── RouteBase.html │ │ │ │ ├── RouteErrorHandling.html │ │ │ │ ├── RouteInterceptor.html │ │ │ │ └── URLComponentsConvertible.html │ │ │ ├── Typealiases.html │ │ │ ├── css │ │ │ │ ├── highlight.css │ │ │ │ └── jazzy.css │ │ │ ├── img │ │ │ │ ├── carat.png │ │ │ │ ├── dash.png │ │ │ │ ├── gh.png │ │ │ │ └── spinner.gif │ │ │ ├── index.html │ │ │ ├── js │ │ │ │ ├── jazzy.js │ │ │ │ ├── jazzy.search.js │ │ │ │ ├── jquery.min.js │ │ │ │ ├── lunr.min.js │ │ │ │ └── typeahead.jquery.js │ │ │ └── search.json │ │ │ └── docSet.dsidx │ └── UIRouter.tgz ├── img │ ├── carat.png │ ├── dash.png │ ├── gh.png │ └── spinner.gif ├── index.html ├── js │ ├── jazzy.js │ ├── jazzy.search.js │ ├── jquery.min.js │ ├── lunr.min.js │ └── typeahead.jquery.js ├── search.json └── undocumented.json └── log.png /.gitattributes: -------------------------------------------------------------------------------- 1 | *.rb linguist-language=Swift -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## User settings 6 | xcuserdata/ 7 | 8 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) 9 | *.xcscmblueprint 10 | *.xccheckout 11 | 12 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) 13 | build/ 14 | DerivedData/ 15 | *.moved-aside 16 | *.pbxuser 17 | !default.pbxuser 18 | *.mode1v3 19 | !default.mode1v3 20 | *.mode2v3 21 | !default.mode2v3 22 | *.perspectivev3 23 | !default.perspectivev3 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | 28 | ## App packaging 29 | *.ipa 30 | *.dSYM.zip 31 | *.dSYM 32 | 33 | ## Playgrounds 34 | timeline.xctimeline 35 | playground.xcworkspace 36 | 37 | # Swift Package Manager 38 | # 39 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 40 | Packages/ 41 | Package.pins 42 | Package.resolved 43 | # *.xcodeproj 44 | # 45 | # Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata 46 | # hence it is not needed unless you have added a package configuration file to your project 47 | .swiftpm 48 | 49 | .build/ 50 | 51 | # CocoaPods 52 | # 53 | # We recommend against adding the Pods directory to your .gitignore. However 54 | # you should judge for yourself, the pros and cons are mentioned at: 55 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 56 | # 57 | # Pods/ 58 | # 59 | # Add this line if you want to avoid checking in source code from the Xcode workspace 60 | # *.xcworkspace 61 | 62 | # Carthage 63 | # 64 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 65 | # Carthage/Checkouts 66 | 67 | Carthage/Build/ 68 | 69 | # npm 70 | node_modules/ 71 | package-lock.json 72 | package.json 73 | .config 74 | # Accio dependency management 75 | Dependencies/ 76 | .accio/ 77 | 78 | # fastlane 79 | # 80 | # It is recommended to not store the screenshots in the git repo. 81 | # Instead, use fastlane to re-generate the screenshots whenever they are needed. 82 | # For more information about the recommended setup visit: 83 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 84 | 85 | fastlane/report.xml 86 | fastlane/Preview.html 87 | fastlane/screenshots/**/*.png 88 | fastlane/test_output 89 | 90 | # Code Injection 91 | # 92 | # After new code Injection tools there's a generated folder /iOSInjectionProject 93 | # https://github.com/johnno1962/injectionforxcode 94 | 95 | iOSInjectionProject/ 96 | theme 97 | -------------------------------------------------------------------------------- /.jazzy.yaml: -------------------------------------------------------------------------------- 1 | module: UIRouter 2 | sdk: iphone 3 | swift_build_tool: xcodebuild 4 | podspec: UIRouter.podspec 5 | module_version: 0.2.0.alpha 6 | clean: true 7 | theme: theme/fullwidth -------------------------------------------------------------------------------- /.swiftformat: -------------------------------------------------------------------------------- 1 | --allman false 2 | --assetliterals visual-width 3 | --beforemarks 4 | --binarygrouping 4,8 5 | --categorymark "MARK: %c" 6 | --classthreshold 0 7 | --closingparen balanced 8 | --commas always 9 | --conflictmarkers reject 10 | --decimalgrouping 3,6 11 | --elseposition same-line 12 | --emptybraces no-space 13 | --enumthreshold 0 14 | --exponentcase lowercase 15 | --exponentgrouping disabled 16 | --extensionacl on-extension 17 | --extensionlength 0 18 | --extensionmark "MARK: - %t + %c" 19 | --fractiongrouping disabled 20 | --fragment false 21 | --funcattributes preserve 22 | --groupedextension "MARK: %c" 23 | --guardelse auto 24 | --header ignore 25 | --hexgrouping ignore 26 | --hexliteralcase lowercase 27 | --ifdef indent 28 | --importgrouping testable-last 29 | --indent 4 30 | --indentcase true 31 | --lifecycle 32 | --linebreaks lf 33 | --markextensions always 34 | --marktypes always 35 | --maxwidth none 36 | --modifierorder 37 | --nevertrailing 38 | --nospaceoperators 39 | --nowrapoperators 40 | --octalgrouping 4,8 41 | --operatorfunc no-space 42 | --organizetypes class,enum,struct 43 | --patternlet inline 44 | --ranges spaced 45 | --redundanttype inferred 46 | --self init-only 47 | --selfrequired 48 | --semicolons inline 49 | --shortoptionals always 50 | --smarttabs enabled 51 | --stripunusedargs closure-only 52 | --structthreshold 0 53 | --swiftversion 5.3 54 | --tabwidth 2 55 | --trailingclosures 56 | --trimwhitespace always 57 | --typeattributes preserve 58 | --typemark "MARK: - %t" 59 | --varattributes preserve 60 | --voidtype void 61 | --wraparguments preserve 62 | --wrapcollections before-first 63 | --wrapconditions preserve 64 | --wrapparameters preserve 65 | --wrapreturntype preserve 66 | --xcodeindentation disabled 67 | --yodaswap always 68 | --disable andOperator,blankLinesAtStartOfScope,linebreakAtEndOfFile,linebreaks,redundantBackticks,redundantLetError,spaceInsideBraces,spaceInsideGenerics,spaceInsideParens,trailingClosures,trailingCommas,wrap,wrapMultilineStatementBraces 69 | --enable isEmpty 70 | -------------------------------------------------------------------------------- /Example/.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## User settings 6 | xcuserdata/ 7 | 8 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) 9 | *.xcscmblueprint 10 | *.xccheckout 11 | 12 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) 13 | build/ 14 | DerivedData/ 15 | *.moved-aside 16 | *.pbxuser 17 | !default.pbxuser 18 | *.mode1v3 19 | !default.mode1v3 20 | *.mode2v3 21 | !default.mode2v3 22 | *.perspectivev3 23 | !default.perspectivev3 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | 28 | ## App packaging 29 | *.ipa 30 | *.dSYM.zip 31 | *.dSYM 32 | 33 | ## Playgrounds 34 | timeline.xctimeline 35 | playground.xcworkspace 36 | 37 | # Swift Package Manager 38 | # 39 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 40 | Packages/ 41 | Package.pins 42 | Package.resolved 43 | # *.xcodeproj 44 | # 45 | # Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata 46 | # hence it is not needed unless you have added a package configuration file to your project 47 | .swiftpm 48 | 49 | .build/ 50 | 51 | # CocoaPods 52 | # 53 | # We recommend against adding the Pods directory to your .gitignore. However 54 | # you should judge for yourself, the pros and cons are mentioned at: 55 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 56 | # 57 | Pods/ 58 | # 59 | # Add this line if you want to avoid checking in source code from the Xcode workspace 60 | # *.xcworkspace 61 | 62 | # Carthage 63 | # 64 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 65 | # Carthage/Checkouts 66 | 67 | Carthage/Build/ 68 | 69 | # npm 70 | node_modules/ 71 | package-lock.json 72 | package.json 73 | .config 74 | # Accio dependency management 75 | Dependencies/ 76 | .accio/ 77 | 78 | # fastlane 79 | # 80 | # It is recommended to not store the screenshots in the git repo. 81 | # Instead, use fastlane to re-generate the screenshots whenever they are needed. 82 | # For more information about the recommended setup visit: 83 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 84 | 85 | fastlane/report.xml 86 | fastlane/Preview.html 87 | fastlane/screenshots/**/*.png 88 | fastlane/test_output 89 | 90 | # Code Injection 91 | # 92 | # After new code Injection tools there's a generated folder /iOSInjectionProject 93 | # https://github.com/johnno1962/injectionforxcode 94 | 95 | iOSInjectionProject/ 96 | -------------------------------------------------------------------------------- /Example/Example.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Example/Example.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Example/Podfile: -------------------------------------------------------------------------------- 1 | platform :ios, '14.5' 2 | inhibit_all_warnings! 3 | install! 'cocoapods', 4 | 5 | :generate_multiple_pod_projects => true, 6 | :incremental_installation => true, 7 | :preserve_pod_file_structure => true, 8 | :warn_for_unused_master_specs_repo => false 9 | 10 | project 'shell.xcodeproj' 11 | workspace 'Example.xcworkspace' 12 | 13 | target 'shell' do 14 | use_frameworks! 15 | pod 'UIRouter/Web', :path => '../' 16 | pod 'Submodule', :path => 'Submodule/' 17 | end 18 | 19 | post_install do |installer| 20 | installer.pod_target_subprojects.flat_map { |p| p.targets }.each do |t| 21 | t.build_configurations.each do |c| 22 | if c.build_settings['IPHONEOS_DEPLOYMENT_TARGET'].to_f < 14.5 23 | c.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '14.5' 24 | end 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /Example/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Submodule (0.1.0): 3 | - UIRouter 4 | - UIRouter (0.2.0.alpha): 5 | - UIRouter/Core (= 0.2.0.alpha) 6 | - UIRouter/Core (0.2.0.alpha) 7 | - UIRouter/Web (0.2.0.alpha): 8 | - UIRouter/Core 9 | 10 | DEPENDENCIES: 11 | - Submodule (from `Submodule/`) 12 | - UIRouter/Web (from `../`) 13 | 14 | EXTERNAL SOURCES: 15 | Submodule: 16 | :path: Submodule/ 17 | UIRouter: 18 | :path: "../" 19 | 20 | SPEC CHECKSUMS: 21 | Submodule: 4d6c30b294c43d33503985e348909fd7c4c02353 22 | UIRouter: f0fadcc043f620b846aa1feee97ea8803127f27f 23 | 24 | PODFILE CHECKSUM: 5387537adb0879783c29a9e30e3acc9630837f78 25 | 26 | COCOAPODS: 1.10.1 27 | -------------------------------------------------------------------------------- /Example/Submodule/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Wang Xiaolong 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 | -------------------------------------------------------------------------------- /Example/Submodule/Source/SubViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SubViewController.swift 3 | // Submodule 4 | // 5 | // Created by wxlpp on 2021/5/1. 6 | // 7 | 8 | import UIKit 9 | import UIRouter 10 | 11 | public final class SubViewController: UIViewController { 12 | override public func viewDidLoad() { 13 | super.viewDidLoad() 14 | view.backgroundColor = .white 15 | } 16 | 17 | override public func viewWillAppear(_ animated: Bool) { 18 | super.viewWillAppear(animated) 19 | title = "Sub" 20 | } 21 | } 22 | 23 | // MARK: 路由 24 | 25 | extension SubViewController: Routable { 26 | 27 | public static func route(parameters: RouterParameters, object: Any?, completion: @escaping RouteCompletionHandler) { 28 | completion(.success(SubViewController())) 29 | } 30 | 31 | public static var paths: [String] { 32 | ["sub"] 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Example/Submodule/Submodule.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # Be sure to run `pod lib lint UIRouter.podspec' to ensure this is a 3 | # valid spec before submitting. 4 | # 5 | # Any lines starting with a # are optional, but their use is encouraged 6 | # To learn more about a Podspec see https://guides.cocoapods.org/syntax/podspec.html 7 | # 8 | 9 | Pod::Spec.new do |s| 10 | s.name = 'Submodule' 11 | s.version = '0.1.0' 12 | s.summary = 'Submodule for Example.' 13 | s.description = <<-DESC 14 | TODO: Add long description of the pod here. 15 | DESC 16 | 17 | s.homepage = 'https://github.com/wxlpp/Submodule' 18 | s.license = { :type => 'MIT', :file => 'LICENSE' } 19 | s.author = { 'wxlpp' => 'wxlpp91@foxmail.com' } 20 | s.source = { :git => 'https://github.com/wxlpp/Submodule.git', :tag => s.version.to_s } 21 | 22 | s.ios.deployment_target = '9.0' 23 | 24 | s.source_files = 'Source/**/*' 25 | s.ios.frameworks = 'UIKit' 26 | s.dependency 'UIRouter' 27 | end 28 | -------------------------------------------------------------------------------- /Example/Submodule/UserProfileViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UserProfileViewController.swift 3 | // Submodule 4 | // 5 | // Created by wxlpp on 2021/5/1. 6 | // 7 | 8 | import UIKit 9 | import UIRouter 10 | 11 | public final class UserProfileViewController: UIViewController {} 12 | 13 | // MARK: 路由 14 | 15 | extension UserProfileViewController: Routable { 16 | 17 | public static var paths: [String] { 18 | ["user/profile/:id"] 19 | } 20 | 21 | public static func route(parameters: RouterParameters, object: Any?, completion: @escaping RouteCompletionHandler) { 22 | if let userID: String = parameters.get("userID") { 23 | completion(.success(UserProfileViewController())) 24 | } else { 25 | completion(.failure(RouteError.parameterValidationFailed(vcType: Self.self, name: "userID"))) 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Example/shell.xcodeproj/xcshareddata/xcschemes/shell.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 45 | 51 | 52 | 53 | 54 | 60 | 62 | 68 | 69 | 70 | 71 | 73 | 74 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /Example/shell/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // Example 4 | // 5 | // Created by wxlpp on 2021/5/1. 6 | // 7 | 8 | import UIKit 9 | import UIRouter 10 | 11 | @main 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 15 | application.router.autoRegisterIfNeed() 16 | application.router.register(interceptors: [WebInterceptor()]) 17 | application.router.registerErrorHandler(RouteErrorHandler()) 18 | return true 19 | } 20 | 21 | // MARK: UISceneSession Lifecycle 22 | 23 | func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { 24 | UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Example/shell/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Example/shell/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "2x", 6 | "size" : "20x20" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "scale" : "3x", 11 | "size" : "20x20" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "scale" : "2x", 16 | "size" : "29x29" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "scale" : "3x", 21 | "size" : "29x29" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "scale" : "2x", 26 | "size" : "40x40" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "scale" : "3x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "scale" : "2x", 36 | "size" : "60x60" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "scale" : "3x", 41 | "size" : "60x60" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "scale" : "1x", 46 | "size" : "20x20" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "scale" : "2x", 51 | "size" : "20x20" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "scale" : "1x", 56 | "size" : "29x29" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "scale" : "2x", 61 | "size" : "29x29" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "scale" : "1x", 66 | "size" : "40x40" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "scale" : "2x", 71 | "size" : "40x40" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "scale" : "1x", 76 | "size" : "76x76" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "scale" : "2x", 81 | "size" : "76x76" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "scale" : "2x", 86 | "size" : "83.5x83.5" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "scale" : "1x", 91 | "size" : "1024x1024" 92 | } 93 | ], 94 | "info" : { 95 | "author" : "xcode", 96 | "version" : 1 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /Example/shell/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Example/shell/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 | -------------------------------------------------------------------------------- /Example/shell/ErrorDetailsViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ErrorDetailsViewController.swift 3 | // shell 4 | // 5 | // Created by wxlpp on 2021/5/1. 6 | // 7 | 8 | import UIKit 9 | 10 | class ErrorDetailsViewController: UITableViewController { 11 | let error: Error 12 | let errorDetails: [(key: String, value: String)] 13 | 14 | init(error: Error) { 15 | self.error = error 16 | if let localizedError = error as? LocalizedError { 17 | var list: [(key: String, value: String)] = [] 18 | if let errorDescription = localizedError.errorDescription { 19 | list.append(("简述", errorDescription)) 20 | } 21 | if let failureReason = localizedError.failureReason { 22 | list.append(("原因", failureReason)) 23 | } 24 | if let helpAnchor = localizedError.helpAnchor { 25 | list.append(("解决", helpAnchor)) 26 | } 27 | if let recoverySuggestion = localizedError.recoverySuggestion { 28 | list.append(("恢复", recoverySuggestion)) 29 | } 30 | self.errorDetails = list 31 | } else { 32 | self.errorDetails = [("错误", error.localizedDescription)] 33 | } 34 | super.init(style: .insetGrouped) 35 | } 36 | 37 | @available(*, unavailable) 38 | required init?(coder: NSCoder) { 39 | fatalError("init(coder:) has not been implemented") 40 | } 41 | 42 | override func viewWillAppear(_ animated: Bool) { 43 | super.viewWillAppear(animated) 44 | title = "错误信息" 45 | } 46 | 47 | override func viewDidLoad() { 48 | super.viewDidLoad() 49 | tableView.allowsSelection = false 50 | tableView.register(UITableViewCell.classForCoder(), forCellReuseIdentifier: "cell") 51 | } 52 | 53 | override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 54 | errorDetails.count 55 | } 56 | 57 | override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 58 | let cell = tableView.dequeueReusableCell(withIdentifier: "cell")! 59 | var contentConfiguration = UIListContentConfiguration.subtitleCell() 60 | contentConfiguration.text = errorDetails[indexPath.row].key 61 | contentConfiguration.secondaryText = errorDetails[indexPath.row].value 62 | cell.contentConfiguration = contentConfiguration 63 | return cell 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /Example/shell/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 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UIApplicationSceneManifest 24 | 25 | UIApplicationSupportsMultipleScenes 26 | 27 | UISceneConfigurations 28 | 29 | UIWindowSceneSessionRoleApplication 30 | 31 | 32 | UISceneConfigurationName 33 | Default Configuration 34 | UISceneDelegateClassName 35 | $(PRODUCT_MODULE_NAME).SceneDelegate 36 | 37 | 38 | 39 | 40 | UIApplicationSupportsIndirectInputEvents 41 | 42 | UILaunchStoryboardName 43 | LaunchScreen 44 | UIRequiredDeviceCapabilities 45 | 46 | armv7 47 | 48 | UISupportedInterfaceOrientations 49 | 50 | UIInterfaceOrientationPortrait 51 | UIInterfaceOrientationLandscapeLeft 52 | UIInterfaceOrientationLandscapeRight 53 | 54 | UISupportedInterfaceOrientations~ipad 55 | 56 | UIInterfaceOrientationPortrait 57 | UIInterfaceOrientationPortraitUpsideDown 58 | UIInterfaceOrientationLandscapeLeft 59 | UIInterfaceOrientationLandscapeRight 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /Example/shell/MainViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MainViewController.swift 3 | // shell 4 | // 5 | // Created by wxlpp on 2021/5/1. 6 | // 7 | 8 | import UIKit 9 | import UIRouter 10 | class MainViewController: UITableViewController { 11 | let routes = [ 12 | (name: "SubModule", path: "sub"), 13 | (name: "Baidu", path: "https://www.baidu.com"), 14 | (name: "用户简介", path: "user/profile") 15 | ] 16 | 17 | override func viewDidLoad() { 18 | super.viewDidLoad() 19 | tableView.register(UITableViewCell.classForCoder(), forCellReuseIdentifier: "cell") 20 | } 21 | 22 | override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 23 | routes.count 24 | } 25 | 26 | override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 27 | let cell = tableView.dequeueReusableCell(withIdentifier: "cell")! 28 | var contentConfiguration = UIListContentConfiguration.cell() 29 | contentConfiguration.text = routes[indexPath.row].name 30 | cell.contentConfiguration = contentConfiguration 31 | return cell 32 | } 33 | 34 | override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 35 | tableView.deselectRow(at: indexPath, animated: true) 36 | UIApplication.shared.route(url: routes[indexPath.row].path).push() 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Example/shell/RouteErrorHandler.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RouteErrorHandler.swift 3 | // shell 4 | // 5 | // Created by wxlpp on 2021/5/1. 6 | // 7 | 8 | import UIRouter 9 | 10 | class RouteErrorHandler: RouteErrorHandling { 11 | func handleRouteError(_ error: RouteError) { 12 | #if DEBUG 13 | let vc = ErrorDetailsViewController(error: error) 14 | UIApplication.shared.route(viewcontroller: vc).presentWithNavigationController(UINavigationController.self) 15 | #else 16 | debugPrint(error.errorDescription) 17 | #endif 18 | } 19 | 20 | func handleCustomError(_ error: Error) {} 21 | } 22 | -------------------------------------------------------------------------------- /Example/shell/SceneDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SceneDelegate.swift 3 | // Example 4 | // 5 | // Created by wxlpp on 2021/5/1. 6 | // 7 | 8 | import SwiftUI 9 | import UIKit 10 | 11 | class SceneDelegate: UIResponder, UIWindowSceneDelegate { 12 | 13 | var window: UIWindow? 14 | 15 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { 16 | if let windowScene = scene as? UIWindowScene { 17 | let window = UIWindow(windowScene: windowScene) 18 | window.rootViewController = UINavigationController(rootViewController: MainViewController()) 19 | self.window = window 20 | window.makeKeyAndVisible() 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Wang Xiaolong 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 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.4 2 | // 3 | // Package.swift 4 | // Copyright (c) 2021 Wang Xiaolong 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | 24 | import PackageDescription 25 | 26 | let package = Package( 27 | name: "UIRouter", 28 | platforms: [.iOS(.v9)], 29 | products: [ 30 | .library( 31 | name: "UIRouter", 32 | targets: ["UIRouter"] 33 | ), 34 | ], 35 | targets: [ 36 | .target( 37 | name: "UIRouter", path: "Source" 38 | ), 39 | .testTarget( 40 | name: "UIRouterTests", 41 | dependencies: ["UIRouter"] 42 | ), 43 | ], 44 | swiftLanguageVersions: [.v5] 45 | ) 46 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![UIRouter: Router in Swift](https://raw.githubusercontent.com/wxlpp/UIRouter/main/log.png) 2 | [![CocoaPods Compatible](https://img.shields.io/cocoapods/v/UIRouter.svg)](https://img.shields.io/cocoapods/v/UIRouter.svg) 3 | ![Cocoapods](https://img.shields.io/cocoapods/l/UIRouter) 4 | ![Bitbucket open issues](https://img.shields.io/bitbucket/issues/wxlpp/UIRouter) 5 | ![Documentation](https://raw.githubusercontent.com/wxlpp/UIRouter/main/docs/badge.svg) 6 | 7 | UIRouter 是一个用Swift实现的路由解耦框架. 8 | [API文档](https://wxlpp.github.io/UIRouter/) 9 | ## 安装 10 | 11 | ### CocoaPods 12 | 13 | ```ruby 14 | pod 'UIRouter', '~> 0.2.0.alpha' 15 | ``` 16 | 17 | ### Swift Package Manager 18 | 19 | ```swift 20 | dependencies: [ 21 | .package(url: "https://github.com/wxlpp/UIRouter", .upToNextMajor(from: "0.2.0")) 22 | ] 23 | ``` 24 | ## 使用 25 | 26 | ### 页面注册 27 | 28 | ```swift 29 | import UIKit 30 | import UIRouter 31 | 32 | @main 33 | class AppDelegate: UIResponder, UIApplicationDelegate { 34 | 35 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 36 | // 可以在此调用 autoRegisterIfNeed 进行页面预注册,否则会在第一次页面路由发生时进行注册 37 | application.router.autoRegisterIfNeed() 38 | // 注册拦截器,WebInterceptor 是一个默认实现的 htttp 协议拦截器 39 | application.router.register(interceptors: [WebInterceptor()]) 40 | // 注册错误处理器进行错误处理 41 | application.router.registerErrorHandler(RouteErrorHandler()) 42 | return true 43 | } 44 | } 45 | 46 | ``` 47 | 48 | ```swift 49 | import UIKit 50 | import UIRouter 51 | 52 | public final class UserProfileViewController: UIViewController {} 53 | 54 | // MARK: 路由 55 | 56 | extension UserProfileViewController: Routable { 57 | public static var paths: [String] { 58 | ["user/profile/:id"] 59 | } 60 | 61 | public static func route(parameters: RouterParameters, object: Any?, completion: @escaping RouteCompletionHandler) { 62 | if let userID: String = parameters.get("userID") { 63 | completion(.success(UserProfileViewController())) 64 | } else { 65 | completion(.failure(RouteError.parameterValidationFailed(vcType: Self.self, name: "userID"))) 66 | } 67 | } 68 | } 69 | ``` 70 | ### 页面路由 71 | ```swift 72 | UIApplication.shared.route(url: "user/profile/123456?name=wxlpp").push() 73 | UIApplication.shared.route(url: "https://github.com/wxlpp/UIRouter").present() 74 | UIApplication.shared.route(url: "flutter://shop.com/home").presentWithNavigationController(UINavigationController.self) 75 | ``` 76 | ### 错误拦截 77 | ```swift 78 | import UIRouter 79 | 80 | class RouteErrorHandler: RouteErrorHandling { 81 | func handleRouteError(_ error: RouteError) { 82 | #if DEBUG 83 | let vc = ErrorDetailsViewController(error: error) 84 | UIApplication.shared.route(viewcontroller: vc).presentWithNavigationController(UINavigationController.self) 85 | #else 86 | debugPrint(error.errorDescription) 87 | #endif 88 | } 89 | 90 | func handleCustomError(_ error: Error) { 91 | //这里对业务错误信息进行处理,比如用户鉴权失败弹出登录页面 92 | } 93 | } 94 | ``` -------------------------------------------------------------------------------- /Source/Core/PathComponent.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PathComponent.swift 3 | // 4 | // Created by wxlpp on 2021/5/1. 5 | // Copyright © 2021 Wang Xiaolong. All rights reserved. 6 | // 7 | 8 | import Foundation 9 | 10 | public enum PathComponent: ExpressibleByStringLiteral, CustomStringConvertible { 11 | 12 | case constant(String) 13 | 14 | case parameter(String) 15 | 16 | case anything 17 | 18 | case catchall 19 | 20 | public init(stringLiteral value: String) { 21 | if value.hasPrefix(":") { 22 | self = .parameter(.init(value.dropFirst())) 23 | } else if value == ":" { 24 | self = .anything 25 | } else if value == "*" { 26 | self = .catchall 27 | } else { 28 | self = .constant(value) 29 | } 30 | } 31 | 32 | public var description: String { 33 | switch self { 34 | case .anything: return ":" 35 | case .catchall: return "*" 36 | case .parameter(let name): return ":" + name 37 | case .constant(let constant): return constant 38 | } 39 | } 40 | } 41 | 42 | public extension Array where Element == PathComponent { 43 | 44 | var string: String { 45 | map(\.description).joined(separator: "/") 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Source/Core/Routable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Routable.swift 3 | // UIRouter 4 | // 5 | // Created by wxlpp on 2021/5/3. 6 | // Copyright © 2021 Wang Xiaolong. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public protocol RouteBase: UIViewController { 12 | static var paths: [String] { get } 13 | static func routeVC(parameters: RouterParameters, object: Any?, completion: @escaping RouteCompletionHandler) 14 | } 15 | 16 | /// 需要通过路由解耦的 `UIViewController` 实现此协议后,路由中心将会自动完成组件的注册 17 | public protocol Routable: RouteBase { 18 | 19 | /// 路由中心通过路由链接到指定 `UIViewController` 后由此回调进行页面构造 20 | /// - Parameters: 21 | /// - parameters: 由路由 `URL` 解析后传递的参数 22 | /// - object: 业务对象 23 | /// - completion: 验证参数后构造页面通过回调返回结果 24 | static func route(parameters: RouterParameters, object: Any?, completion: @escaping RouteCompletionHandler) 25 | } 26 | 27 | public extension Routable { 28 | static func routeVC(parameters: RouterParameters, object: Any?, completion: @escaping RouteCompletionHandler) { 29 | route(parameters: parameters, object: object) { result in 30 | completion(result.map { $0 as UIViewController }) 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Source/Core/Route.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Route.swift 3 | // 4 | // Created by wxlpp on 2021/5/1. 5 | // Copyright © 2021 Wang Xiaolong. All rights reserved. 6 | // 7 | 8 | import UIKit 9 | 10 | /// 路由结果回调闭包 11 | public typealias RouteCompletionHandler = (Result) -> Void 12 | /// 路由处理闭包 13 | public typealias RouterHandler = (RouterParameters, Any?, @escaping RouteCompletionHandler) -> Void 14 | 15 | struct Route { 16 | 17 | var path: [PathComponent] 18 | 19 | var handler: RouterHandler 20 | 21 | init(path: [PathComponent], handler: @escaping RouterHandler) { 22 | self.path = path 23 | self.handler = handler 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Source/Core/RouteError.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RouteError.swift 3 | // UIRouter 4 | // 5 | // Created by wxlpp on 2021/5/1. 6 | // 7 | 8 | import UIKit 9 | 10 | /// 路由错误 11 | public enum RouteError { 12 | /// 路由地址格式错误 13 | case badURL(url: String) 14 | /// 尝试注册路由但地址已存在 15 | case alreadyExist(url: String) 16 | /// 路由终点页面未注册 17 | case routeDoesNotExist(url: String) 18 | /// 参数验证失败 19 | case parameterValidationFailed(vcType: UIViewController.Type, name: String) 20 | } 21 | 22 | // MARK: - LocalizedError 23 | 24 | extension RouteError: LocalizedError { 25 | 26 | /// 错误信息 27 | public var errorDescription: String? { 28 | switch self { 29 | case .badURL(url: let url): 30 | return "路由失败 路径[\(url)]" 31 | case .alreadyExist(url: let url): 32 | return "路由失败 路径[\(url)]" 33 | case .routeDoesNotExist(url: let url): 34 | return "路由失败 路径[\(url)]" 35 | case .parameterValidationFailed(vcType: let type, name: let name): 36 | return "路由参数验证失败 页面[\(type)] 参数[\(name)]" 37 | } 38 | } 39 | 40 | /// 错误发生原因 41 | public var failureReason: String? { 42 | switch self { 43 | case .badURL: 44 | return "路由地址格式错误" 45 | case .alreadyExist: 46 | return "尝试注册路由但地址已存在" 47 | case .routeDoesNotExist: 48 | return "路由终点页面未注册" 49 | case .parameterValidationFailed(vcType: _, name: let name): 50 | return "未能获取参数[\(name)]" 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Source/Core/RouteErrorHandling.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RouteErrorHandling.swift 3 | // UIRouter 4 | // 5 | // Created by wxlpp on 2021/5/1. 6 | // 7 | 8 | import Foundation 9 | 10 | /// 实现`RouteErrorHandling`完成路由错误的集中处理 11 | public protocol RouteErrorHandling { 12 | 13 | /// 处理所有错误,已有默认实现,无需实现 14 | /// - Parameter error: 任意错误 15 | func handle(error: Error) 16 | 17 | /// 处理路由错误 18 | /// - Parameter error: 一个`RouteError`实例 19 | func handleRouteError(_ error: RouteError) 20 | 21 | /// 处理自定义错误 22 | /// - Parameter error: 用户自定义的错误 23 | func handleCustomError(_ error: Error) 24 | } 25 | 26 | public extension RouteErrorHandling { 27 | func handle(error: Error) { 28 | switch error { 29 | case let rooError as RouteError: 30 | handleRouteError(rooError) 31 | default: 32 | handleCustomError(error) 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Source/Core/RouteInterceptor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RouteInterceptor.swift 3 | // UIRouter 4 | // 5 | // Created by wxlpp on 2021/5/1. 6 | // 7 | 8 | import UIKit 9 | 10 | public protocol RouteInterceptor { 11 | func handle(components: URLComponents, object: Any?, completionHandler: @escaping RouteCompletionHandler) 12 | } 13 | 14 | extension URLRouter: RouteInterceptor where Output == UIViewController { 15 | public func handle(components: URLComponents, object: Any?, completionHandler: @escaping RouteCompletionHandler) { 16 | route(url: components, object: object) { result in 17 | completionHandler(result.map({ $0 as UIViewController?})) 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Source/Core/RouteRequest.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RouteRequest.swift 3 | // UIRouter 4 | // 5 | // Created by wxlpp on 2021/5/2. 6 | // 7 | 8 | import UIKit 9 | 10 | /// 路由请求包装,由此可以进行简便的页面跳转操作. 11 | public final class RouteRequest { 12 | var viewController: UIViewController? 13 | 14 | private let url: URLComponentsConvertible 15 | private let object: Any? 16 | 17 | init(url: URLComponentsConvertible, object: Any?) { 18 | self.url = url 19 | self.object = object 20 | } 21 | 22 | private func getVisibleViewController() -> UIViewController? { 23 | let keyWindow: UIWindow? 24 | if #available(iOS 13.0.0, *) { 25 | keyWindow = UIApplication.shared.connectedScenes 26 | .filter({$0.activationState == .foregroundActive}) 27 | .map({$0 as? UIWindowScene}) 28 | .compactMap({$0}) 29 | .first?.windows 30 | .filter(\.isKeyWindow).first 31 | } else { 32 | keyWindow = UIApplication.shared.keyWindow 33 | } 34 | return keyWindow?.rootViewController?.visibleViewController() 35 | } 36 | 37 | /// 异步获取路由结果页面 38 | /// - Parameter completionHandler: 异步回调 39 | public func asyncGetViewController(_ completionHandler: @escaping RouteCompletionHandler) { 40 | if let vc = viewController { 41 | completionHandler(.success(vc)) 42 | return 43 | } 44 | UIViewControllerRouter.shared.route(url: url, object: object) { result in 45 | switch result { 46 | case .success(let vc): 47 | self.viewController = vc 48 | case .failure(let error): 49 | UIViewControllerRouter.shared.errorHandler?.handle(error: error) 50 | } 51 | completionHandler(result) 52 | } 53 | } 54 | 55 | /// 进行页面跳转 56 | /// - Parameters: 57 | /// - root: 使用这个`UIViewController`的`UINavigationController`进行页面跳转 58 | /// - animated: 是否开启动画 59 | /// - completionHandler: 异步回调结果 60 | public func push(by root: UIViewController? = nil, animated: Bool = true, completionHandler: RouteCompletionHandler? = nil) { 61 | if let vc = viewController { 62 | let root = root ?? getVisibleViewController() 63 | root?.navigationController?.pushViewController(vc, animated: animated) 64 | completionHandler?(.success(vc)) 65 | return 66 | } 67 | asyncGetViewController { result in 68 | switch result { 69 | case .success(let vc): 70 | let root = root ?? self.getVisibleViewController() 71 | root?.navigationController?.pushViewController(vc, animated: animated) 72 | completionHandler?(.success(vc)) 73 | case .failure(let error): 74 | completionHandler?(.failure(error)) 75 | } 76 | } 77 | } 78 | 79 | /// 弹出页面 80 | /// - Parameters: 81 | /// - root: 使用这个`UIViewController`进行弹出 82 | /// - animated: 是否开启动画 83 | /// - completionHandler: 异步回调结果 84 | public func present(by root: UIViewController? = nil, animated: Bool = true, completionHandler: RouteCompletionHandler? = nil) { 85 | if let vc = viewController { 86 | let root = root ?? getVisibleViewController() 87 | root?.present(vc, animated: animated, completion: { 88 | completionHandler?(.success(vc)) 89 | }) 90 | return 91 | } 92 | asyncGetViewController { result in 93 | switch result { 94 | case .success(let vc): 95 | let root = root ?? self.getVisibleViewController() 96 | root?.present(vc, animated: animated, completion: { 97 | completionHandler?(.success(vc)) 98 | }) 99 | case .failure(let error): 100 | completionHandler?(.failure(error)) 101 | } 102 | } 103 | } 104 | 105 | /// 将页面包裹在`UINavigationController`中后弹出 106 | /// - Parameters: 107 | /// - type: `UINavigationController`的类型 108 | /// - root: 使用这个`UIViewController`进行弹出 109 | /// - animated: 是否开启动画 110 | /// - completionHandler: 完成后异步回调 111 | public func presentWithNavigationController(_ type: N.Type, by root: UIViewController? = nil, animated: Bool = true, completionHandler: RouteCompletionHandler? = nil) { 112 | if let vc = viewController { 113 | let root = root ?? getVisibleViewController() 114 | root?.present(type.init(rootViewController: vc), animated: animated, completion: { 115 | completionHandler?(.success(vc)) 116 | }) 117 | return 118 | } 119 | asyncGetViewController { result in 120 | switch result { 121 | case .success(let vc): 122 | let root = root ?? self.getVisibleViewController() 123 | root?.present(type.init(rootViewController: vc), animated: animated, completion: { 124 | completionHandler?(.success(vc)) 125 | }) 126 | case .failure(let error): 127 | completionHandler?(.failure(error)) 128 | } 129 | } 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /Source/Core/RouterParameters.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RouterParameters.swift 3 | // UIRouter 4 | // 5 | // Created by wxlpp on 2021/5/2. 6 | // 7 | 8 | import Foundation 9 | /// 路由参数集合 10 | /// 11 | /// 12 | /// let parameters: RouterParameters = [URLQueryItem(name: "id", value: "0123456"), 13 | /// URLQueryItem(name: "name", value: "wxlpp")] 14 | /// let id: Int! = parameters.get("id") 15 | /// print(id) 16 | /// // Prints "0123456" 17 | /// let name = parameters.get("name", as: String.self) 18 | /// print(name) 19 | /// // Prints "wxlpp" 20 | /// 21 | public typealias RouterParameters = [URLQueryItem] 22 | 23 | public extension RouterParameters { 24 | 25 | /// 将路由参数转为字典集合 26 | func toDictionary() -> [String: String] { 27 | var dic: [String: String] = [:] 28 | for parameter in self { 29 | dic.updateValue(parameter.value ?? "", forKey: parameter.name) 30 | } 31 | return dic 32 | } 33 | 34 | /// 获取 35 | /// - Parameters: 36 | /// - name: 参数名称 37 | /// - : 参数类型,需要符合`LosslessStringConvertible`协议 38 | /// - Returns: 返回找到的参数,不存在返回 nil 39 | func get(_ name: String, as _: T.Type = T.self) -> T? where T: LosslessStringConvertible { 40 | first(where: { $0.name == name })?.value.flatMap(T.init) 41 | } 42 | 43 | /// 添加参数 44 | /// - Parameters: 45 | /// - value: 参数 46 | /// - name: 名称 47 | mutating func set(_ value: T?, to name: String) where T: CustomStringConvertible { 48 | append(URLQueryItem(name: name, value: value?.description)) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Source/Core/UIApplication[Router].swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIApplication[Router].swift 3 | // UIRouter 4 | // 5 | // Created by wxlpp on 2021/5/2. 6 | // 7 | 8 | import UIKit 9 | 10 | public extension UIApplication { 11 | 12 | /// 返回 `UIViewControllerRouter.shared` 13 | var router: UIViewControllerRouter { 14 | .shared 15 | } 16 | 17 | /// 通过链接进行页面跳转 18 | /// - Parameter url: 路由链接 19 | /// - Parameter object: 业务对象,默认为nil 20 | /// - Returns: 返回一个`RouteRequest`实例 21 | @inline(__always) 22 | func route(url: URLComponentsConvertible, object: Any? = nil) -> RouteRequest { 23 | RouteRequest(url: url, object: object) 24 | } 25 | 26 | /// 直接进行页面跳转 27 | /// - Parameter viewcontroller: 想要跳转的`UIViewController`实例 28 | /// - Returns: 返回一个`RouteRequest`实例 29 | func route(viewcontroller: UIViewController) -> RouteRequest { 30 | let router = RouteRequest(url: String(describing: viewcontroller), object: nil) 31 | router.viewController = viewcontroller 32 | return router 33 | } 34 | } 35 | 36 | extension UIViewController { 37 | func visibleViewController() -> UIViewController? { 38 | if let presentedViewController = self.presentedViewController { 39 | return presentedViewController.visibleViewController() 40 | } 41 | if let navigationController = self as? UINavigationController { 42 | return navigationController.visibleViewController 43 | } 44 | if let tabBarController = self as? UITabBarController { 45 | return tabBarController.selectedViewController?.visibleViewController() 46 | } 47 | if isViewLoaded { 48 | return self 49 | } 50 | return nil 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Source/Core/UIViewControllerRouter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIViewControllerRouter.swift 3 | // 4 | // Created by wxlpp on 2021/5/1. 5 | // Copyright © 2021 Wang Xiaolong. All rights reserved. 6 | // 7 | 8 | import UIKit 9 | 10 | /// 一个路由器可以通过解析链接获得一个指定的`UIViewController`实例 11 | public class UIViewControllerRouter { 12 | private var isInitialized = false 13 | 14 | static let shared = UIViewControllerRouter() 15 | private let urlRouter = URLRouter() 16 | private var interceptors: [RouteInterceptor] = [] 17 | private(set) var errorHandler: RouteErrorHandling? 18 | private var lock = pthread_rwlock_t() 19 | 20 | init() { 21 | pthread_rwlock_init(&lock, nil) 22 | interceptors.append(urlRouter) 23 | } 24 | 25 | deinit { 26 | pthread_rwlock_destroy(&self.lock) 27 | } 28 | 29 | /// 如果路由未注册,自动进行注册 30 | public func autoRegisterIfNeed() { 31 | if !isInitialized { 32 | DispatchQueue(label: "com.router.register").async { 33 | self.registerIfNeed() 34 | } 35 | } 36 | } 37 | 38 | /// 注册拦截器 39 | /// - Parameter interceptors: 拦截器数组 40 | public func register(interceptors: [RouteInterceptor]) { 41 | let last = self.interceptors.removeLast() 42 | self.interceptors.append(contentsOf: interceptors) 43 | self.interceptors.append(last) 44 | } 45 | 46 | /// 注册错误处理器 47 | /// - Parameter handler: 一个错误处理器 48 | public func registerErrorHandler(_ handler: RouteErrorHandling) { 49 | errorHandler = handler 50 | } 51 | 52 | func registerIfNeed() { 53 | if isInitialized { 54 | return 55 | } 56 | pthread_rwlock_wrlock(&lock) 57 | let numberOfClasses = Int(objc_getClassList(nil, 0)) 58 | if numberOfClasses > 0 { 59 | let classesPtr = UnsafeMutablePointer.allocate(capacity: numberOfClasses) 60 | let autoreleasingClasses = AutoreleasingUnsafeMutablePointer(classesPtr) 61 | let count = objc_getClassList(autoreleasingClasses, Int32(numberOfClasses)) 62 | assert(numberOfClasses == count) 63 | defer { classesPtr.deallocate() } 64 | for i in 0 ..< numberOfClasses { 65 | let item: AnyClass = classesPtr[i] 66 | if let vcType = item as? RouteBase.Type { 67 | for path in vcType.paths { 68 | try? urlRouter.register(route: path) { parameters, object, completion in 69 | vcType.routeVC(parameters: parameters, object: object, completion: completion) 70 | } 71 | } 72 | } 73 | } 74 | } 75 | isInitialized = true 76 | pthread_rwlock_unlock(&lock) 77 | } 78 | 79 | func handleInterceptor(interceptors: [RouteInterceptor], components: URLComponents, object: Any?, completionHandler: @escaping RouteCompletionHandler) { 80 | if interceptors.isEmpty { 81 | return 82 | } 83 | var interceptors = interceptors 84 | let interceptor = interceptors.removeFirst() 85 | interceptor.handle(components: components, object: object) {[weak self] result in 86 | switch result { 87 | case .success(let vc): 88 | if let vc = vc { 89 | completionHandler(.success(vc)) 90 | } else { 91 | self?.handleInterceptor(interceptors: interceptors, components: components, object: object, completionHandler: completionHandler) 92 | } 93 | case .failure(let error): 94 | completionHandler(.failure(error)) 95 | } 96 | } 97 | } 98 | 99 | func route(url: URLComponentsConvertible, object: Any?, completionHandler: @escaping RouteCompletionHandler) { 100 | do { 101 | let components = try url.asURLComponents() 102 | registerIfNeed() 103 | pthread_rwlock_rdlock(&lock) 104 | handleInterceptor(interceptors: interceptors, components: components, object: object) {[weak self] result in 105 | switch result { 106 | case .success(let vc): 107 | completionHandler(.success(vc)) 108 | case .failure(let error): 109 | completionHandler(.failure(error)) 110 | } 111 | if let self = self { 112 | pthread_rwlock_unlock(&self.lock) 113 | } 114 | } 115 | } catch { 116 | completionHandler(.failure(error)) 117 | } 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /Source/Core/URLComponentsConvertible.swift: -------------------------------------------------------------------------------- 1 | // 2 | // URLComponentsConvertible.swift 3 | // UIRouter 4 | // 5 | // Created by wxlpp on 2021/5/2. 6 | // 7 | 8 | import Foundation 9 | 10 | /// Types adopting the `URLComponentsConvertible` protocol can be used to construct `URLComponents`s, which can then be used to route. 11 | public protocol URLComponentsConvertible { 12 | /// 如果构造成功则返回一个 `URLComponents`,否则抛出异常. 13 | /// 14 | /// - Returns: 构造成功的`URLComponents` 15 | /// - Throws: 一个`RouteError.badURL(url:)` 实例. 16 | func asURLComponents() throws -> URLComponents 17 | } 18 | 19 | extension String: URLComponentsConvertible { 20 | 21 | /// 如果构造成功则返回一个 `URLComponents`,否则抛出异常. 22 | /// 23 | /// - Returns: 构造成功的`URLComponents` 24 | /// - Throws: 一个`RouteError.badURL(url:)` 实例. 25 | public func asURLComponents() throws -> URLComponents { 26 | guard let components = URLComponents(string: trimmingCharacters(in: .whitespaces)) else { 27 | throw RouteError.badURL(url: self) 28 | } 29 | return components 30 | } 31 | } 32 | 33 | extension URL: URLComponentsConvertible { 34 | /// 如果构造成功则返回一个 `URLComponents`,否则抛出异常. 35 | /// 36 | /// - Returns: 构造成功的`URLComponents` 37 | /// - Throws: 一个`RouteError.badURL(url:)` 实例. 38 | public func asURLComponents() throws -> URLComponents { 39 | guard let components = URLComponents(url: self, resolvingAgainstBaseURL: true) else { 40 | throw RouteError.badURL(url: absoluteString) 41 | } 42 | return components 43 | } 44 | } 45 | 46 | extension URLComponents: URLComponentsConvertible { 47 | /// 如果构造成功则返回一个 `URLComponents`,否则抛出异常. 48 | /// 49 | /// - Returns: 构造成功的`URLComponents` 50 | /// - Throws: 一个`RouteError.badURL(url:)` 实例. 51 | public func asURLComponents() throws -> URLComponents { self } 52 | } 53 | -------------------------------------------------------------------------------- /Source/Core/URLRouter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // URLRouter.swift 3 | // 4 | // Created by wxlpp on 2021/5/1. 5 | // Copyright © 2021 Wang Xiaolong. All rights reserved. 6 | // 7 | 8 | import Foundation 9 | 10 | open class URLRouter: CustomStringConvertible { 11 | private var root = Node() 12 | 13 | public init() {} 14 | public func register(route path: String, handler: @escaping RouterHandler) throws { 15 | var current = root 16 | for item in path.split(separator: "/") { 17 | let component = PathComponent(stringLiteral: String(item)) 18 | current = current.buildOrFetchChild(for: component) 19 | } 20 | if current.handler != nil { 21 | throw RouteError.alreadyExist(url: path) 22 | } 23 | 24 | current.handler = handler 25 | } 26 | 27 | open func route(url: URLComponentsConvertible, object: Any?, completionHandler: @escaping RouteCompletionHandler) { 28 | do { 29 | let components = try url.asURLComponents() 30 | var parameters = components.queryItems ?? [] 31 | let pathComponents = components.path.split(separator: "/").map({String($0)}) 32 | var currentNode: Node = root 33 | search: for path in pathComponents { 34 | if let constant = currentNode.constants[path] { 35 | currentNode = constant 36 | continue search 37 | } 38 | 39 | if let (name, parameter) = currentNode.parameter { 40 | parameters.set(path, to: name) 41 | currentNode = parameter 42 | continue search 43 | } 44 | 45 | if let anything = currentNode.anything { 46 | currentNode = anything 47 | continue search 48 | } 49 | 50 | guard let catchallHandle = currentNode.catchall?.handler else { 51 | throw RouteError.routeDoesNotExist(url: components.path) 52 | } 53 | return catchallHandle(parameters, object, completionHandler) 54 | } 55 | guard let handle = currentNode.handler else { 56 | throw RouteError.routeDoesNotExist(url: components.path) 57 | } 58 | return handle(parameters, object, completionHandler) 59 | } catch { 60 | completionHandler(.failure(error)) 61 | } 62 | } 63 | 64 | public var description: String { 65 | root.description 66 | } 67 | } 68 | 69 | extension URLRouter { 70 | final class Node: CustomStringConvertible { 71 | var constants: [String: Node] 72 | var parameter: (String, Node)? 73 | var catchall: Node? 74 | var anything: Node? 75 | var handler: RouterHandler? 76 | 77 | init(handle: RouterHandler? = nil) { 78 | self.handler = handle 79 | self.constants = [String: Node]() 80 | } 81 | 82 | func buildOrFetchChild(for component: PathComponent) -> Node { 83 | switch component { 84 | case .constant(let string): 85 | if let node = constants[string] { 86 | return node 87 | } 88 | let node = Node() 89 | constants[string] = node 90 | return node 91 | case .parameter(let name): 92 | let node: Node 93 | if let (existingName, existingNode) = parameter { 94 | node = existingNode 95 | assert(existingName == name, "Route parameter name mis-match \(existingName) != \(name)") 96 | } else { 97 | node = Node() 98 | parameter = (name, node) 99 | } 100 | return node 101 | case .catchall: 102 | let node: Node 103 | if let fallback = catchall { 104 | node = fallback 105 | } else { 106 | node = Node() 107 | catchall = node 108 | } 109 | return node 110 | case .anything: 111 | let node: Node 112 | if let anything = self.anything { 113 | node = anything 114 | } else { 115 | node = Node() 116 | anything = node 117 | } 118 | return node 119 | } 120 | } 121 | 122 | var description: String { 123 | var desc: [String] = [] 124 | if let (name, parameter) = self.parameter { 125 | desc.append("→ \(name)") 126 | desc.append(parameter.description.indented()) 127 | } 128 | if let catchall = self.catchall { 129 | desc.append("→ *") 130 | desc.append(catchall.description.indented()) 131 | } 132 | if let anything = self.anything { 133 | desc.append("→ :") 134 | desc.append(anything.description.indented()) 135 | } 136 | for (name, constant) in constants { 137 | desc.append("→ \(name)") 138 | desc.append(constant.description.indented()) 139 | } 140 | return desc.joined(separator: "\n") 141 | } 142 | } 143 | } 144 | 145 | private extension String { 146 | func indented() -> String { 147 | split(separator: "\n").map { line in 148 | " " + line 149 | }.joined(separator: "\n") 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /Source/Web/WebInterceptor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WebInterceptor.swift 3 | // UIRouter 4 | // 5 | // Created by wxlpp on 2021/5/1. 6 | // 7 | 8 | import SafariServices 9 | import UIKit 10 | 11 | /// 默认的网页拦截器,可以拦截`http`协议的链接,返回一个`SFSafariViewController` 12 | open class WebInterceptor: RouteInterceptor { 13 | 14 | public init() {} 15 | 16 | /// 收到路由事件后由此进行拦截,可以继承后修改实现. 17 | /// - Parameters: 18 | /// - components: 一个`URLComponents`实例,由路由链接构造. 19 | /// - completionHandler: 拦截后由此进行回调. 20 | open func handle(components: URLComponents, object: Any?, completionHandler: @escaping RouteCompletionHandler) { 21 | if components.scheme?.hasPrefix("http") ?? false { 22 | if let url = components.url { 23 | let vc = SFSafariViewController(url: url) 24 | completionHandler(.success(vc)) 25 | } 26 | } else { 27 | completionHandler(.success(nil)) 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Tests/UIRouterTests/UIRouterTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import UIRouter 3 | 4 | final class UIRouterTests: XCTestCase { 5 | func testExample() {} 6 | } 7 | -------------------------------------------------------------------------------- /UIRouter.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = 'UIRouter' 3 | s.version = '0.2.0.alpha' 4 | s.summary = 'Swift 实现的路由解耦框架.' 5 | s.description = 'Swift 实现的路由解耦框架.' 6 | 7 | s.homepage = 'https://github.com/wxlpp/UIRouter' 8 | s.license = { :type => 'MIT', :file => 'LICENSE' } 9 | s.author = { 'wxlpp' => 'wxlpp91@foxmail.com' } 10 | s.source = { :git => 'https://github.com/wxlpp/UIRouter.git', :tag => s.version.to_s } 11 | 12 | s.ios.deployment_target = '9.0' 13 | s.swift_versions = '5.4' 14 | s.ios.frameworks = 'UIKit' 15 | s.default_subspecs = 'Core' 16 | 17 | s.subspec 'Core' do |ss| 18 | ss.source_files = 'Source/Core/*' 19 | end 20 | 21 | s.subspec 'Web' do |ss| 22 | ss.dependency 'UIRouter/Core' 23 | ss.source_files = 'Source/Web/*' 24 | ss.ios.frameworks = 'SafariServices' 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /docs/Extensions/Array.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Array Extension Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 |

23 | 24 | UIRouter 0.2.0.alpha Docs 25 | 26 | (72% documented) 27 |

28 | 29 |

30 |

31 | 32 |
33 |

34 | 35 | 36 |
37 | 38 | 43 | 44 |
45 | 134 |
135 | 136 |
137 |
138 |

Array

139 |
140 |
141 | 142 |
public extension Array where Element == PathComponent
143 | 144 |
145 |
146 | 147 |
148 |
149 | 150 |
151 |
152 |
153 |
154 | 155 | 156 |
157 | 158 |

Available where Element == PathComponent

159 |

160 |
161 |
162 |
    163 |
  • 164 |
    165 | 166 | 167 | 168 | string 169 | 170 |
    171 |
    172 |
    173 |
    174 |
    175 |
    176 |

    Undocumented

    177 | 178 |
    179 |
    180 |

    Declaration

    181 |
    182 |

    Swift

    183 |
    var string: String { get }
    184 | 185 |
    186 |
    187 |
    188 |
    189 |
  • 190 |
191 |
192 |
193 |
194 | 195 |
196 |
197 | 201 | 202 | 203 | 204 | -------------------------------------------------------------------------------- /docs/Extensions/String.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | String Extension Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 |

23 | 24 | UIRouter 0.2.0.alpha Docs 25 | 26 | (72% documented) 27 |

28 | 29 |

30 |

31 | 32 |
33 |

34 | 35 | 36 |
37 | 38 | 43 | 44 |
45 | 134 |
135 | 136 |
137 |
138 |

String

139 |
140 |
141 | 142 |
extension String: URLComponentsConvertible
143 | 144 |
145 |
146 | 147 |
148 |
149 | 150 |
151 |
152 |
153 |
    154 |
  • 155 |
    156 | 157 | 158 | 159 | asURLComponents() 160 | 161 |
    162 |
    163 |
    164 |
    165 |
    166 |
    167 |

    如果构造成功则返回一个 URLComponents,否则抛出异常.

    168 |
    169 |

    Throws

    170 | 一个RouteError.badURL(url:) 实例. 171 | 172 |
    173 | 174 |
    175 |
    176 |

    Declaration

    177 |
    178 |

    Swift

    179 |
    public func asURLComponents() throws -> URLComponents
    180 | 181 |
    182 |
    183 |
    184 |

    Return Value

    185 |

    构造成功的URLComponents

    186 |
    187 |
    188 |
    189 |
  • 190 |
191 |
192 |
193 |
194 | 195 |
196 |
197 | 201 | 202 | 203 | 204 | -------------------------------------------------------------------------------- /docs/Extensions/URL.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | URL Extension Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 |

23 | 24 | UIRouter 0.2.0.alpha Docs 25 | 26 | (72% documented) 27 |

28 | 29 |

30 |

31 | 32 |
33 |

34 | 35 | 36 |
37 | 38 | 43 | 44 |
45 | 134 |
135 | 136 |
137 |
138 |

URL

139 |
140 |
141 | 142 |
extension URL: URLComponentsConvertible
143 | 144 |
145 |
146 | 147 |
148 |
149 | 150 |
151 |
152 |
153 |
    154 |
  • 155 |
    156 | 157 | 158 | 159 | asURLComponents() 160 | 161 |
    162 |
    163 |
    164 |
    165 |
    166 |
    167 |

    如果构造成功则返回一个 URLComponents,否则抛出异常.

    168 |
    169 |

    Throws

    170 | 一个RouteError.badURL(url:) 实例. 171 | 172 |
    173 | 174 |
    175 |
    176 |

    Declaration

    177 |
    178 |

    Swift

    179 |
    public func asURLComponents() throws -> URLComponents
    180 | 181 |
    182 |
    183 |
    184 |

    Return Value

    185 |

    构造成功的URLComponents

    186 |
    187 |
    188 |
    189 |
  • 190 |
191 |
192 |
193 |
194 | 195 |
196 |
197 | 201 | 202 | 203 | 204 | -------------------------------------------------------------------------------- /docs/Extensions/URLComponents.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | URLComponents Extension Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 |

23 | 24 | UIRouter 0.2.0.alpha Docs 25 | 26 | (72% documented) 27 |

28 | 29 |

30 |

31 | 32 |
33 |

34 | 35 | 36 |
37 | 38 | 43 | 44 |
45 | 134 |
135 | 136 |
137 |
138 |

URLComponents

139 |
140 |
141 | 142 |
extension URLComponents: URLComponentsConvertible
143 | 144 |
145 |
146 | 147 |
148 |
149 | 150 |
151 |
152 |
153 |
    154 |
  • 155 |
    156 | 157 | 158 | 159 | asURLComponents() 160 | 161 |
    162 |
    163 |
    164 |
    165 |
    166 |
    167 |

    如果构造成功则返回一个 URLComponents,否则抛出异常.

    168 |
    169 |

    Throws

    170 | 一个RouteError.badURL(url:) 实例. 171 | 172 |
    173 | 174 |
    175 |
    176 |

    Declaration

    177 |
    178 |

    Swift

    179 |
    public func asURLComponents() throws -> URLComponents
    180 | 181 |
    182 |
    183 |
    184 |

    Return Value

    185 |

    构造成功的URLComponents

    186 |
    187 |
    188 |
    189 |
  • 190 |
191 |
192 |
193 |
194 | 195 |
196 |
197 | 201 | 202 | 203 | 204 | -------------------------------------------------------------------------------- /docs/Protocols/RouteInterceptor.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | RouteInterceptor Protocol Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 |

23 | 24 | UIRouter 0.2.0.alpha Docs 25 | 26 | (72% documented) 27 |

28 | 29 |

30 |

31 | 32 |
33 |

34 | 35 | 36 |
37 | 38 | 43 | 44 |
45 | 134 |
135 | 136 |
137 |
138 |

RouteInterceptor

139 |
140 |
141 | 142 |
public protocol RouteInterceptor
143 | 144 |
145 |
146 |

Undocumented

147 | 148 |
149 |
150 | 151 |
152 |
153 |
154 |
    155 |
  • 156 |
    157 | 158 | 159 | 160 | handle(components:object:completionHandler:) 161 | 162 |
    163 |
    164 |
    165 |
    166 |
    167 |
    168 |

    Undocumented

    169 | 170 |
    171 |
    172 |

    Declaration

    173 |
    174 |

    Swift

    175 |
    func handle(components: URLComponents, object: Any, completionHandler: @escaping RouteCompletionHandler<UIViewController?>)
    176 | 177 |
    178 |
    179 |
    180 |
    181 |
  • 182 |
183 |
184 |
185 |
186 | 187 |
188 |
189 | 193 | 194 | 195 | 196 | -------------------------------------------------------------------------------- /docs/Protocols/URLComponentsConvertible.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | URLComponentsConvertible Protocol Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 |

23 | 24 | UIRouter 0.2.0.alpha Docs 25 | 26 | (72% documented) 27 |

28 | 29 |

30 |

31 | 32 |
33 |

34 | 35 | 36 |
37 | 38 | 43 | 44 |
45 | 134 |
135 | 136 |
137 |
138 |

URLComponentsConvertible

139 |
140 |
141 | 142 |
public protocol URLComponentsConvertible
143 | 144 |
145 |
146 |

Types adopting the URLComponentsConvertible protocol can be used to construct URLComponentss, which can then be used to route.

147 | 148 |
149 |
150 | 151 |
152 |
153 |
154 |
    155 |
  • 156 |
    157 | 158 | 159 | 160 | asURLComponents() 161 | 162 |
    163 |
    164 |
    165 |
    166 |
    167 |
    168 |

    如果构造成功则返回一个 URLComponents,否则抛出异常.

    169 |
    170 |

    Throws

    171 | 一个RouteError.badURL(url:) 实例. 172 | 173 |
    174 | 175 |
    176 |
    177 |

    Declaration

    178 |
    179 |

    Swift

    180 |
    func asURLComponents() throws -> URLComponents
    181 | 182 |
    183 |
    184 |
    185 |

    Return Value

    186 |

    构造成功的URLComponents

    187 |
    188 |
    189 |
    190 |
  • 191 |
192 |
193 |
194 |
195 | 196 |
197 |
198 | 202 | 203 | 204 | 205 | -------------------------------------------------------------------------------- /docs/badge.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | documentation 17 | 18 | 19 | documentation 20 | 21 | 22 | 72% 23 | 24 | 25 | 72% 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /docs/css/highlight.css: -------------------------------------------------------------------------------- 1 | /* Credit to https://gist.github.com/wataru420/2048287 */ 2 | .highlight { 3 | /* Comment */ 4 | /* Error */ 5 | /* Keyword */ 6 | /* Operator */ 7 | /* Comment.Multiline */ 8 | /* Comment.Preproc */ 9 | /* Comment.Single */ 10 | /* Comment.Special */ 11 | /* Generic.Deleted */ 12 | /* Generic.Deleted.Specific */ 13 | /* Generic.Emph */ 14 | /* Generic.Error */ 15 | /* Generic.Heading */ 16 | /* Generic.Inserted */ 17 | /* Generic.Inserted.Specific */ 18 | /* Generic.Output */ 19 | /* Generic.Prompt */ 20 | /* Generic.Strong */ 21 | /* Generic.Subheading */ 22 | /* Generic.Traceback */ 23 | /* Keyword.Constant */ 24 | /* Keyword.Declaration */ 25 | /* Keyword.Pseudo */ 26 | /* Keyword.Reserved */ 27 | /* Keyword.Type */ 28 | /* Literal.Number */ 29 | /* Literal.String */ 30 | /* Name.Attribute */ 31 | /* Name.Builtin */ 32 | /* Name.Class */ 33 | /* Name.Constant */ 34 | /* Name.Entity */ 35 | /* Name.Exception */ 36 | /* Name.Function */ 37 | /* Name.Namespace */ 38 | /* Name.Tag */ 39 | /* Name.Variable */ 40 | /* Operator.Word */ 41 | /* Text.Whitespace */ 42 | /* Literal.Number.Float */ 43 | /* Literal.Number.Hex */ 44 | /* Literal.Number.Integer */ 45 | /* Literal.Number.Oct */ 46 | /* Literal.String.Backtick */ 47 | /* Literal.String.Char */ 48 | /* Literal.String.Doc */ 49 | /* Literal.String.Double */ 50 | /* Literal.String.Escape */ 51 | /* Literal.String.Heredoc */ 52 | /* Literal.String.Interpol */ 53 | /* Literal.String.Other */ 54 | /* Literal.String.Regex */ 55 | /* Literal.String.Single */ 56 | /* Literal.String.Symbol */ 57 | /* Name.Builtin.Pseudo */ 58 | /* Name.Variable.Class */ 59 | /* Name.Variable.Global */ 60 | /* Name.Variable.Instance */ 61 | /* Literal.Number.Integer.Long */ } 62 | .highlight .c { 63 | color: #999988; 64 | font-style: italic; } 65 | .highlight .err { 66 | color: #a61717; 67 | background-color: #e3d2d2; } 68 | .highlight .k { 69 | color: #000000; 70 | font-weight: bold; } 71 | .highlight .o { 72 | color: #000000; 73 | font-weight: bold; } 74 | .highlight .cm { 75 | color: #999988; 76 | font-style: italic; } 77 | .highlight .cp { 78 | color: #999999; 79 | font-weight: bold; } 80 | .highlight .c1 { 81 | color: #999988; 82 | font-style: italic; } 83 | .highlight .cs { 84 | color: #999999; 85 | font-weight: bold; 86 | font-style: italic; } 87 | .highlight .gd { 88 | color: #000000; 89 | background-color: #ffdddd; } 90 | .highlight .gd .x { 91 | color: #000000; 92 | background-color: #ffaaaa; } 93 | .highlight .ge { 94 | color: #000000; 95 | font-style: italic; } 96 | .highlight .gr { 97 | color: #aa0000; } 98 | .highlight .gh { 99 | color: #999999; } 100 | .highlight .gi { 101 | color: #000000; 102 | background-color: #ddffdd; } 103 | .highlight .gi .x { 104 | color: #000000; 105 | background-color: #aaffaa; } 106 | .highlight .go { 107 | color: #888888; } 108 | .highlight .gp { 109 | color: #555555; } 110 | .highlight .gs { 111 | font-weight: bold; } 112 | .highlight .gu { 113 | color: #aaaaaa; } 114 | .highlight .gt { 115 | color: #aa0000; } 116 | .highlight .kc { 117 | color: #000000; 118 | font-weight: bold; } 119 | .highlight .kd { 120 | color: #000000; 121 | font-weight: bold; } 122 | .highlight .kp { 123 | color: #000000; 124 | font-weight: bold; } 125 | .highlight .kr { 126 | color: #000000; 127 | font-weight: bold; } 128 | .highlight .kt { 129 | color: #445588; } 130 | .highlight .m { 131 | color: #009999; } 132 | .highlight .s { 133 | color: #d14; } 134 | .highlight .na { 135 | color: #008080; } 136 | .highlight .nb { 137 | color: #0086B3; } 138 | .highlight .nc { 139 | color: #445588; 140 | font-weight: bold; } 141 | .highlight .no { 142 | color: #008080; } 143 | .highlight .ni { 144 | color: #800080; } 145 | .highlight .ne { 146 | color: #990000; 147 | font-weight: bold; } 148 | .highlight .nf { 149 | color: #990000; } 150 | .highlight .nn { 151 | color: #555555; } 152 | .highlight .nt { 153 | color: #000080; } 154 | .highlight .nv { 155 | color: #008080; } 156 | .highlight .ow { 157 | color: #000000; 158 | font-weight: bold; } 159 | .highlight .w { 160 | color: #bbbbbb; } 161 | .highlight .mf { 162 | color: #009999; } 163 | .highlight .mh { 164 | color: #009999; } 165 | .highlight .mi { 166 | color: #009999; } 167 | .highlight .mo { 168 | color: #009999; } 169 | .highlight .sb { 170 | color: #d14; } 171 | .highlight .sc { 172 | color: #d14; } 173 | .highlight .sd { 174 | color: #d14; } 175 | .highlight .s2 { 176 | color: #d14; } 177 | .highlight .se { 178 | color: #d14; } 179 | .highlight .sh { 180 | color: #d14; } 181 | .highlight .si { 182 | color: #d14; } 183 | .highlight .sx { 184 | color: #d14; } 185 | .highlight .sr { 186 | color: #009926; } 187 | .highlight .s1 { 188 | color: #d14; } 189 | .highlight .ss { 190 | color: #990073; } 191 | .highlight .bp { 192 | color: #999999; } 193 | .highlight .vc { 194 | color: #008080; } 195 | .highlight .vg { 196 | color: #008080; } 197 | .highlight .vi { 198 | color: #008080; } 199 | .highlight .il { 200 | color: #009999; } 201 | -------------------------------------------------------------------------------- /docs/css/jazzy.css: -------------------------------------------------------------------------------- 1 | *, *:before, *:after { 2 | box-sizing: inherit; } 3 | 4 | body { 5 | margin: 0; 6 | background: #fff; 7 | color: #333; 8 | font: 16px/1.7 "Helvetica Neue", Helvetica, Arial, sans-serif; 9 | letter-spacing: .2px; 10 | -webkit-font-smoothing: antialiased; 11 | box-sizing: border-box; } 12 | 13 | h1 { 14 | font-size: 2rem; 15 | font-weight: 700; 16 | margin: 1.275em 0 0.6em; } 17 | 18 | h2 { 19 | font-size: 1.75rem; 20 | font-weight: 700; 21 | margin: 1.275em 0 0.3em; } 22 | 23 | h3 { 24 | font-size: 1.5rem; 25 | font-weight: 700; 26 | margin: 1em 0 0.3em; } 27 | 28 | h4 { 29 | font-size: 1.25rem; 30 | font-weight: 700; 31 | margin: 1.275em 0 0.85em; } 32 | 33 | h5 { 34 | font-size: 1rem; 35 | font-weight: 700; 36 | margin: 1.275em 0 0.85em; } 37 | 38 | h6 { 39 | font-size: 1rem; 40 | font-weight: 700; 41 | margin: 1.275em 0 0.85em; 42 | color: #777; } 43 | 44 | p { 45 | margin: 0 0 1em; } 46 | 47 | ul, ol { 48 | padding: 0 0 0 2em; 49 | margin: 0 0 0.85em; } 50 | 51 | blockquote { 52 | margin: 0 0 0.85em; 53 | padding: 0 15px; 54 | color: #858585; 55 | border-left: 4px solid #e5e5e5; } 56 | 57 | img { 58 | max-width: 100%; } 59 | 60 | a { 61 | color: #1890ff; 62 | text-decoration: none; } 63 | a:hover, a:focus { 64 | outline: 0; 65 | text-decoration: underline; } 66 | a.discouraged { 67 | text-decoration: line-through; } 68 | a.discouraged:hover, a.discouraged:focus { 69 | text-decoration: underline line-through; } 70 | 71 | table { 72 | background: #fff; 73 | width: 100%; 74 | border-collapse: collapse; 75 | border-spacing: 0; 76 | overflow: auto; 77 | margin: 0 0 0.85em; } 78 | 79 | tr:nth-child(2n) { 80 | background-color: #fbfbfb; } 81 | 82 | th, td { 83 | padding: 6px 13px; 84 | border: 1px solid #ddd; } 85 | 86 | pre { 87 | margin: 0 0 1.275em; 88 | padding: .85em 1em; 89 | overflow: auto; 90 | background: #f7f7f7; 91 | font-size: .85em; 92 | font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; } 93 | 94 | code { 95 | font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; } 96 | 97 | .item-container p > code, .item-container li > code, .top-matter p > code, .top-matter li > code { 98 | background: #f7f7f7; 99 | padding: .2em; } 100 | .item-container p > code:before, .item-container p > code:after, .item-container li > code:before, .item-container li > code:after, .top-matter p > code:before, .top-matter p > code:after, .top-matter li > code:before, .top-matter li > code:after { 101 | letter-spacing: -.2em; 102 | content: "\00a0"; } 103 | 104 | pre code { 105 | padding: 0; 106 | white-space: pre; } 107 | 108 | .content-wrapper { 109 | display: flex; 110 | flex-direction: column; } 111 | @media (min-width: 768px) { 112 | .content-wrapper { 113 | flex-direction: row; } } 114 | .header { 115 | display: flex; 116 | padding: 8px; 117 | font-size: 0.875em; 118 | background: #fff; 119 | color: #fff; } 120 | 121 | .header-col { 122 | margin: 0; 123 | padding: 0 8px; } 124 | 125 | .header-col--primary { 126 | flex: 1; } 127 | 128 | .header-link { 129 | color: #000000d9; } 130 | 131 | .header-icon { 132 | padding-right: 6px; 133 | vertical-align: -4px; 134 | height: 16px; } 135 | 136 | .breadcrumbs { 137 | font-size: 0.875em; 138 | padding: 8px 16px; 139 | margin: 0; 140 | background: #fbfbfb; 141 | border-bottom: 1px solid #ddd; } 142 | 143 | .carat { 144 | height: 10px; 145 | margin: 0 5px; } 146 | 147 | .navigation { 148 | order: 2; } 149 | @media (min-width: 768px) { 150 | .navigation { 151 | order: 1; 152 | width: 25%; 153 | max-width: 300px; 154 | padding-bottom: 64px; 155 | overflow: hidden; 156 | word-wrap: normal; 157 | background: #fff; } } 158 | .nav-groups { 159 | list-style-type: none; 160 | padding-left: 0; } 161 | 162 | .nav-group-name { 163 | padding: 8px 0 8px 16px; } 164 | 165 | .nav-group-name-link { 166 | color: #000000d9; } 167 | 168 | .nav-group-tasks { 169 | margin: 8px 0; 170 | padding: 0 0 0 8px; } 171 | 172 | .nav-group-task { 173 | font-size: 1em; 174 | list-style-type: none; 175 | white-space: nowrap; } 176 | 177 | .nav-group-task-link { 178 | color: #808080; } 179 | 180 | .main-content { 181 | order: 1; } 182 | @media (min-width: 768px) { 183 | .main-content { 184 | order: 2; 185 | flex: 1; 186 | padding-bottom: 60px; } } 187 | .section { 188 | padding: 0 32px; } 189 | 190 | .section-content { 191 | max-width: 834px; 192 | margin: 0 auto; 193 | padding: 16px 0; } 194 | 195 | .section-name { 196 | color: #666; 197 | display: block; } 198 | .section-name p { 199 | margin-bottom: inherit; } 200 | 201 | .declaration .highlight { 202 | overflow-x: initial; 203 | padding: 8px 0; 204 | margin: 0; 205 | background-color: transparent; 206 | border: none; } 207 | 208 | .task-group-section { 209 | border-top: 1px solid #ddd; } 210 | 211 | .task-group { 212 | padding-top: 0px; } 213 | 214 | .task-name-container a[name]:before { 215 | content: ""; 216 | display: block; } 217 | 218 | .section-name-container { 219 | position: relative; } 220 | .section-name-container .section-name-link { 221 | position: absolute; 222 | top: 0; 223 | left: 0; 224 | bottom: 0; 225 | right: 0; 226 | margin-bottom: 0; } 227 | .section-name-container .section-name { 228 | position: relative; 229 | pointer-events: none; 230 | z-index: 1; } 231 | .section-name-container .section-name a { 232 | pointer-events: auto; } 233 | 234 | .item-container { 235 | padding: 0; } 236 | 237 | .item { 238 | padding-top: 8px; 239 | width: 100%; 240 | list-style-type: none; } 241 | .item a[name]:before { 242 | content: ""; 243 | display: block; } 244 | .item .token, .item .direct-link { 245 | display: inline-block; 246 | text-indent: -20px; 247 | padding-left: 3px; 248 | margin-left: 20px; 249 | font-size: 1rem; } 250 | .item .declaration-note { 251 | font-size: .85em; 252 | color: #808080; 253 | font-style: italic; } 254 | 255 | .pointer-container { 256 | border-bottom: 1px solid #ddd; 257 | left: -23px; 258 | padding-bottom: 13px; 259 | position: relative; 260 | width: 110%; } 261 | 262 | .pointer { 263 | left: 21px; 264 | top: 7px; 265 | display: block; 266 | position: absolute; 267 | width: 12px; 268 | height: 12px; 269 | border-left: 1px solid #ddd; 270 | border-top: 1px solid #ddd; 271 | background: #fff; 272 | transform: rotate(45deg); } 273 | 274 | .height-container { 275 | display: none; 276 | position: relative; 277 | width: 100%; 278 | overflow: hidden; } 279 | .height-container .section { 280 | background: #fff; 281 | border: 1px solid #ddd; 282 | border-top-width: 0; 283 | padding-top: 10px; 284 | padding-bottom: 5px; 285 | padding: 8px 16px; } 286 | 287 | .aside, .language { 288 | padding: 6px 12px; 289 | margin: 12px 0; 290 | border-left: 5px solid #dddddd; 291 | overflow-y: hidden; } 292 | .aside .aside-title, .language .aside-title { 293 | font-size: 9px; 294 | letter-spacing: 2px; 295 | text-transform: uppercase; 296 | padding-bottom: 0; 297 | margin: 0; 298 | color: #aaa; 299 | -webkit-user-select: none; } 300 | .aside p:last-child, .language p:last-child { 301 | margin-bottom: 0; } 302 | 303 | .language { 304 | border-left: 5px solid #cde9f4; } 305 | .language .aside-title { 306 | color: #4183c4; } 307 | 308 | .aside-warning, .aside-deprecated, .aside-unavailable { 309 | border-left: 5px solid #ff6666; } 310 | .aside-warning .aside-title, .aside-deprecated .aside-title, .aside-unavailable .aside-title { 311 | color: #ff0000; } 312 | 313 | .graybox { 314 | border-collapse: collapse; 315 | width: 100%; } 316 | .graybox p { 317 | margin: 0; 318 | word-break: break-word; 319 | min-width: 50px; } 320 | .graybox td { 321 | border: 1px solid #ddd; 322 | padding: 5px 25px 5px 10px; 323 | vertical-align: middle; } 324 | .graybox tr td:first-of-type { 325 | text-align: right; 326 | padding: 7px; 327 | vertical-align: top; 328 | word-break: normal; 329 | width: 40px; } 330 | 331 | .slightly-smaller { 332 | font-size: 0.9em; } 333 | 334 | .footer { 335 | padding: 8px 16px; 336 | background: #444; 337 | color: #ddd; 338 | font-size: 0.8em; } 339 | .footer p { 340 | margin: 8px 0; } 341 | .footer a { 342 | color: #fff; } 343 | 344 | html.dash .header, html.dash .breadcrumbs, html.dash .navigation { 345 | display: none; } 346 | 347 | html.dash .height-container { 348 | display: block; } 349 | 350 | form[role=search] input { 351 | font: 16px/1.7 "Helvetica Neue", Helvetica, Arial, sans-serif; 352 | font-size: 14px; 353 | line-height: 24px; 354 | padding: 0 10px; 355 | margin: 0; 356 | border: none; 357 | border-radius: 1em; } 358 | .loading form[role=search] input { 359 | background: white url(../img/spinner.gif) center right 4px no-repeat; } 360 | 361 | form[role=search] .tt-menu { 362 | margin: 0; 363 | min-width: 300px; 364 | background: #fff; 365 | color: #333; 366 | border: 1px solid #ddd; } 367 | 368 | form[role=search] .tt-highlight { 369 | font-weight: bold; } 370 | 371 | form[role=search] .tt-suggestion { 372 | font: 16px/1.7 "Helvetica Neue", Helvetica, Arial, sans-serif; 373 | padding: 0 8px; } 374 | form[role=search] .tt-suggestion span { 375 | display: table-cell; 376 | white-space: nowrap; } 377 | form[role=search] .tt-suggestion .doc-parent-name { 378 | width: 100%; 379 | text-align: right; 380 | font-weight: normal; 381 | font-size: 0.9em; 382 | padding-left: 16px; } 383 | 384 | form[role=search] .tt-suggestion:hover, 385 | form[role=search] .tt-suggestion.tt-cursor { 386 | cursor: pointer; 387 | background-color: #1890ff; 388 | color: #fff; } 389 | 390 | form[role=search] .tt-suggestion:hover .doc-parent-name, 391 | form[role=search] .tt-suggestion.tt-cursor .doc-parent-name { 392 | color: #fff; } 393 | -------------------------------------------------------------------------------- /docs/docsets/UIRouter.docset/Contents/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleIdentifier 6 | com.jazzy.uirouter 7 | CFBundleName 8 | UIRouter 9 | DocSetPlatformFamily 10 | uirouter 11 | isDashDocset 12 | 13 | dashIndexFilePath 14 | index.html 15 | isJavaScriptEnabled 16 | 17 | DashDocSetFamily 18 | dashtoc 19 | 20 | 21 | -------------------------------------------------------------------------------- /docs/docsets/UIRouter.docset/Contents/Resources/Documents/Extensions/Array.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Array Extension Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 |

23 | 24 | UIRouter 0.2.0.alpha Docs 25 | 26 | (72% documented) 27 |

28 | 29 |

30 |

31 | 32 |
33 |

34 | 35 | 36 |
37 | 38 | 43 | 44 |
45 | 134 |
135 | 136 |
137 |
138 |

Array

139 |
140 |
141 | 142 |
public extension Array where Element == PathComponent
143 | 144 |
145 |
146 | 147 |
148 |
149 | 150 |
151 |
152 |
153 |
154 | 155 | 156 |
157 | 158 |

Available where Element == PathComponent

159 |

160 |
161 |
162 |
    163 |
  • 164 |
    165 | 166 | 167 | 168 | string 169 | 170 |
    171 |
    172 |
    173 |
    174 |
    175 |
    176 |

    Undocumented

    177 | 178 |
    179 |
    180 |

    Declaration

    181 |
    182 |

    Swift

    183 |
    var string: String { get }
    184 | 185 |
    186 |
    187 |
    188 |
    189 |
  • 190 |
191 |
192 |
193 |
194 | 195 |
196 |
197 | 201 | 202 | 203 | 204 | -------------------------------------------------------------------------------- /docs/docsets/UIRouter.docset/Contents/Resources/Documents/Extensions/String.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | String Extension Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 |

23 | 24 | UIRouter 0.2.0.alpha Docs 25 | 26 | (72% documented) 27 |

28 | 29 |

30 |

31 | 32 |
33 |

34 | 35 | 36 |
37 | 38 | 43 | 44 |
45 | 134 |
135 | 136 |
137 |
138 |

String

139 |
140 |
141 | 142 |
extension String: URLComponentsConvertible
143 | 144 |
145 |
146 | 147 |
148 |
149 | 150 |
151 |
152 |
153 |
    154 |
  • 155 |
    156 | 157 | 158 | 159 | asURLComponents() 160 | 161 |
    162 |
    163 |
    164 |
    165 |
    166 |
    167 |

    如果构造成功则返回一个 URLComponents,否则抛出异常.

    168 |
    169 |

    Throws

    170 | 一个RouteError.badURL(url:) 实例. 171 | 172 |
    173 | 174 |
    175 |
    176 |

    Declaration

    177 |
    178 |

    Swift

    179 |
    public func asURLComponents() throws -> URLComponents
    180 | 181 |
    182 |
    183 |
    184 |

    Return Value

    185 |

    构造成功的URLComponents

    186 |
    187 |
    188 |
    189 |
  • 190 |
191 |
192 |
193 |
194 | 195 |
196 |
197 | 201 | 202 | 203 | 204 | -------------------------------------------------------------------------------- /docs/docsets/UIRouter.docset/Contents/Resources/Documents/Extensions/URL.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | URL Extension Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 |

23 | 24 | UIRouter 0.2.0.alpha Docs 25 | 26 | (72% documented) 27 |

28 | 29 |

30 |

31 | 32 |
33 |

34 | 35 | 36 |
37 | 38 | 43 | 44 |
45 | 134 |
135 | 136 |
137 |
138 |

URL

139 |
140 |
141 | 142 |
extension URL: URLComponentsConvertible
143 | 144 |
145 |
146 | 147 |
148 |
149 | 150 |
151 |
152 |
153 |
    154 |
  • 155 |
    156 | 157 | 158 | 159 | asURLComponents() 160 | 161 |
    162 |
    163 |
    164 |
    165 |
    166 |
    167 |

    如果构造成功则返回一个 URLComponents,否则抛出异常.

    168 |
    169 |

    Throws

    170 | 一个RouteError.badURL(url:) 实例. 171 | 172 |
    173 | 174 |
    175 |
    176 |

    Declaration

    177 |
    178 |

    Swift

    179 |
    public func asURLComponents() throws -> URLComponents
    180 | 181 |
    182 |
    183 |
    184 |

    Return Value

    185 |

    构造成功的URLComponents

    186 |
    187 |
    188 |
    189 |
  • 190 |
191 |
192 |
193 |
194 | 195 |
196 |
197 | 201 | 202 | 203 | 204 | -------------------------------------------------------------------------------- /docs/docsets/UIRouter.docset/Contents/Resources/Documents/Extensions/URLComponents.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | URLComponents Extension Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 |

23 | 24 | UIRouter 0.2.0.alpha Docs 25 | 26 | (72% documented) 27 |

28 | 29 |

30 |

31 | 32 |
33 |

34 | 35 | 36 |
37 | 38 | 43 | 44 |
45 | 134 |
135 | 136 |
137 |
138 |

URLComponents

139 |
140 |
141 | 142 |
extension URLComponents: URLComponentsConvertible
143 | 144 |
145 |
146 | 147 |
148 |
149 | 150 |
151 |
152 |
153 |
    154 |
  • 155 |
    156 | 157 | 158 | 159 | asURLComponents() 160 | 161 |
    162 |
    163 |
    164 |
    165 |
    166 |
    167 |

    如果构造成功则返回一个 URLComponents,否则抛出异常.

    168 |
    169 |

    Throws

    170 | 一个RouteError.badURL(url:) 实例. 171 | 172 |
    173 | 174 |
    175 |
    176 |

    Declaration

    177 |
    178 |

    Swift

    179 |
    public func asURLComponents() throws -> URLComponents
    180 | 181 |
    182 |
    183 |
    184 |

    Return Value

    185 |

    构造成功的URLComponents

    186 |
    187 |
    188 |
    189 |
  • 190 |
191 |
192 |
193 |
194 | 195 |
196 |
197 | 201 | 202 | 203 | 204 | -------------------------------------------------------------------------------- /docs/docsets/UIRouter.docset/Contents/Resources/Documents/Protocols/RouteInterceptor.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | RouteInterceptor Protocol Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 |

23 | 24 | UIRouter 0.2.0.alpha Docs 25 | 26 | (72% documented) 27 |

28 | 29 |

30 |

31 | 32 |
33 |

34 | 35 | 36 |
37 | 38 | 43 | 44 |
45 | 134 |
135 | 136 |
137 |
138 |

RouteInterceptor

139 |
140 |
141 | 142 |
public protocol RouteInterceptor
143 | 144 |
145 |
146 |

Undocumented

147 | 148 |
149 |
150 | 151 |
152 |
153 |
154 |
    155 |
  • 156 |
    157 | 158 | 159 | 160 | handle(components:object:completionHandler:) 161 | 162 |
    163 |
    164 |
    165 |
    166 |
    167 |
    168 |

    Undocumented

    169 | 170 |
    171 |
    172 |

    Declaration

    173 |
    174 |

    Swift

    175 |
    func handle(components: URLComponents, object: Any, completionHandler: @escaping RouteCompletionHandler<UIViewController?>)
    176 | 177 |
    178 |
    179 |
    180 |
    181 |
  • 182 |
183 |
184 |
185 |
186 | 187 |
188 |
189 | 193 | 194 | 195 | 196 | -------------------------------------------------------------------------------- /docs/docsets/UIRouter.docset/Contents/Resources/Documents/Protocols/URLComponentsConvertible.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | URLComponentsConvertible Protocol Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 |

23 | 24 | UIRouter 0.2.0.alpha Docs 25 | 26 | (72% documented) 27 |

28 | 29 |

30 |

31 | 32 |
33 |

34 | 35 | 36 |
37 | 38 | 43 | 44 |
45 | 134 |
135 | 136 |
137 |
138 |

URLComponentsConvertible

139 |
140 |
141 | 142 |
public protocol URLComponentsConvertible
143 | 144 |
145 |
146 |

Types adopting the URLComponentsConvertible protocol can be used to construct URLComponentss, which can then be used to route.

147 | 148 |
149 |
150 | 151 |
152 |
153 |
154 |
    155 |
  • 156 |
    157 | 158 | 159 | 160 | asURLComponents() 161 | 162 |
    163 |
    164 |
    165 |
    166 |
    167 |
    168 |

    如果构造成功则返回一个 URLComponents,否则抛出异常.

    169 |
    170 |

    Throws

    171 | 一个RouteError.badURL(url:) 实例. 172 | 173 |
    174 | 175 |
    176 |
    177 |

    Declaration

    178 |
    179 |

    Swift

    180 |
    func asURLComponents() throws -> URLComponents
    181 | 182 |
    183 |
    184 |
    185 |

    Return Value

    186 |

    构造成功的URLComponents

    187 |
    188 |
    189 |
    190 |
  • 191 |
192 |
193 |
194 |
195 | 196 |
197 |
198 | 202 | 203 | 204 | 205 | -------------------------------------------------------------------------------- /docs/docsets/UIRouter.docset/Contents/Resources/Documents/css/highlight.css: -------------------------------------------------------------------------------- 1 | /* Credit to https://gist.github.com/wataru420/2048287 */ 2 | .highlight { 3 | /* Comment */ 4 | /* Error */ 5 | /* Keyword */ 6 | /* Operator */ 7 | /* Comment.Multiline */ 8 | /* Comment.Preproc */ 9 | /* Comment.Single */ 10 | /* Comment.Special */ 11 | /* Generic.Deleted */ 12 | /* Generic.Deleted.Specific */ 13 | /* Generic.Emph */ 14 | /* Generic.Error */ 15 | /* Generic.Heading */ 16 | /* Generic.Inserted */ 17 | /* Generic.Inserted.Specific */ 18 | /* Generic.Output */ 19 | /* Generic.Prompt */ 20 | /* Generic.Strong */ 21 | /* Generic.Subheading */ 22 | /* Generic.Traceback */ 23 | /* Keyword.Constant */ 24 | /* Keyword.Declaration */ 25 | /* Keyword.Pseudo */ 26 | /* Keyword.Reserved */ 27 | /* Keyword.Type */ 28 | /* Literal.Number */ 29 | /* Literal.String */ 30 | /* Name.Attribute */ 31 | /* Name.Builtin */ 32 | /* Name.Class */ 33 | /* Name.Constant */ 34 | /* Name.Entity */ 35 | /* Name.Exception */ 36 | /* Name.Function */ 37 | /* Name.Namespace */ 38 | /* Name.Tag */ 39 | /* Name.Variable */ 40 | /* Operator.Word */ 41 | /* Text.Whitespace */ 42 | /* Literal.Number.Float */ 43 | /* Literal.Number.Hex */ 44 | /* Literal.Number.Integer */ 45 | /* Literal.Number.Oct */ 46 | /* Literal.String.Backtick */ 47 | /* Literal.String.Char */ 48 | /* Literal.String.Doc */ 49 | /* Literal.String.Double */ 50 | /* Literal.String.Escape */ 51 | /* Literal.String.Heredoc */ 52 | /* Literal.String.Interpol */ 53 | /* Literal.String.Other */ 54 | /* Literal.String.Regex */ 55 | /* Literal.String.Single */ 56 | /* Literal.String.Symbol */ 57 | /* Name.Builtin.Pseudo */ 58 | /* Name.Variable.Class */ 59 | /* Name.Variable.Global */ 60 | /* Name.Variable.Instance */ 61 | /* Literal.Number.Integer.Long */ } 62 | .highlight .c { 63 | color: #999988; 64 | font-style: italic; } 65 | .highlight .err { 66 | color: #a61717; 67 | background-color: #e3d2d2; } 68 | .highlight .k { 69 | color: #000000; 70 | font-weight: bold; } 71 | .highlight .o { 72 | color: #000000; 73 | font-weight: bold; } 74 | .highlight .cm { 75 | color: #999988; 76 | font-style: italic; } 77 | .highlight .cp { 78 | color: #999999; 79 | font-weight: bold; } 80 | .highlight .c1 { 81 | color: #999988; 82 | font-style: italic; } 83 | .highlight .cs { 84 | color: #999999; 85 | font-weight: bold; 86 | font-style: italic; } 87 | .highlight .gd { 88 | color: #000000; 89 | background-color: #ffdddd; } 90 | .highlight .gd .x { 91 | color: #000000; 92 | background-color: #ffaaaa; } 93 | .highlight .ge { 94 | color: #000000; 95 | font-style: italic; } 96 | .highlight .gr { 97 | color: #aa0000; } 98 | .highlight .gh { 99 | color: #999999; } 100 | .highlight .gi { 101 | color: #000000; 102 | background-color: #ddffdd; } 103 | .highlight .gi .x { 104 | color: #000000; 105 | background-color: #aaffaa; } 106 | .highlight .go { 107 | color: #888888; } 108 | .highlight .gp { 109 | color: #555555; } 110 | .highlight .gs { 111 | font-weight: bold; } 112 | .highlight .gu { 113 | color: #aaaaaa; } 114 | .highlight .gt { 115 | color: #aa0000; } 116 | .highlight .kc { 117 | color: #000000; 118 | font-weight: bold; } 119 | .highlight .kd { 120 | color: #000000; 121 | font-weight: bold; } 122 | .highlight .kp { 123 | color: #000000; 124 | font-weight: bold; } 125 | .highlight .kr { 126 | color: #000000; 127 | font-weight: bold; } 128 | .highlight .kt { 129 | color: #445588; } 130 | .highlight .m { 131 | color: #009999; } 132 | .highlight .s { 133 | color: #d14; } 134 | .highlight .na { 135 | color: #008080; } 136 | .highlight .nb { 137 | color: #0086B3; } 138 | .highlight .nc { 139 | color: #445588; 140 | font-weight: bold; } 141 | .highlight .no { 142 | color: #008080; } 143 | .highlight .ni { 144 | color: #800080; } 145 | .highlight .ne { 146 | color: #990000; 147 | font-weight: bold; } 148 | .highlight .nf { 149 | color: #990000; } 150 | .highlight .nn { 151 | color: #555555; } 152 | .highlight .nt { 153 | color: #000080; } 154 | .highlight .nv { 155 | color: #008080; } 156 | .highlight .ow { 157 | color: #000000; 158 | font-weight: bold; } 159 | .highlight .w { 160 | color: #bbbbbb; } 161 | .highlight .mf { 162 | color: #009999; } 163 | .highlight .mh { 164 | color: #009999; } 165 | .highlight .mi { 166 | color: #009999; } 167 | .highlight .mo { 168 | color: #009999; } 169 | .highlight .sb { 170 | color: #d14; } 171 | .highlight .sc { 172 | color: #d14; } 173 | .highlight .sd { 174 | color: #d14; } 175 | .highlight .s2 { 176 | color: #d14; } 177 | .highlight .se { 178 | color: #d14; } 179 | .highlight .sh { 180 | color: #d14; } 181 | .highlight .si { 182 | color: #d14; } 183 | .highlight .sx { 184 | color: #d14; } 185 | .highlight .sr { 186 | color: #009926; } 187 | .highlight .s1 { 188 | color: #d14; } 189 | .highlight .ss { 190 | color: #990073; } 191 | .highlight .bp { 192 | color: #999999; } 193 | .highlight .vc { 194 | color: #008080; } 195 | .highlight .vg { 196 | color: #008080; } 197 | .highlight .vi { 198 | color: #008080; } 199 | .highlight .il { 200 | color: #009999; } 201 | -------------------------------------------------------------------------------- /docs/docsets/UIRouter.docset/Contents/Resources/Documents/img/carat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wxlpp/UIRouter/758210a642f5252fca54b12737b9a7cb9ed6d37b/docs/docsets/UIRouter.docset/Contents/Resources/Documents/img/carat.png -------------------------------------------------------------------------------- /docs/docsets/UIRouter.docset/Contents/Resources/Documents/img/dash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wxlpp/UIRouter/758210a642f5252fca54b12737b9a7cb9ed6d37b/docs/docsets/UIRouter.docset/Contents/Resources/Documents/img/dash.png -------------------------------------------------------------------------------- /docs/docsets/UIRouter.docset/Contents/Resources/Documents/img/gh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wxlpp/UIRouter/758210a642f5252fca54b12737b9a7cb9ed6d37b/docs/docsets/UIRouter.docset/Contents/Resources/Documents/img/gh.png -------------------------------------------------------------------------------- /docs/docsets/UIRouter.docset/Contents/Resources/Documents/img/spinner.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wxlpp/UIRouter/758210a642f5252fca54b12737b9a7cb9ed6d37b/docs/docsets/UIRouter.docset/Contents/Resources/Documents/img/spinner.gif -------------------------------------------------------------------------------- /docs/docsets/UIRouter.docset/Contents/Resources/Documents/js/jazzy.js: -------------------------------------------------------------------------------- 1 | window.jazzy = {'docset': false} 2 | if (typeof window.dash != 'undefined') { 3 | document.documentElement.className += ' dash' 4 | window.jazzy.docset = true 5 | } 6 | if (navigator.userAgent.match(/xcode/i)) { 7 | document.documentElement.className += ' xcode' 8 | window.jazzy.docset = true 9 | } 10 | 11 | function toggleItem($link, $content) { 12 | var animationDuration = 300; 13 | $link.toggleClass('token-open'); 14 | $content.slideToggle(animationDuration); 15 | } 16 | 17 | function itemLinkToContent($link) { 18 | return $link.parent().parent().next(); 19 | } 20 | 21 | // On doc load + hash-change, open any targetted item 22 | function openCurrentItemIfClosed() { 23 | if (window.jazzy.docset) { 24 | return; 25 | } 26 | var $link = $(`a[name="${location.hash.substring(1)}"]`).nextAll('.token'); 27 | $content = itemLinkToContent($link); 28 | if ($content.is(':hidden')) { 29 | toggleItem($link, $content); 30 | } 31 | } 32 | 33 | $(openCurrentItemIfClosed); 34 | $(window).on('hashchange', openCurrentItemIfClosed); 35 | 36 | // On item link ('token') click, toggle its discussion 37 | $('.token').on('click', function(event) { 38 | if (window.jazzy.docset) { 39 | return; 40 | } 41 | var $link = $(this); 42 | toggleItem($link, itemLinkToContent($link)); 43 | 44 | // Keeps the document from jumping to the hash. 45 | var href = $link.attr('href'); 46 | if (history.pushState) { 47 | history.pushState({}, '', href); 48 | } else { 49 | location.hash = href; 50 | } 51 | event.preventDefault(); 52 | }); 53 | 54 | // Clicks on links to the current, closed, item need to open the item 55 | $("a:not('.token')").on('click', function() { 56 | if (location == this.href) { 57 | openCurrentItemIfClosed(); 58 | } 59 | }); 60 | 61 | // KaTeX rendering 62 | if ("katex" in window) { 63 | $($('.math').each( (_, element) => { 64 | katex.render(element.textContent, element, { 65 | displayMode: $(element).hasClass('m-block'), 66 | throwOnError: false, 67 | trust: true 68 | }); 69 | })) 70 | } 71 | -------------------------------------------------------------------------------- /docs/docsets/UIRouter.docset/Contents/Resources/Documents/js/jazzy.search.js: -------------------------------------------------------------------------------- 1 | $(function(){ 2 | var $typeahead = $('[data-typeahead]'); 3 | var $form = $typeahead.parents('form'); 4 | var searchURL = $form.attr('action'); 5 | 6 | function displayTemplate(result) { 7 | return result.name; 8 | } 9 | 10 | function suggestionTemplate(result) { 11 | var t = '
'; 12 | t += '' + result.name + ''; 13 | if (result.parent_name) { 14 | t += '' + result.parent_name + ''; 15 | } 16 | t += '
'; 17 | return t; 18 | } 19 | 20 | $typeahead.one('focus', function() { 21 | $form.addClass('loading'); 22 | 23 | $.getJSON(searchURL).then(function(searchData) { 24 | const searchIndex = lunr(function() { 25 | this.ref('url'); 26 | this.field('name'); 27 | this.field('abstract'); 28 | for (const [url, doc] of Object.entries(searchData)) { 29 | this.add({url: url, name: doc.name, abstract: doc.abstract}); 30 | } 31 | }); 32 | 33 | $typeahead.typeahead( 34 | { 35 | highlight: true, 36 | minLength: 3, 37 | autoselect: true 38 | }, 39 | { 40 | limit: 10, 41 | display: displayTemplate, 42 | templates: { suggestion: suggestionTemplate }, 43 | source: function(query, sync) { 44 | const lcSearch = query.toLowerCase(); 45 | const results = searchIndex.query(function(q) { 46 | q.term(lcSearch, { boost: 100 }); 47 | q.term(lcSearch, { 48 | boost: 10, 49 | wildcard: lunr.Query.wildcard.TRAILING 50 | }); 51 | }).map(function(result) { 52 | var doc = searchData[result.ref]; 53 | doc.url = result.ref; 54 | return doc; 55 | }); 56 | sync(results); 57 | } 58 | } 59 | ); 60 | $form.removeClass('loading'); 61 | $typeahead.trigger('focus'); 62 | }); 63 | }); 64 | 65 | var baseURL = searchURL.slice(0, -"search.json".length); 66 | 67 | $typeahead.on('typeahead:select', function(e, result) { 68 | window.location = baseURL + result.url; 69 | }); 70 | }); 71 | -------------------------------------------------------------------------------- /docs/docsets/UIRouter.docset/Contents/Resources/docSet.dsidx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wxlpp/UIRouter/758210a642f5252fca54b12737b9a7cb9ed6d37b/docs/docsets/UIRouter.docset/Contents/Resources/docSet.dsidx -------------------------------------------------------------------------------- /docs/docsets/UIRouter.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wxlpp/UIRouter/758210a642f5252fca54b12737b9a7cb9ed6d37b/docs/docsets/UIRouter.tgz -------------------------------------------------------------------------------- /docs/img/carat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wxlpp/UIRouter/758210a642f5252fca54b12737b9a7cb9ed6d37b/docs/img/carat.png -------------------------------------------------------------------------------- /docs/img/dash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wxlpp/UIRouter/758210a642f5252fca54b12737b9a7cb9ed6d37b/docs/img/dash.png -------------------------------------------------------------------------------- /docs/img/gh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wxlpp/UIRouter/758210a642f5252fca54b12737b9a7cb9ed6d37b/docs/img/gh.png -------------------------------------------------------------------------------- /docs/img/spinner.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wxlpp/UIRouter/758210a642f5252fca54b12737b9a7cb9ed6d37b/docs/img/spinner.gif -------------------------------------------------------------------------------- /docs/js/jazzy.js: -------------------------------------------------------------------------------- 1 | window.jazzy = {'docset': false} 2 | if (typeof window.dash != 'undefined') { 3 | document.documentElement.className += ' dash' 4 | window.jazzy.docset = true 5 | } 6 | if (navigator.userAgent.match(/xcode/i)) { 7 | document.documentElement.className += ' xcode' 8 | window.jazzy.docset = true 9 | } 10 | 11 | function toggleItem($link, $content) { 12 | var animationDuration = 300; 13 | $link.toggleClass('token-open'); 14 | $content.slideToggle(animationDuration); 15 | } 16 | 17 | function itemLinkToContent($link) { 18 | return $link.parent().parent().next(); 19 | } 20 | 21 | // On doc load + hash-change, open any targetted item 22 | function openCurrentItemIfClosed() { 23 | if (window.jazzy.docset) { 24 | return; 25 | } 26 | var $link = $(`a[name="${location.hash.substring(1)}"]`).nextAll('.token'); 27 | $content = itemLinkToContent($link); 28 | if ($content.is(':hidden')) { 29 | toggleItem($link, $content); 30 | } 31 | } 32 | 33 | $(openCurrentItemIfClosed); 34 | $(window).on('hashchange', openCurrentItemIfClosed); 35 | 36 | // On item link ('token') click, toggle its discussion 37 | $('.token').on('click', function(event) { 38 | if (window.jazzy.docset) { 39 | return; 40 | } 41 | var $link = $(this); 42 | toggleItem($link, itemLinkToContent($link)); 43 | 44 | // Keeps the document from jumping to the hash. 45 | var href = $link.attr('href'); 46 | if (history.pushState) { 47 | history.pushState({}, '', href); 48 | } else { 49 | location.hash = href; 50 | } 51 | event.preventDefault(); 52 | }); 53 | 54 | // Clicks on links to the current, closed, item need to open the item 55 | $("a:not('.token')").on('click', function() { 56 | if (location == this.href) { 57 | openCurrentItemIfClosed(); 58 | } 59 | }); 60 | 61 | // KaTeX rendering 62 | if ("katex" in window) { 63 | $($('.math').each( (_, element) => { 64 | katex.render(element.textContent, element, { 65 | displayMode: $(element).hasClass('m-block'), 66 | throwOnError: false, 67 | trust: true 68 | }); 69 | })) 70 | } 71 | -------------------------------------------------------------------------------- /docs/js/jazzy.search.js: -------------------------------------------------------------------------------- 1 | $(function(){ 2 | var $typeahead = $('[data-typeahead]'); 3 | var $form = $typeahead.parents('form'); 4 | var searchURL = $form.attr('action'); 5 | 6 | function displayTemplate(result) { 7 | return result.name; 8 | } 9 | 10 | function suggestionTemplate(result) { 11 | var t = '
'; 12 | t += '' + result.name + ''; 13 | if (result.parent_name) { 14 | t += '' + result.parent_name + ''; 15 | } 16 | t += '
'; 17 | return t; 18 | } 19 | 20 | $typeahead.one('focus', function() { 21 | $form.addClass('loading'); 22 | 23 | $.getJSON(searchURL).then(function(searchData) { 24 | const searchIndex = lunr(function() { 25 | this.ref('url'); 26 | this.field('name'); 27 | this.field('abstract'); 28 | for (const [url, doc] of Object.entries(searchData)) { 29 | this.add({url: url, name: doc.name, abstract: doc.abstract}); 30 | } 31 | }); 32 | 33 | $typeahead.typeahead( 34 | { 35 | highlight: true, 36 | minLength: 3, 37 | autoselect: true 38 | }, 39 | { 40 | limit: 10, 41 | display: displayTemplate, 42 | templates: { suggestion: suggestionTemplate }, 43 | source: function(query, sync) { 44 | const lcSearch = query.toLowerCase(); 45 | const results = searchIndex.query(function(q) { 46 | q.term(lcSearch, { boost: 100 }); 47 | q.term(lcSearch, { 48 | boost: 10, 49 | wildcard: lunr.Query.wildcard.TRAILING 50 | }); 51 | }).map(function(result) { 52 | var doc = searchData[result.ref]; 53 | doc.url = result.ref; 54 | return doc; 55 | }); 56 | sync(results); 57 | } 58 | } 59 | ); 60 | $form.removeClass('loading'); 61 | $typeahead.trigger('focus'); 62 | }); 63 | }); 64 | 65 | var baseURL = searchURL.slice(0, -"search.json".length); 66 | 67 | $typeahead.on('typeahead:select', function(e, result) { 68 | window.location = baseURL + result.url; 69 | }); 70 | }); 71 | -------------------------------------------------------------------------------- /log.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wxlpp/UIRouter/758210a642f5252fca54b12737b9a7cb9ed6d37b/log.png --------------------------------------------------------------------------------