├── .gitignore ├── Apis.podspec ├── Apis.xcodeproj ├── project.pbxproj └── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ └── IDEWorkspaceChecks.plist ├── Demo ├── Demo.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── Demo.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── Demo │ ├── Account.swift │ ├── AppDelegate.swift │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ └── Contents.json │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── FastViewController.swift │ ├── Info.plist │ ├── LiveViewController.swift │ ├── LoginViewController.swift │ ├── NeedLoginViewController.swift │ ├── NoneViewController.swift │ ├── Router │ │ ├── Plugins │ │ │ ├── RouterAccountPlugin.swift │ │ │ ├── RouterLaunchPlugin.swift │ │ │ └── RouterSinglePlugin.swift │ │ ├── Router.swift │ │ └── RouterTarget.swift │ ├── Utils │ │ ├── Notification.swift │ │ └── Notificationable.swift │ └── ViewController.swift ├── Podfile ├── Podfile.lock └── Pods │ ├── Local Podspecs │ ├── Apis.podspec.json │ └── Router.podspec.json │ ├── Manifest.lock │ ├── Pods.xcodeproj │ └── project.pbxproj │ ├── SnapKit │ ├── LICENSE │ ├── README.md │ └── Source │ │ ├── Constraint.swift │ │ ├── ConstraintAttributes.swift │ │ ├── ConstraintConfig.swift │ │ ├── ConstraintConstantTarget.swift │ │ ├── ConstraintDSL.swift │ │ ├── ConstraintDescription.swift │ │ ├── ConstraintDirectionalInsetTarget.swift │ │ ├── ConstraintDirectionalInsets.swift │ │ ├── ConstraintInsetTarget.swift │ │ ├── ConstraintInsets.swift │ │ ├── ConstraintItem.swift │ │ ├── ConstraintLayoutGuide+Extensions.swift │ │ ├── ConstraintLayoutGuide.swift │ │ ├── ConstraintLayoutGuideDSL.swift │ │ ├── ConstraintLayoutSupport.swift │ │ ├── ConstraintLayoutSupportDSL.swift │ │ ├── ConstraintMaker.swift │ │ ├── ConstraintMakerEditable.swift │ │ ├── ConstraintMakerExtendable.swift │ │ ├── ConstraintMakerFinalizable.swift │ │ ├── ConstraintMakerPriortizable.swift │ │ ├── ConstraintMakerRelatable.swift │ │ ├── ConstraintMultiplierTarget.swift │ │ ├── ConstraintOffsetTarget.swift │ │ ├── ConstraintPriority.swift │ │ ├── ConstraintPriorityTarget.swift │ │ ├── ConstraintRelatableTarget.swift │ │ ├── ConstraintRelation.swift │ │ ├── ConstraintView+Extensions.swift │ │ ├── ConstraintView.swift │ │ ├── ConstraintViewDSL.swift │ │ ├── Debugging.swift │ │ ├── LayoutConstraint.swift │ │ ├── LayoutConstraintItem.swift │ │ ├── Typealiases.swift │ │ └── UILayoutSupport+Extensions.swift │ └── Target Support Files │ ├── Apis │ ├── Apis-Info.plist │ ├── Apis-dummy.m │ ├── Apis-prefix.pch │ ├── Apis-umbrella.h │ ├── Apis.debug.xcconfig │ ├── Apis.modulemap │ ├── Apis.release.xcconfig │ ├── ResourceBundle-Apis-Apis-Info.plist │ └── ResourceBundle-Privacy-Apis-Info.plist │ ├── Pods-Demo │ ├── Pods-Demo-Info.plist │ ├── Pods-Demo-acknowledgements.markdown │ ├── Pods-Demo-acknowledgements.plist │ ├── Pods-Demo-dummy.m │ ├── Pods-Demo-frameworks-Debug-input-files.xcfilelist │ ├── Pods-Demo-frameworks-Debug-output-files.xcfilelist │ ├── Pods-Demo-frameworks-Release-input-files.xcfilelist │ ├── Pods-Demo-frameworks-Release-output-files.xcfilelist │ ├── Pods-Demo-frameworks.sh │ ├── Pods-Demo-umbrella.h │ ├── Pods-Demo.debug.xcconfig │ ├── Pods-Demo.modulemap │ └── Pods-Demo.release.xcconfig │ └── SnapKit │ ├── SnapKit-Info.plist │ ├── SnapKit-dummy.m │ ├── SnapKit-prefix.pch │ ├── SnapKit-umbrella.h │ ├── SnapKit.debug.xcconfig │ ├── SnapKit.modulemap │ └── SnapKit.release.xcconfig ├── LICENSE ├── README.md ├── README_CN.md └── Sources ├── Apis.h ├── Context.swift ├── PrivacyInfo.xcprivacy ├── Protocol.swift ├── Provider.swift ├── UIApplicationExtension.swift ├── UIViewControllerExtension.swift ├── URLConvertible.swift ├── URLMatcher.swift ├── URLNavigator.swift └── URLPathComponent.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 | # Accio dependency management 70 | Dependencies/ 71 | .accio/ 72 | 73 | # fastlane 74 | # 75 | # It is recommended to not store the screenshots in the git repo. 76 | # Instead, use fastlane to re-generate the screenshots whenever they are needed. 77 | # For more information about the recommended setup visit: 78 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 79 | 80 | fastlane/report.xml 81 | fastlane/Preview.html 82 | fastlane/screenshots/**/*.png 83 | fastlane/test_output 84 | 85 | # Code Injection 86 | # 87 | # After new code Injection tools there's a generated folder /iOSInjectionProject 88 | # https://github.com/johnno1962/injectionforxcode 89 | 90 | iOSInjectionProject/ 91 | -------------------------------------------------------------------------------- /Apis.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | 3 | s.name = "Apis" 4 | s.version = "1.2.2" 5 | s.summary = "基于URLNavigator抽象的URL路由组件 灵感来自Moya 配置化 插件化." 6 | 7 | s.homepage = "https://github.com/lixiang1994/Apis" 8 | 9 | s.license = { :type => "MIT", :file => "LICENSE" } 10 | 11 | s.author = { "LEE" => "18611401994@163.com" } 12 | 13 | s.platform = :ios, "9.0" 14 | 15 | s.source = { :git => "https://github.com/lixiang1994/Apis.git", :tag => s.version } 16 | 17 | s.source_files = "Sources/**/*.swift" 18 | 19 | s.requires_arc = true 20 | 21 | s.frameworks = "UIKit", "Foundation" 22 | 23 | s.swift_version = "5.0" 24 | 25 | s.subspec 'Privacy' do |ss| 26 | ss.resource_bundles = { 27 | s.name => 'Sources/PrivacyInfo.xcprivacy' 28 | } 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /Apis.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Apis.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Demo/Demo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Demo/Demo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Demo/Demo.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Demo/Demo.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Demo/Demo/Account.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Account.swift 3 | // Demo 4 | // 5 | // Created by 李响 on 2019/4/27. 6 | // Copyright © 2019 swift. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | enum Account { 12 | 13 | static var isLogin: Bool { 14 | return info != nil 15 | } 16 | 17 | static var info: User? 18 | } 19 | 20 | struct User { 21 | let id: Int 22 | let name: String 23 | let avatar: String 24 | /* ... */ 25 | } 26 | -------------------------------------------------------------------------------- /Demo/Demo/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // Demo 4 | // 5 | // Created by 李响 on 2019/4/12. 6 | // Copyright © 2019 swift. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | static var shared: AppDelegate { 15 | return UIApplication.shared.delegate as! AppDelegate 16 | } 17 | 18 | var window: UIWindow? 19 | var isLaunched: Bool = false 20 | 21 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 22 | 23 | // 模拟1秒后启动完成 24 | DispatchQueue.main.asyncAfter(deadline: .now() + 1) { 25 | self.isLaunched = true 26 | NotificationCenter.Delegate.post(.launched) 27 | } 28 | 29 | return true 30 | } 31 | 32 | func applicationWillResignActive(_ application: UIApplication) { 33 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 34 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 35 | } 36 | 37 | func applicationDidEnterBackground(_ application: UIApplication) { 38 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 39 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 40 | } 41 | 42 | func applicationWillEnterForeground(_ application: UIApplication) { 43 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 44 | } 45 | 46 | func applicationDidBecomeActive(_ application: UIApplication) { 47 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 48 | } 49 | 50 | func applicationWillTerminate(_ application: UIApplication) { 51 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 52 | } 53 | 54 | 55 | } 56 | 57 | -------------------------------------------------------------------------------- /Demo/Demo/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /Demo/Demo/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Demo/Demo/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Demo/Demo/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /Demo/Demo/FastViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FastViewController.swift 3 | // Demo 4 | // 5 | // Created by 李响 on 2019/4/10. 6 | // Copyright © 2019 swift. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class FastViewController: UIViewController { 12 | 13 | override func viewDidLoad() { 14 | super.viewDidLoad() 15 | 16 | view.backgroundColor = #colorLiteral(red: 0.5568627715, green: 0.3529411852, blue: 0.9686274529, alpha: 1) 17 | 18 | let tap = UITapGestureRecognizer(target: self, action: #selector(tapAction)) 19 | view.addGestureRecognizer(tap) 20 | } 21 | 22 | @objc func tapAction() { 23 | close() 24 | } 25 | } 26 | 27 | extension FastViewController: RouterSingleable { 28 | 29 | var single: RouterSingleType { 30 | return .fast 31 | } 32 | 33 | func close(will single: RouterSingleType, completion: @escaping (Bool) -> Void) { 34 | switch single { 35 | case .fast: 36 | completion(false) 37 | 38 | default: 39 | close { 40 | completion(true) 41 | } 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Demo/Demo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDisplayName 8 | Router 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleURLTypes 22 | 23 | 24 | CFBundleTypeRole 25 | Editor 26 | CFBundleURLSchemes 27 | 28 | router 29 | 30 | 31 | 32 | CFBundleVersion 33 | 1 34 | LSRequiresIPhoneOS 35 | 36 | UILaunchStoryboardName 37 | LaunchScreen 38 | UIMainStoryboardFile 39 | Main 40 | UIRequiredDeviceCapabilities 41 | 42 | armv7 43 | 44 | UISupportedInterfaceOrientations 45 | 46 | UIInterfaceOrientationPortrait 47 | 48 | UISupportedInterfaceOrientations~ipad 49 | 50 | UIInterfaceOrientationPortrait 51 | UIInterfaceOrientationPortraitUpsideDown 52 | UIInterfaceOrientationLandscapeLeft 53 | UIInterfaceOrientationLandscapeRight 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /Demo/Demo/LiveViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LiveViewController.swift 3 | // Demo 4 | // 5 | // Created by 李响 on 2019/4/10. 6 | // Copyright © 2019 swift. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class LiveViewController: UIViewController { 12 | 13 | var id: String = "" 14 | 15 | override func viewDidLoad() { 16 | super.viewDidLoad() 17 | 18 | view.backgroundColor = #colorLiteral(red: 0.2549019754, green: 0.2745098174, blue: 0.3019607961, alpha: 1) 19 | 20 | let tap = UITapGestureRecognizer(target: self, action: #selector(tapAction)) 21 | view.addGestureRecognizer(tap) 22 | } 23 | 24 | @objc func tapAction() { 25 | close() 26 | } 27 | } 28 | 29 | extension LiveViewController: RouterSingleable { 30 | 31 | var single: RouterSingleType { 32 | return .live 33 | } 34 | 35 | func close(will single: RouterSingleType, completion: @escaping (Bool) -> Void) { 36 | close { 37 | completion(true) 38 | } 39 | } 40 | 41 | func open(_ completion: @escaping () -> Void) { 42 | guard let controller = UIViewController.top() else { 43 | return 44 | } 45 | controller.present(self, animated: true, completion: completion) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Demo/Demo/LoginViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LoginViewController.swift 3 | // Demo 4 | // 5 | // Created by 李响 on 2019/4/27. 6 | // Copyright © 2019 swift. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import SnapKit 11 | 12 | class LoginViewController: UIViewController { 13 | 14 | lazy var loginButton: UIButton = { 15 | $0.setTitle("Login", for: .normal) 16 | $0.setTitleColor(.black, for: .normal) 17 | $0.addTarget(self, action: #selector(loginAction), for: .touchUpInside) 18 | return $0 19 | } ( UIButton() ) 20 | 21 | lazy var cancelButton: UIButton = { 22 | $0.setTitle("Cancel", for: .normal) 23 | $0.setTitleColor(.red, for: .normal) 24 | $0.addTarget(self, action: #selector(cancelAction), for: .touchUpInside) 25 | return $0 26 | } ( UIButton() ) 27 | 28 | private var completion: ((Bool) -> Void)? 29 | 30 | init(_ completion: @escaping ((Bool) -> Void)) { 31 | self.completion = completion 32 | super.init(nibName: nil, bundle: nil) 33 | } 34 | 35 | required init?(coder aDecoder: NSCoder) { 36 | super.init(coder: aDecoder) 37 | } 38 | 39 | override func viewDidLoad() { 40 | super.viewDidLoad() 41 | setup() 42 | setupLayout() 43 | } 44 | 45 | private func setup() { 46 | view.backgroundColor = .white 47 | view.addSubview(loginButton) 48 | view.addSubview(cancelButton) 49 | } 50 | 51 | private func setupLayout() { 52 | loginButton.snp.makeConstraints { (make) in 53 | make.center.equalToSuperview() 54 | } 55 | 56 | cancelButton.snp.makeConstraints { (make) in 57 | make.centerX.equalToSuperview() 58 | make.top.equalTo(loginButton.snp.bottom).offset(40) 59 | } 60 | } 61 | } 62 | 63 | extension LoginViewController { 64 | 65 | @objc private func loginAction(_ sender: UIButton) { 66 | // 假装登录成功 存储用户信息 67 | Account.info = User(id: 1994, name: "LEE", avatar: "XXXX") 68 | 69 | dismiss(animated: true) { 70 | self.completion?(true) 71 | } 72 | } 73 | 74 | @objc private func cancelAction(_ sender: UIButton) { 75 | dismiss(animated: true) { 76 | self.completion?(false) 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /Demo/Demo/NeedLoginViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NeedLoginViewController.swift 3 | // Demo 4 | // 5 | // Created by 李响 on 2019/4/27. 6 | // Copyright © 2019 swift. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class NeedLoginViewController: UIViewController { 12 | 13 | override func viewDidLoad() { 14 | super.viewDidLoad() 15 | 16 | view.backgroundColor = #colorLiteral(red: 1, green: 0.3137254902, blue: 0.3137254902, alpha: 1) 17 | 18 | let tap = UITapGestureRecognizer(target: self, action: #selector(tapAction)) 19 | view.addGestureRecognizer(tap) 20 | } 21 | 22 | @objc func tapAction() { 23 | close() 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Demo/Demo/NoneViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NoneViewController.swift 3 | // Demo 4 | // 5 | // Created by 李响 on 2019/4/10. 6 | // Copyright © 2019 swift. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class NoneViewController: UIViewController { 12 | 13 | override func viewDidLoad() { 14 | super.viewDidLoad() 15 | 16 | view.backgroundColor = #colorLiteral(red: 0.8039215803, green: 0.8039215803, blue: 0.8039215803, alpha: 1) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Demo/Demo/Router/Plugins/RouterAccountPlugin.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RouterAccountPlugin.swift 3 | // Demo 4 | // 5 | // Created by 李响 on 2019/4/10. 6 | // Copyright © 2019 swift. All rights reserved. 7 | // 8 | 9 | import Apis 10 | 11 | class RouterAccountPlugin: Apis.PluginType { 12 | 13 | /* 14 | 在准备打开阶段 拦截需要登录的类型, 并根据登录状态处理是否需要打开登录页面 15 | 登录成功后可执行回调继续打开流程 16 | */ 17 | func prepare(open target: TargetType, completion: @escaping (Bool) -> Void) { 18 | guard case .open_needlogin = target as? RouterTarget else { 19 | completion(true) 20 | return 21 | } 22 | guard !Account.isLogin else { 23 | completion(true) 24 | return 25 | } 26 | guard let root = AppDelegate.shared.window?.rootViewController else { 27 | completion(false) 28 | return 29 | } 30 | let controller = LoginViewController.init { (result) in 31 | completion(result) 32 | } 33 | root.present(controller, animated: true) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Demo/Demo/Router/Plugins/RouterLaunchPlugin.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RouterLaunchPlugin.swift 3 | // Demo 4 | // 5 | // Created by 李响 on 2019/4/10. 6 | // Copyright © 2019 swift. All rights reserved. 7 | // 8 | 9 | import Apis 10 | 11 | class RouterLaunchPlugin: Apis.PluginType { 12 | 13 | private var completion: ((Bool) -> Void)? 14 | 15 | init() { 16 | NotificationCenter.Delegate.add( 17 | .launched, 18 | observer: self, 19 | selector: #selector(launched) 20 | ) 21 | } 22 | 23 | @objc private func launched() { 24 | completion?(true) 25 | completion = nil 26 | } 27 | 28 | /* 29 | 在准备打开阶段 处理启动状态 30 | 通常在APP启动后 我们需要进行一些必要的操作 例如: 加载必要的配置数据等等, 31 | 这些操作没完成之前就打开了某些页面 是非常危险的. 32 | 这里演示的就是根据启动完成状态延后打开操作, 常见的情景为 外部通过OpenURL启动APP打开某一页面. 33 | */ 34 | func prepare(open target: TargetType, completion: @escaping (Bool) -> Void) { 35 | guard !AppDelegate.shared.isLaunched else { 36 | completion(true) 37 | return 38 | } 39 | self.completion?(false) 40 | self.completion = completion 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Demo/Demo/Router/Plugins/RouterSinglePlugin.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RouterSinglePlugin.swift 3 | // Route 4 | // 5 | // Created by 李响 on 2019/4/10. 6 | // Copyright © 2019 swift. All rights reserved. 7 | // 8 | 9 | import Apis 10 | 11 | protocol RouterSingleable: Routerable { 12 | 13 | /// 当前类型 14 | var single: RouterSingleType { get } 15 | 16 | /// 关闭 17 | /// 18 | /// - Parameters: 19 | /// - single: 即将准备打开的类型 20 | /// - completion: 关闭完成回调 21 | func close(will single: RouterSingleType, completion: @escaping (Bool) -> Void) 22 | } 23 | 24 | enum RouterSingleType { 25 | case live 26 | case fast 27 | } 28 | 29 | class RouterSinglePlugin: Apis.PluginType { 30 | 31 | /* 32 | 在准备打开阶段 拦截单一特性页面 33 | (在一些业务场景中 最多只能有一个打开, 不能同时打开的页面的这种特性 简称单一特性) 34 | 35 | 判断当前要打开的类型是否为单一特性类型. 36 | 判断当前是否有相同单一特性的页面已经打开. 37 | 告知原来已经打开的页面即将要打开一个新的页面 请求关闭处理. 38 | */ 39 | func prepare(open target: TargetType, completion: @escaping (Bool) -> Void) { 40 | guard let single = make(target) else { 41 | completion(true) 42 | return 43 | } 44 | guard let current = current?.object else { 45 | completion(true) 46 | return 47 | } 48 | current.close(will: single, completion: completion) 49 | } 50 | 51 | /* 52 | 在即将打开的方法中记录新的单一特性页面, 以供下一次打开时进行操作 53 | */ 54 | func will(open target: TargetType, controller: Routerable) { 55 | guard let controller = controller as? Singleable else { 56 | return 57 | } 58 | current = WeakWrapper(controller) 59 | } 60 | } 61 | 62 | extension RouterSinglePlugin { 63 | 64 | typealias Singleable = RouterSingleable 65 | private static var currentWrapper: WeakWrapper? 66 | 67 | private var current: WeakWrapper? { 68 | get { return RouterSinglePlugin.currentWrapper } 69 | set { RouterSinglePlugin.currentWrapper = newValue } 70 | } 71 | 72 | private class WeakWrapper { 73 | weak var object: Singleable? 74 | init(_ object: Singleable?) { 75 | self.object = object 76 | } 77 | } 78 | } 79 | 80 | extension RouterSinglePlugin { 81 | 82 | func make(_ target: TargetType) -> RouterSingleType? { 83 | guard let target = target as? RouterTarget else { return nil } 84 | switch target { 85 | case .open_live: return .live 86 | case .open_fast: return .fast 87 | default: return nil 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /Demo/Demo/Router/Router.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Router.swift 3 | // Demo 4 | // 5 | // Created by 李响 on 2019/4/12. 6 | // Copyright © 2019 swift. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | import Apis 12 | 13 | enum Router { 14 | 15 | // 初始化Router 并传入需要的插件 16 | static let shared = Apis.Provider( 17 | plugins: 18 | [ 19 | RouterLaunchPlugin(), 20 | RouterAccountPlugin(), 21 | RouterSinglePlugin() 22 | ] 23 | ) 24 | } 25 | 26 | extension Router { 27 | 28 | /// 打开 29 | /// 30 | /// - Parameters: 31 | /// - url: url 32 | /// - completion: 完成回调 33 | /// - Returns: true or false 34 | @discardableResult 35 | static func open(_ url: URLConvertible, 36 | completion: ((Bool) -> Void)? = .none) -> Bool { 37 | return shared.open(url, completion: completion) 38 | } 39 | 40 | /// 获取视图控制器 41 | /// 42 | /// - Parameters: 43 | /// - url: url 44 | /// - context: context 45 | /// - Returns: 视图控制器 46 | static func viewController(_ url: URLConvertible) -> UIViewController? { 47 | return shared.controller(url) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Demo/Demo/Router/RouterTarget.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RouterTarget.swift 3 | // Demo 4 | // 5 | // Created by 李响 on 2021/12/10. 6 | // Copyright © 2021 swift. All rights reserved. 7 | // 8 | 9 | import Apis 10 | import SafariServices 11 | 12 | enum RouterTarget { 13 | case open_http(url: URL) 14 | case open_https(url: URL) 15 | case open_none 16 | case open_live(id: String) 17 | case open_fast 18 | case open_needlogin 19 | case open_some 20 | } 21 | 22 | extension RouterTarget: Apis.TargetType { 23 | 24 | var task: Task { 25 | switch(self) { 26 | case let .open_http(url): 27 | return .controller(SFSafariViewController(url: url)) 28 | 29 | case let .open_https(url): 30 | return .controller(SFSafariViewController(url: url)) 31 | 32 | case .open_none: 33 | return .controller(NoneViewController()) 34 | 35 | case let .open_live(id): 36 | let controller = LiveViewController() 37 | controller.id = id 38 | return .controller(controller) 39 | 40 | case .open_fast: 41 | return .controller(FastViewController()) 42 | 43 | case .open_needlogin: 44 | return .controller(NeedLoginViewController()) 45 | 46 | case .open_some: 47 | return .handle { completion in 48 | // 处理一些其他事情, 结束后务必调用completion回调 49 | DispatchQueue.main.asyncAfter(deadline: .now() + 2) { 50 | // 巴拉巴拉.. 51 | print("do something") 52 | completion(true) 53 | } 54 | } 55 | } 56 | } 57 | } 58 | 59 | private let schemes = "router" 60 | 61 | extension RouterTarget: URLTargetType { 62 | 63 | static var bindings: [URLPatternBinding] { 64 | return [ 65 | .init("http://") { source in 66 | guard let url = source.url.value else { return .none } 67 | return .open_http(url: url) 68 | }, 69 | .init("https://") { source in 70 | guard let url = source.url.value else { return .none } 71 | return .open_https(url: url) 72 | }, 73 | .init(schemes + "://open/none") { 74 | return .open_none 75 | }, 76 | .init(schemes + "://open/fast") { 77 | return .open_fast 78 | }, 79 | .init(schemes + "://open/live") { source in 80 | guard let id = source.url.queryParameters["id"] else { return nil } 81 | return .open_live(id: id) 82 | }, 83 | .init(schemes + "://open/needlogin") { 84 | return .open_needlogin 85 | }, 86 | .init(schemes + "://open/some") { 87 | return .open_some 88 | } 89 | ] 90 | } 91 | } 92 | 93 | // 所有需要支持 Router 的视图控制器都需要实现 Routerable 协议 94 | // Routerable 协议默认实现了通用的打开关闭处理逻辑 如无法满足 可重写 95 | 96 | extension NoneViewController: Routerable { } 97 | extension NeedLoginViewController: Routerable { } 98 | 99 | extension SFSafariViewController: Routerable { 100 | 101 | public func open(with completion: @escaping () -> Void = {}) { 102 | guard let controller = UIViewController.top() else { 103 | return 104 | } 105 | 106 | controller.present(self, animated: true, completion: completion) 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /Demo/Demo/Utils/Notification.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | extension NotificationCenter { 4 | 5 | enum Delegate: Notificationable { 6 | enum defaultKeys: String { 7 | case launched 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Demo/Demo/Utils/Notificationable.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | protocol Notificationable { 4 | associatedtype defaultKeys: RawRepresentable 5 | } 6 | 7 | extension Notificationable where defaultKeys.RawValue == String { 8 | 9 | static func post(_ name: defaultKeys, object: Any? = nil, userInfo: [AnyHashable: Any]? = nil) { 10 | NotificationCenter.default.post( 11 | name: conversion(name), 12 | object: object, 13 | userInfo: userInfo 14 | ) 15 | } 16 | 17 | static func add(_ name: defaultKeys, observer: Any, selector: Selector, object: Any? = nil) { 18 | NotificationCenter.default.addObserver( 19 | observer, 20 | selector: selector, 21 | name: conversion(name), 22 | object: object 23 | ) 24 | } 25 | 26 | static func add(_ name: defaultKeys, 27 | _ object: Any? = nil, 28 | queue: OperationQueue = .main, 29 | using block: @escaping (Notification) -> Void) { 30 | NotificationCenter.default.addObserver( 31 | forName: conversion(name), 32 | object: object, 33 | queue: .main, 34 | using: block 35 | ) 36 | } 37 | 38 | static func remove(_ name: defaultKeys, observer: Any, object: Any? = nil) { 39 | NotificationCenter.default.removeObserver( 40 | observer, 41 | name: conversion(name), 42 | object: object 43 | ) 44 | } 45 | 46 | static private func conversion(_ key: defaultKeys) -> NSNotification.Name { 47 | return NSNotification.Name("\(self).\(key.rawValue)") 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Demo/Demo/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // Demo 4 | // 5 | // Created by 李响 on 2019/4/12. 6 | // Copyright © 2019 swift. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ViewController: UIViewController { 12 | 13 | @IBOutlet weak var tableView: UITableView! 14 | 15 | private var list: [String] = [ 16 | "打开http/https链接", 17 | "打开一个普通的页面", 18 | "打开一个需要登录的页面", 19 | "打开一个单一特性的页面 4s后打开另一个", 20 | "打开一个单一特性的页面 4s后打开新页面" 21 | ] 22 | 23 | override func viewDidLoad() { 24 | super.viewDidLoad() 25 | 26 | setup() 27 | } 28 | 29 | private func setup() { 30 | tableView.delegate = self 31 | tableView.dataSource = self 32 | } 33 | } 34 | 35 | extension ViewController: UITableViewDelegate { 36 | 37 | func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { 38 | return 50 39 | } 40 | } 41 | 42 | extension ViewController: UITableViewDataSource { 43 | 44 | func numberOfSections(in tableView: UITableView) -> Int { 45 | return 1 46 | } 47 | 48 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 49 | return list.count 50 | } 51 | 52 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 53 | let cell = tableView.dequeueReusableCell( 54 | withIdentifier: "cell", 55 | for: indexPath 56 | ) 57 | cell.textLabel?.text = list[indexPath.row] 58 | return cell 59 | } 60 | 61 | func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 62 | tableView.deselectRow(at: indexPath, animated: true) 63 | 64 | switch indexPath.row { 65 | case 0: // 打开http/https链接 66 | Router.open("https://www.baidu.com") 67 | 68 | case 1: // 打开一个普通的页面 69 | Router.open("router://open/none") 70 | 71 | case 2: // 打开一个需要登录的页面 72 | Router.open("router://open/needlogin") 73 | 74 | case 3: // 打开一个单一特性的页面 75 | /* 76 | LiveViewController 和 FastViewController 在业务场景中为单一特性 (最多只能有一个打开, 不能同时打开 简称单一特性) 77 | 这里模拟 LiveViewController 打开后 因某种原因要打开 FastViewController 78 | 演示的逻辑为 关闭 LiveViewController 后再打开 FastViewController 79 | */ 80 | Router.open("router://open/live?id=1") 81 | 82 | DispatchQueue.main.asyncAfter(deadline: .now() + 4) { 83 | Router.open("router://open/fast") 84 | } 85 | 86 | case 4: 87 | /* 88 | LiveViewController 在业务场景中为单一特性 (最多只能有一个打开, 不能同时打开 简称单一特性) 89 | 这里模拟 LiveViewController 打开后 因某种原因要打开另一个 LiveViewController 90 | (比如点击某个推送通知 触发了打开一个新的 LiveViewController) 91 | 演示的逻辑为 关闭 LiveViewController 后再打开新的 LiveViewController 92 | */ 93 | Router.open("router://open/live?id=1") 94 | 95 | DispatchQueue.main.asyncAfter(deadline: .now() + 4) { 96 | Router.open("router://open/live?id=1") 97 | } 98 | 99 | default: 100 | break 101 | } 102 | } 103 | } 104 | 105 | -------------------------------------------------------------------------------- /Demo/Podfile: -------------------------------------------------------------------------------- 1 | platform :ios, '10.0' 2 | inhibit_all_warnings! 3 | 4 | target 'Demo' do 5 | use_frameworks! 6 | 7 | pod 'Apis', :path => "../" 8 | pod 'SnapKit' , '~>5.0.0' 9 | 10 | end 11 | -------------------------------------------------------------------------------- /Demo/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Apis (1.2.2): 3 | - Apis/Privacy (= 1.2.2) 4 | - Apis/Privacy (1.2.2) 5 | - SnapKit (5.0.1) 6 | 7 | DEPENDENCIES: 8 | - Apis (from `../`) 9 | - SnapKit (~> 5.0.0) 10 | 11 | SPEC REPOS: 12 | trunk: 13 | - SnapKit 14 | 15 | EXTERNAL SOURCES: 16 | Apis: 17 | :path: "../" 18 | 19 | SPEC CHECKSUMS: 20 | Apis: e618055c841dbabc0b444f6bb9575831139758bc 21 | SnapKit: 97b92857e3df3a0c71833cce143274bf6ef8e5eb 22 | 23 | PODFILE CHECKSUM: 199c77b4710d6bba7448d0daa0a0ecd06f10ef6c 24 | 25 | COCOAPODS: 1.15.2 26 | -------------------------------------------------------------------------------- /Demo/Pods/Local Podspecs/Apis.podspec.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Apis", 3 | "version": "1.2.2", 4 | "summary": "基于URLNavigator抽象的URL路由组件 灵感来自Moya 配置化 插件化.", 5 | "homepage": "https://github.com/lixiang1994/Apis", 6 | "license": { 7 | "type": "MIT", 8 | "file": "LICENSE" 9 | }, 10 | "authors": { 11 | "LEE": "18611401994@163.com" 12 | }, 13 | "platforms": { 14 | "ios": "9.0" 15 | }, 16 | "source": { 17 | "git": "https://github.com/lixiang1994/Apis.git", 18 | "tag": "1.2.2" 19 | }, 20 | "source_files": "Sources/**/*.swift", 21 | "requires_arc": true, 22 | "frameworks": [ 23 | "UIKit", 24 | "Foundation" 25 | ], 26 | "swift_versions": "5.0", 27 | "subspecs": [ 28 | { 29 | "name": "Privacy", 30 | "resource_bundles": { 31 | "Apis": "Sources/PrivacyInfo.xcprivacy" 32 | } 33 | } 34 | ], 35 | "swift_version": "5.0" 36 | } 37 | -------------------------------------------------------------------------------- /Demo/Pods/Local Podspecs/Router.podspec.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Router", 3 | "version": "2.0.0", 4 | "summary": "基于URLNavigator抽象的路由组件 支持任意类型配置 插件机制", 5 | "homepage": "https://github.com/lixiang1994/Router", 6 | "license": { 7 | "type": "MIT", 8 | "file": "LICENSE" 9 | }, 10 | "authors": { 11 | "LEE": "18611401994@163.com" 12 | }, 13 | "platforms": { 14 | "ios": "9.0" 15 | }, 16 | "source": { 17 | "git": "https://github.com/lixiang1994/Router.git", 18 | "tag": "2.0.0" 19 | }, 20 | "source_files": "Sources/**/*.swift", 21 | "requires_arc": true, 22 | "frameworks": [ 23 | "UIKit", 24 | "Foundation" 25 | ], 26 | "swift_versions": "5.0", 27 | "swift_version": "5.0" 28 | } 29 | -------------------------------------------------------------------------------- /Demo/Pods/Manifest.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Apis (1.2.2): 3 | - Apis/Privacy (= 1.2.2) 4 | - Apis/Privacy (1.2.2) 5 | - SnapKit (5.0.1) 6 | 7 | DEPENDENCIES: 8 | - Apis (from `../`) 9 | - SnapKit (~> 5.0.0) 10 | 11 | SPEC REPOS: 12 | trunk: 13 | - SnapKit 14 | 15 | EXTERNAL SOURCES: 16 | Apis: 17 | :path: "../" 18 | 19 | SPEC CHECKSUMS: 20 | Apis: e618055c841dbabc0b444f6bb9575831139758bc 21 | SnapKit: 97b92857e3df3a0c71833cce143274bf6ef8e5eb 22 | 23 | PODFILE CHECKSUM: 199c77b4710d6bba7448d0daa0a0ecd06f10ef6c 24 | 25 | COCOAPODS: 1.15.2 26 | -------------------------------------------------------------------------------- /Demo/Pods/SnapKit/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /Demo/Pods/SnapKit/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | SnapKit is a DSL to make Auto Layout easy on both iOS and OS X. 4 | 5 | [![Build Status](https://travis-ci.org/SnapKit/SnapKit.svg)](https://travis-ci.org/SnapKit/SnapKit) 6 | [![Platform](https://img.shields.io/cocoapods/p/SnapKit.svg?style=flat)](https://github.com/SnapKit/SnapKit) 7 | [![Cocoapods Compatible](https://img.shields.io/cocoapods/v/SnapKit.svg)](https://cocoapods.org/pods/SnapKit) 8 | [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) 9 | 10 | #### ⚠️ **To use with Swift 4.x please ensure you are using >= 4.0.0** ⚠️ 11 | #### ⚠️ **To use with Swift 5.x please ensure you are using >= 5.0.0** ⚠️ 12 | 13 | ## Contents 14 | 15 | - [Requirements](#requirements) 16 | - [Migration Guides](#migration-guides) 17 | - [Communication](#communication) 18 | - [Installation](#installation) 19 | - [Usage](#usage) 20 | - [Credits](#credits) 21 | - [License](#license) 22 | 23 | ## Requirements 24 | 25 | - iOS 10.0+ / Mac OS X 10.12+ / tvOS 10.0+ 26 | - Xcode 10.0+ 27 | - Swift 4.0+ 28 | 29 | ## Communication 30 | 31 | - If you **need help**, use [Stack Overflow](http://stackoverflow.com/questions/tagged/snapkit). (Tag 'snapkit') 32 | - If you'd like to **ask a general question**, use [Stack Overflow](http://stackoverflow.com/questions/tagged/snapkit). 33 | - If you **found a bug**, open an issue. 34 | - If you **have a feature request**, open an issue. 35 | - If you **want to contribute**, submit a pull request. 36 | 37 | 38 | ## Installation 39 | 40 | ### CocoaPods 41 | 42 | [CocoaPods](http://cocoapods.org) is a dependency manager for Cocoa projects. You can install it with the following command: 43 | 44 | ```bash 45 | $ gem install cocoapods 46 | ``` 47 | 48 | > CocoaPods 1.1.0+ is required to build SnapKit 4.0.0+. 49 | 50 | To integrate SnapKit into your Xcode project using CocoaPods, specify it in your `Podfile`: 51 | 52 | ```ruby 53 | source 'https://github.com/CocoaPods/Specs.git' 54 | platform :ios, '10.0' 55 | use_frameworks! 56 | 57 | target '' do 58 | pod 'SnapKit', '~> 5.0.0' 59 | end 60 | ``` 61 | 62 | Then, run the following command: 63 | 64 | ```bash 65 | $ pod install 66 | ``` 67 | 68 | ### Carthage 69 | 70 | [Carthage](https://github.com/Carthage/Carthage) is a decentralized dependency manager that builds your dependencies and provides you with binary frameworks. 71 | 72 | You can install Carthage with [Homebrew](http://brew.sh/) using the following command: 73 | 74 | ```bash 75 | $ brew update 76 | $ brew install carthage 77 | ``` 78 | 79 | To integrate SnapKit into your Xcode project using Carthage, specify it in your `Cartfile`: 80 | 81 | ```ogdl 82 | github "SnapKit/SnapKit" ~> 5.0.0 83 | ``` 84 | 85 | Run `carthage update` to build the framework and drag the built `SnapKit.framework` into your Xcode project. 86 | 87 | ### Manually 88 | 89 | If you prefer not to use either of the aforementioned dependency managers, you can integrate SnapKit into your project manually. 90 | 91 | --- 92 | 93 | ## Usage 94 | 95 | ### Quick Start 96 | 97 | ```swift 98 | import SnapKit 99 | 100 | class MyViewController: UIViewController { 101 | 102 | lazy var box = UIView() 103 | 104 | override func viewDidLoad() { 105 | super.viewDidLoad() 106 | 107 | self.view.addSubview(box) 108 | box.snp.makeConstraints { (make) -> Void in 109 | make.width.height.equalTo(50) 110 | make.center.equalTo(self.view) 111 | } 112 | } 113 | 114 | } 115 | ``` 116 | 117 | ### Playground 118 | You can try SnapKit in Playground. 119 | 120 | **Note:** 121 | 122 | > To try SnapKit in playground, open `SnapKit.xcworkspace` and build SnapKit.framework for any simulator first. 123 | 124 | ### Resources 125 | 126 | - [Documentation](http://snapkit.io/docs/) 127 | - [F.A.Q.](http://snapkit.io/faq/) 128 | 129 | ## Credits 130 | 131 | - Robert Payne ([@robertjpayne](https://twitter.com/robertjpayne)) 132 | - Many other contributors 133 | 134 | ## License 135 | 136 | SnapKit is released under the MIT license. See LICENSE for details. 137 | -------------------------------------------------------------------------------- /Demo/Pods/SnapKit/Source/ConstraintConfig.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SnapKit 3 | // 4 | // Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit 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 14 | // all 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 22 | // THE SOFTWARE. 23 | 24 | #if os(iOS) || os(tvOS) 25 | import UIKit 26 | public typealias ConstraintInterfaceLayoutDirection = UIUserInterfaceLayoutDirection 27 | #else 28 | import AppKit 29 | public typealias ConstraintInterfaceLayoutDirection = NSUserInterfaceLayoutDirection 30 | #endif 31 | 32 | 33 | public struct ConstraintConfig { 34 | 35 | public static var interfaceLayoutDirection: ConstraintInterfaceLayoutDirection = .leftToRight 36 | 37 | } 38 | -------------------------------------------------------------------------------- /Demo/Pods/SnapKit/Source/ConstraintDSL.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SnapKit 3 | // 4 | // Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit 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 14 | // all 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 22 | // THE SOFTWARE. 23 | 24 | #if os(iOS) || os(tvOS) 25 | import UIKit 26 | #else 27 | import AppKit 28 | #endif 29 | 30 | 31 | public protocol ConstraintDSL { 32 | 33 | var target: AnyObject? { get } 34 | 35 | func setLabel(_ value: String?) 36 | func label() -> String? 37 | 38 | } 39 | extension ConstraintDSL { 40 | 41 | public func setLabel(_ value: String?) { 42 | objc_setAssociatedObject(self.target as Any, &labelKey, value, .OBJC_ASSOCIATION_COPY_NONATOMIC) 43 | } 44 | public func label() -> String? { 45 | return objc_getAssociatedObject(self.target as Any, &labelKey) as? String 46 | } 47 | 48 | } 49 | private var labelKey: UInt8 = 0 50 | 51 | 52 | public protocol ConstraintBasicAttributesDSL : ConstraintDSL { 53 | } 54 | extension ConstraintBasicAttributesDSL { 55 | 56 | // MARK: Basics 57 | 58 | public var left: ConstraintItem { 59 | return ConstraintItem(target: self.target, attributes: ConstraintAttributes.left) 60 | } 61 | 62 | public var top: ConstraintItem { 63 | return ConstraintItem(target: self.target, attributes: ConstraintAttributes.top) 64 | } 65 | 66 | public var right: ConstraintItem { 67 | return ConstraintItem(target: self.target, attributes: ConstraintAttributes.right) 68 | } 69 | 70 | public var bottom: ConstraintItem { 71 | return ConstraintItem(target: self.target, attributes: ConstraintAttributes.bottom) 72 | } 73 | 74 | public var leading: ConstraintItem { 75 | return ConstraintItem(target: self.target, attributes: ConstraintAttributes.leading) 76 | } 77 | 78 | public var trailing: ConstraintItem { 79 | return ConstraintItem(target: self.target, attributes: ConstraintAttributes.trailing) 80 | } 81 | 82 | public var width: ConstraintItem { 83 | return ConstraintItem(target: self.target, attributes: ConstraintAttributes.width) 84 | } 85 | 86 | public var height: ConstraintItem { 87 | return ConstraintItem(target: self.target, attributes: ConstraintAttributes.height) 88 | } 89 | 90 | public var centerX: ConstraintItem { 91 | return ConstraintItem(target: self.target, attributes: ConstraintAttributes.centerX) 92 | } 93 | 94 | public var centerY: ConstraintItem { 95 | return ConstraintItem(target: self.target, attributes: ConstraintAttributes.centerY) 96 | } 97 | 98 | public var edges: ConstraintItem { 99 | return ConstraintItem(target: self.target, attributes: ConstraintAttributes.edges) 100 | } 101 | 102 | public var directionalEdges: ConstraintItem { 103 | return ConstraintItem(target: self.target, attributes: ConstraintAttributes.directionalEdges) 104 | } 105 | 106 | public var size: ConstraintItem { 107 | return ConstraintItem(target: self.target, attributes: ConstraintAttributes.size) 108 | } 109 | 110 | public var center: ConstraintItem { 111 | return ConstraintItem(target: self.target, attributes: ConstraintAttributes.center) 112 | } 113 | 114 | } 115 | 116 | public protocol ConstraintAttributesDSL : ConstraintBasicAttributesDSL { 117 | } 118 | extension ConstraintAttributesDSL { 119 | 120 | // MARK: Baselines 121 | 122 | @available(*, deprecated, message:"Use .lastBaseline instead") 123 | public var baseline: ConstraintItem { 124 | return ConstraintItem(target: self.target, attributes: ConstraintAttributes.lastBaseline) 125 | } 126 | 127 | @available(iOS 8.0, OSX 10.11, *) 128 | public var lastBaseline: ConstraintItem { 129 | return ConstraintItem(target: self.target, attributes: ConstraintAttributes.lastBaseline) 130 | } 131 | 132 | @available(iOS 8.0, OSX 10.11, *) 133 | public var firstBaseline: ConstraintItem { 134 | return ConstraintItem(target: self.target, attributes: ConstraintAttributes.firstBaseline) 135 | } 136 | 137 | // MARK: Margins 138 | 139 | @available(iOS 8.0, *) 140 | public var leftMargin: ConstraintItem { 141 | return ConstraintItem(target: self.target, attributes: ConstraintAttributes.leftMargin) 142 | } 143 | 144 | @available(iOS 8.0, *) 145 | public var topMargin: ConstraintItem { 146 | return ConstraintItem(target: self.target, attributes: ConstraintAttributes.topMargin) 147 | } 148 | 149 | @available(iOS 8.0, *) 150 | public var rightMargin: ConstraintItem { 151 | return ConstraintItem(target: self.target, attributes: ConstraintAttributes.rightMargin) 152 | } 153 | 154 | @available(iOS 8.0, *) 155 | public var bottomMargin: ConstraintItem { 156 | return ConstraintItem(target: self.target, attributes: ConstraintAttributes.bottomMargin) 157 | } 158 | 159 | @available(iOS 8.0, *) 160 | public var leadingMargin: ConstraintItem { 161 | return ConstraintItem(target: self.target, attributes: ConstraintAttributes.leadingMargin) 162 | } 163 | 164 | @available(iOS 8.0, *) 165 | public var trailingMargin: ConstraintItem { 166 | return ConstraintItem(target: self.target, attributes: ConstraintAttributes.trailingMargin) 167 | } 168 | 169 | @available(iOS 8.0, *) 170 | public var centerXWithinMargins: ConstraintItem { 171 | return ConstraintItem(target: self.target, attributes: ConstraintAttributes.centerXWithinMargins) 172 | } 173 | 174 | @available(iOS 8.0, *) 175 | public var centerYWithinMargins: ConstraintItem { 176 | return ConstraintItem(target: self.target, attributes: ConstraintAttributes.centerYWithinMargins) 177 | } 178 | 179 | @available(iOS 8.0, *) 180 | public var margins: ConstraintItem { 181 | return ConstraintItem(target: self.target, attributes: ConstraintAttributes.margins) 182 | } 183 | 184 | @available(iOS 8.0, *) 185 | public var directionalMargins: ConstraintItem { 186 | return ConstraintItem(target: self.target, attributes: ConstraintAttributes.directionalMargins) 187 | } 188 | 189 | @available(iOS 8.0, *) 190 | public var centerWithinMargins: ConstraintItem { 191 | return ConstraintItem(target: self.target, attributes: ConstraintAttributes.centerWithinMargins) 192 | } 193 | 194 | } 195 | -------------------------------------------------------------------------------- /Demo/Pods/SnapKit/Source/ConstraintDescription.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SnapKit 3 | // 4 | // Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit 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 14 | // all 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 22 | // THE SOFTWARE. 23 | 24 | #if os(iOS) || os(tvOS) 25 | import UIKit 26 | #else 27 | import AppKit 28 | #endif 29 | 30 | 31 | public class ConstraintDescription { 32 | 33 | internal let item: LayoutConstraintItem 34 | internal var attributes: ConstraintAttributes 35 | internal var relation: ConstraintRelation? = nil 36 | internal var sourceLocation: (String, UInt)? = nil 37 | internal var label: String? = nil 38 | internal var related: ConstraintItem? = nil 39 | internal var multiplier: ConstraintMultiplierTarget = 1.0 40 | internal var constant: ConstraintConstantTarget = 0.0 41 | internal var priority: ConstraintPriorityTarget = 1000.0 42 | internal lazy var constraint: Constraint? = { 43 | guard let relation = self.relation, 44 | let related = self.related, 45 | let sourceLocation = self.sourceLocation else { 46 | return nil 47 | } 48 | let from = ConstraintItem(target: self.item, attributes: self.attributes) 49 | 50 | return Constraint( 51 | from: from, 52 | to: related, 53 | relation: relation, 54 | sourceLocation: sourceLocation, 55 | label: self.label, 56 | multiplier: self.multiplier, 57 | constant: self.constant, 58 | priority: self.priority 59 | ) 60 | }() 61 | 62 | // MARK: Initialization 63 | 64 | internal init(item: LayoutConstraintItem, attributes: ConstraintAttributes) { 65 | self.item = item 66 | self.attributes = attributes 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /Demo/Pods/SnapKit/Source/ConstraintDirectionalInsetTarget.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SnapKit 3 | // 4 | // Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit 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 14 | // all 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 22 | // THE SOFTWARE. 23 | 24 | #if os(iOS) || os(tvOS) 25 | import UIKit 26 | #else 27 | import AppKit 28 | #endif 29 | 30 | #if os(iOS) || os(tvOS) 31 | public protocol ConstraintDirectionalInsetTarget: ConstraintConstantTarget { 32 | } 33 | 34 | @available(iOS 11.0, tvOS 11.0, *) 35 | extension ConstraintDirectionalInsets: ConstraintDirectionalInsetTarget { 36 | } 37 | 38 | extension ConstraintDirectionalInsetTarget { 39 | 40 | @available(iOS 11.0, tvOS 11.0, *) 41 | internal var constraintDirectionalInsetTargetValue: ConstraintDirectionalInsets { 42 | if let amount = self as? ConstraintDirectionalInsets { 43 | return amount 44 | } else { 45 | return ConstraintDirectionalInsets(top: 0, leading: 0, bottom: 0, trailing: 0) 46 | } 47 | } 48 | } 49 | #endif 50 | -------------------------------------------------------------------------------- /Demo/Pods/SnapKit/Source/ConstraintDirectionalInsets.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SnapKit 3 | // 4 | // Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit 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 14 | // all 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 22 | // THE SOFTWARE. 23 | 24 | #if os(iOS) || os(tvOS) 25 | import UIKit 26 | #else 27 | import AppKit 28 | #endif 29 | 30 | 31 | #if os(iOS) || os(tvOS) 32 | @available(iOS 11.0, tvOS 11.0, *) 33 | public typealias ConstraintDirectionalInsets = NSDirectionalEdgeInsets 34 | #endif 35 | -------------------------------------------------------------------------------- /Demo/Pods/SnapKit/Source/ConstraintInsetTarget.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SnapKit 3 | // 4 | // Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit 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 14 | // all 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 22 | // THE SOFTWARE. 23 | 24 | #if os(iOS) || os(tvOS) 25 | import UIKit 26 | #else 27 | import AppKit 28 | #endif 29 | 30 | 31 | public protocol ConstraintInsetTarget: ConstraintConstantTarget { 32 | } 33 | 34 | extension Int: ConstraintInsetTarget { 35 | } 36 | 37 | extension UInt: ConstraintInsetTarget { 38 | } 39 | 40 | extension Float: ConstraintInsetTarget { 41 | } 42 | 43 | extension Double: ConstraintInsetTarget { 44 | } 45 | 46 | extension CGFloat: ConstraintInsetTarget { 47 | } 48 | 49 | extension ConstraintInsets: ConstraintInsetTarget { 50 | } 51 | 52 | extension ConstraintInsetTarget { 53 | 54 | internal var constraintInsetTargetValue: ConstraintInsets { 55 | if let amount = self as? ConstraintInsets { 56 | return amount 57 | } else if let amount = self as? Float { 58 | return ConstraintInsets(top: CGFloat(amount), left: CGFloat(amount), bottom: CGFloat(amount), right: CGFloat(amount)) 59 | } else if let amount = self as? Double { 60 | return ConstraintInsets(top: CGFloat(amount), left: CGFloat(amount), bottom: CGFloat(amount), right: CGFloat(amount)) 61 | } else if let amount = self as? CGFloat { 62 | return ConstraintInsets(top: amount, left: amount, bottom: amount, right: amount) 63 | } else if let amount = self as? Int { 64 | return ConstraintInsets(top: CGFloat(amount), left: CGFloat(amount), bottom: CGFloat(amount), right: CGFloat(amount)) 65 | } else if let amount = self as? UInt { 66 | return ConstraintInsets(top: CGFloat(amount), left: CGFloat(amount), bottom: CGFloat(amount), right: CGFloat(amount)) 67 | } else { 68 | return ConstraintInsets(top: 0, left: 0, bottom: 0, right: 0) 69 | } 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /Demo/Pods/SnapKit/Source/ConstraintInsets.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SnapKit 3 | // 4 | // Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit 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 14 | // all 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 22 | // THE SOFTWARE. 23 | 24 | #if os(iOS) || os(tvOS) 25 | import UIKit 26 | #else 27 | import AppKit 28 | #endif 29 | 30 | 31 | #if os(iOS) || os(tvOS) 32 | public typealias ConstraintInsets = UIEdgeInsets 33 | #else 34 | public typealias ConstraintInsets = NSEdgeInsets 35 | #endif 36 | -------------------------------------------------------------------------------- /Demo/Pods/SnapKit/Source/ConstraintItem.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SnapKit 3 | // 4 | // Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit 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 14 | // all 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 22 | // THE SOFTWARE. 23 | 24 | #if os(iOS) || os(tvOS) 25 | import UIKit 26 | #else 27 | import AppKit 28 | #endif 29 | 30 | 31 | public final class ConstraintItem { 32 | 33 | internal weak var target: AnyObject? 34 | internal let attributes: ConstraintAttributes 35 | 36 | internal init(target: AnyObject?, attributes: ConstraintAttributes) { 37 | self.target = target 38 | self.attributes = attributes 39 | } 40 | 41 | internal var layoutConstraintItem: LayoutConstraintItem? { 42 | return self.target as? LayoutConstraintItem 43 | } 44 | 45 | } 46 | 47 | public func ==(lhs: ConstraintItem, rhs: ConstraintItem) -> Bool { 48 | // pointer equality 49 | guard lhs !== rhs else { 50 | return true 51 | } 52 | 53 | // must both have valid targets and identical attributes 54 | guard let target1 = lhs.target, 55 | let target2 = rhs.target, 56 | target1 === target2 && lhs.attributes == rhs.attributes else { 57 | return false 58 | } 59 | 60 | return true 61 | } 62 | -------------------------------------------------------------------------------- /Demo/Pods/SnapKit/Source/ConstraintLayoutGuide+Extensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SnapKit 3 | // 4 | // Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit 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 14 | // all 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 22 | // THE SOFTWARE. 23 | 24 | #if os(iOS) || os(tvOS) 25 | import UIKit 26 | #endif 27 | 28 | 29 | @available(iOS 9.0, OSX 10.11, *) 30 | public extension ConstraintLayoutGuide { 31 | 32 | var snp: ConstraintLayoutGuideDSL { 33 | return ConstraintLayoutGuideDSL(guide: self) 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /Demo/Pods/SnapKit/Source/ConstraintLayoutGuide.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SnapKit 3 | // 4 | // Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit 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 14 | // all 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 22 | // THE SOFTWARE. 23 | 24 | #if os(iOS) || os(tvOS) 25 | import UIKit 26 | #else 27 | import AppKit 28 | #endif 29 | 30 | 31 | #if os(iOS) || os(tvOS) 32 | @available(iOS 9.0, *) 33 | public typealias ConstraintLayoutGuide = UILayoutGuide 34 | #else 35 | @available(OSX 10.11, *) 36 | public typealias ConstraintLayoutGuide = NSLayoutGuide 37 | #endif 38 | -------------------------------------------------------------------------------- /Demo/Pods/SnapKit/Source/ConstraintLayoutGuideDSL.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SnapKit 3 | // 4 | // Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit 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 14 | // all 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 22 | // THE SOFTWARE. 23 | 24 | #if os(iOS) || os(tvOS) 25 | import UIKit 26 | #else 27 | import AppKit 28 | #endif 29 | 30 | 31 | @available(iOS 9.0, OSX 10.11, *) 32 | public struct ConstraintLayoutGuideDSL: ConstraintAttributesDSL { 33 | 34 | @discardableResult 35 | public func prepareConstraints(_ closure: (_ make: ConstraintMaker) -> Void) -> [Constraint] { 36 | return ConstraintMaker.prepareConstraints(item: self.guide, closure: closure) 37 | } 38 | 39 | public func makeConstraints(_ closure: (_ make: ConstraintMaker) -> Void) { 40 | ConstraintMaker.makeConstraints(item: self.guide, closure: closure) 41 | } 42 | 43 | public func remakeConstraints(_ closure: (_ make: ConstraintMaker) -> Void) { 44 | ConstraintMaker.remakeConstraints(item: self.guide, closure: closure) 45 | } 46 | 47 | public func updateConstraints(_ closure: (_ make: ConstraintMaker) -> Void) { 48 | ConstraintMaker.updateConstraints(item: self.guide, closure: closure) 49 | } 50 | 51 | public func removeConstraints() { 52 | ConstraintMaker.removeConstraints(item: self.guide) 53 | } 54 | 55 | public var target: AnyObject? { 56 | return self.guide 57 | } 58 | 59 | internal let guide: ConstraintLayoutGuide 60 | 61 | internal init(guide: ConstraintLayoutGuide) { 62 | self.guide = guide 63 | 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /Demo/Pods/SnapKit/Source/ConstraintLayoutSupport.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SnapKit 3 | // 4 | // Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit 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 14 | // all 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 22 | // THE SOFTWARE. 23 | 24 | #if os(iOS) || os(tvOS) 25 | import UIKit 26 | #else 27 | import AppKit 28 | #endif 29 | 30 | 31 | #if os(iOS) || os(tvOS) 32 | @available(iOS 8.0, *) 33 | public typealias ConstraintLayoutSupport = UILayoutSupport 34 | #else 35 | public class ConstraintLayoutSupport {} 36 | #endif 37 | -------------------------------------------------------------------------------- /Demo/Pods/SnapKit/Source/ConstraintLayoutSupportDSL.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SnapKit 3 | // 4 | // Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit 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 14 | // all 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 22 | // THE SOFTWARE. 23 | 24 | #if os(iOS) || os(tvOS) 25 | import UIKit 26 | #else 27 | import AppKit 28 | #endif 29 | 30 | 31 | @available(iOS 8.0, *) 32 | public struct ConstraintLayoutSupportDSL: ConstraintDSL { 33 | 34 | public var target: AnyObject? { 35 | return self.support 36 | } 37 | 38 | internal let support: ConstraintLayoutSupport 39 | 40 | internal init(support: ConstraintLayoutSupport) { 41 | self.support = support 42 | 43 | } 44 | 45 | public var top: ConstraintItem { 46 | return ConstraintItem(target: self.target, attributes: ConstraintAttributes.top) 47 | } 48 | 49 | public var bottom: ConstraintItem { 50 | return ConstraintItem(target: self.target, attributes: ConstraintAttributes.bottom) 51 | } 52 | 53 | public var height: ConstraintItem { 54 | return ConstraintItem(target: self.target, attributes: ConstraintAttributes.height) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Demo/Pods/SnapKit/Source/ConstraintMakerEditable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SnapKit 3 | // 4 | // Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit 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 14 | // all 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 22 | // THE SOFTWARE. 23 | 24 | #if os(iOS) || os(tvOS) 25 | import UIKit 26 | #else 27 | import AppKit 28 | #endif 29 | 30 | 31 | public class ConstraintMakerEditable: ConstraintMakerPriortizable { 32 | 33 | @discardableResult 34 | public func multipliedBy(_ amount: ConstraintMultiplierTarget) -> ConstraintMakerEditable { 35 | self.description.multiplier = amount 36 | return self 37 | } 38 | 39 | @discardableResult 40 | public func dividedBy(_ amount: ConstraintMultiplierTarget) -> ConstraintMakerEditable { 41 | return self.multipliedBy(1.0 / amount.constraintMultiplierTargetValue) 42 | } 43 | 44 | @discardableResult 45 | public func offset(_ amount: ConstraintOffsetTarget) -> ConstraintMakerEditable { 46 | self.description.constant = amount.constraintOffsetTargetValue 47 | return self 48 | } 49 | 50 | @discardableResult 51 | public func inset(_ amount: ConstraintInsetTarget) -> ConstraintMakerEditable { 52 | self.description.constant = amount.constraintInsetTargetValue 53 | return self 54 | } 55 | 56 | #if os(iOS) || os(tvOS) 57 | @discardableResult 58 | @available(iOS 11.0, tvOS 11.0, *) 59 | public func inset(_ amount: ConstraintDirectionalInsetTarget) -> ConstraintMakerEditable { 60 | self.description.constant = amount.constraintDirectionalInsetTargetValue 61 | return self 62 | } 63 | #endif 64 | } 65 | -------------------------------------------------------------------------------- /Demo/Pods/SnapKit/Source/ConstraintMakerExtendable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SnapKit 3 | // 4 | // Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit 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 14 | // all 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 22 | // THE SOFTWARE. 23 | 24 | #if os(iOS) || os(tvOS) 25 | import UIKit 26 | #else 27 | import AppKit 28 | #endif 29 | 30 | 31 | public class ConstraintMakerExtendable: ConstraintMakerRelatable { 32 | 33 | public var left: ConstraintMakerExtendable { 34 | self.description.attributes += .left 35 | return self 36 | } 37 | 38 | public var top: ConstraintMakerExtendable { 39 | self.description.attributes += .top 40 | return self 41 | } 42 | 43 | public var bottom: ConstraintMakerExtendable { 44 | self.description.attributes += .bottom 45 | return self 46 | } 47 | 48 | public var right: ConstraintMakerExtendable { 49 | self.description.attributes += .right 50 | return self 51 | } 52 | 53 | public var leading: ConstraintMakerExtendable { 54 | self.description.attributes += .leading 55 | return self 56 | } 57 | 58 | public var trailing: ConstraintMakerExtendable { 59 | self.description.attributes += .trailing 60 | return self 61 | } 62 | 63 | public var width: ConstraintMakerExtendable { 64 | self.description.attributes += .width 65 | return self 66 | } 67 | 68 | public var height: ConstraintMakerExtendable { 69 | self.description.attributes += .height 70 | return self 71 | } 72 | 73 | public var centerX: ConstraintMakerExtendable { 74 | self.description.attributes += .centerX 75 | return self 76 | } 77 | 78 | public var centerY: ConstraintMakerExtendable { 79 | self.description.attributes += .centerY 80 | return self 81 | } 82 | 83 | @available(*, deprecated, message:"Use lastBaseline instead") 84 | public var baseline: ConstraintMakerExtendable { 85 | self.description.attributes += .lastBaseline 86 | return self 87 | } 88 | 89 | public var lastBaseline: ConstraintMakerExtendable { 90 | self.description.attributes += .lastBaseline 91 | return self 92 | } 93 | 94 | @available(iOS 8.0, OSX 10.11, *) 95 | public var firstBaseline: ConstraintMakerExtendable { 96 | self.description.attributes += .firstBaseline 97 | return self 98 | } 99 | 100 | @available(iOS 8.0, *) 101 | public var leftMargin: ConstraintMakerExtendable { 102 | self.description.attributes += .leftMargin 103 | return self 104 | } 105 | 106 | @available(iOS 8.0, *) 107 | public var rightMargin: ConstraintMakerExtendable { 108 | self.description.attributes += .rightMargin 109 | return self 110 | } 111 | 112 | @available(iOS 8.0, *) 113 | public var topMargin: ConstraintMakerExtendable { 114 | self.description.attributes += .topMargin 115 | return self 116 | } 117 | 118 | @available(iOS 8.0, *) 119 | public var bottomMargin: ConstraintMakerExtendable { 120 | self.description.attributes += .bottomMargin 121 | return self 122 | } 123 | 124 | @available(iOS 8.0, *) 125 | public var leadingMargin: ConstraintMakerExtendable { 126 | self.description.attributes += .leadingMargin 127 | return self 128 | } 129 | 130 | @available(iOS 8.0, *) 131 | public var trailingMargin: ConstraintMakerExtendable { 132 | self.description.attributes += .trailingMargin 133 | return self 134 | } 135 | 136 | @available(iOS 8.0, *) 137 | public var centerXWithinMargins: ConstraintMakerExtendable { 138 | self.description.attributes += .centerXWithinMargins 139 | return self 140 | } 141 | 142 | @available(iOS 8.0, *) 143 | public var centerYWithinMargins: ConstraintMakerExtendable { 144 | self.description.attributes += .centerYWithinMargins 145 | return self 146 | } 147 | 148 | public var edges: ConstraintMakerExtendable { 149 | self.description.attributes += .edges 150 | return self 151 | } 152 | public var directionalEdges: ConstraintMakerExtendable { 153 | self.description.attributes += .directionalEdges 154 | return self 155 | } 156 | public var size: ConstraintMakerExtendable { 157 | self.description.attributes += .size 158 | return self 159 | } 160 | 161 | @available(iOS 8.0, *) 162 | public var margins: ConstraintMakerExtendable { 163 | self.description.attributes += .margins 164 | return self 165 | } 166 | 167 | @available(iOS 8.0, *) 168 | public var directionalMargins: ConstraintMakerExtendable { 169 | self.description.attributes += .directionalMargins 170 | return self 171 | } 172 | 173 | @available(iOS 8.0, *) 174 | public var centerWithinMargins: ConstraintMakerExtendable { 175 | self.description.attributes += .centerWithinMargins 176 | return self 177 | } 178 | 179 | } 180 | -------------------------------------------------------------------------------- /Demo/Pods/SnapKit/Source/ConstraintMakerFinalizable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SnapKit 3 | // 4 | // Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit 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 14 | // all 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 22 | // THE SOFTWARE. 23 | 24 | #if os(iOS) || os(tvOS) 25 | import UIKit 26 | #else 27 | import AppKit 28 | #endif 29 | 30 | 31 | public class ConstraintMakerFinalizable { 32 | 33 | internal let description: ConstraintDescription 34 | 35 | internal init(_ description: ConstraintDescription) { 36 | self.description = description 37 | } 38 | 39 | @discardableResult 40 | public func labeled(_ label: String) -> ConstraintMakerFinalizable { 41 | self.description.label = label 42 | return self 43 | } 44 | 45 | public var constraint: Constraint { 46 | return self.description.constraint! 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /Demo/Pods/SnapKit/Source/ConstraintMakerPriortizable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SnapKit 3 | // 4 | // Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit 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 14 | // all 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 22 | // THE SOFTWARE. 23 | 24 | #if os(iOS) || os(tvOS) 25 | import UIKit 26 | #else 27 | import AppKit 28 | #endif 29 | 30 | 31 | public class ConstraintMakerPriortizable: ConstraintMakerFinalizable { 32 | 33 | @discardableResult 34 | public func priority(_ amount: ConstraintPriority) -> ConstraintMakerFinalizable { 35 | self.description.priority = amount.value 36 | return self 37 | } 38 | 39 | @discardableResult 40 | public func priority(_ amount: ConstraintPriorityTarget) -> ConstraintMakerFinalizable { 41 | self.description.priority = amount 42 | return self 43 | } 44 | 45 | @available(*, deprecated, message:"Use priority(.required) instead.") 46 | @discardableResult 47 | public func priorityRequired() -> ConstraintMakerFinalizable { 48 | return self.priority(.required) 49 | } 50 | 51 | @available(*, deprecated, message:"Use priority(.high) instead.") 52 | @discardableResult 53 | public func priorityHigh() -> ConstraintMakerFinalizable { 54 | return self.priority(.high) 55 | } 56 | 57 | @available(*, deprecated, message:"Use priority(.medium) instead.") 58 | @discardableResult 59 | public func priorityMedium() -> ConstraintMakerFinalizable { 60 | return self.priority(.medium) 61 | } 62 | 63 | @available(*, deprecated, message:"Use priority(.low) instead.") 64 | @discardableResult 65 | public func priorityLow() -> ConstraintMakerFinalizable { 66 | return self.priority(.low) 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /Demo/Pods/SnapKit/Source/ConstraintMakerRelatable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SnapKit 3 | // 4 | // Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit 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 14 | // all 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 22 | // THE SOFTWARE. 23 | 24 | #if os(iOS) || os(tvOS) 25 | import UIKit 26 | #else 27 | import AppKit 28 | #endif 29 | 30 | 31 | public class ConstraintMakerRelatable { 32 | 33 | internal let description: ConstraintDescription 34 | 35 | internal init(_ description: ConstraintDescription) { 36 | self.description = description 37 | } 38 | 39 | internal func relatedTo(_ other: ConstraintRelatableTarget, relation: ConstraintRelation, file: String, line: UInt) -> ConstraintMakerEditable { 40 | let related: ConstraintItem 41 | let constant: ConstraintConstantTarget 42 | 43 | if let other = other as? ConstraintItem { 44 | guard other.attributes == ConstraintAttributes.none || 45 | other.attributes.layoutAttributes.count <= 1 || 46 | other.attributes.layoutAttributes == self.description.attributes.layoutAttributes || 47 | other.attributes == .edges && self.description.attributes == .margins || 48 | other.attributes == .margins && self.description.attributes == .edges || 49 | other.attributes == .directionalEdges && self.description.attributes == .directionalMargins || 50 | other.attributes == .directionalMargins && self.description.attributes == .directionalEdges else { 51 | fatalError("Cannot constraint to multiple non identical attributes. (\(file), \(line))"); 52 | } 53 | 54 | related = other 55 | constant = 0.0 56 | } else if let other = other as? ConstraintView { 57 | related = ConstraintItem(target: other, attributes: ConstraintAttributes.none) 58 | constant = 0.0 59 | } else if let other = other as? ConstraintConstantTarget { 60 | related = ConstraintItem(target: nil, attributes: ConstraintAttributes.none) 61 | constant = other 62 | } else if #available(iOS 9.0, OSX 10.11, *), let other = other as? ConstraintLayoutGuide { 63 | related = ConstraintItem(target: other, attributes: ConstraintAttributes.none) 64 | constant = 0.0 65 | } else { 66 | fatalError("Invalid constraint. (\(file), \(line))") 67 | } 68 | 69 | let editable = ConstraintMakerEditable(self.description) 70 | editable.description.sourceLocation = (file, line) 71 | editable.description.relation = relation 72 | editable.description.related = related 73 | editable.description.constant = constant 74 | return editable 75 | } 76 | 77 | @discardableResult 78 | public func equalTo(_ other: ConstraintRelatableTarget, _ file: String = #file, _ line: UInt = #line) -> ConstraintMakerEditable { 79 | return self.relatedTo(other, relation: .equal, file: file, line: line) 80 | } 81 | 82 | @discardableResult 83 | public func equalToSuperview(_ file: String = #file, _ line: UInt = #line) -> ConstraintMakerEditable { 84 | guard let other = self.description.item.superview else { 85 | fatalError("Expected superview but found nil when attempting make constraint `equalToSuperview`.") 86 | } 87 | return self.relatedTo(other, relation: .equal, file: file, line: line) 88 | } 89 | 90 | @discardableResult 91 | public func lessThanOrEqualTo(_ other: ConstraintRelatableTarget, _ file: String = #file, _ line: UInt = #line) -> ConstraintMakerEditable { 92 | return self.relatedTo(other, relation: .lessThanOrEqual, file: file, line: line) 93 | } 94 | 95 | @discardableResult 96 | public func lessThanOrEqualToSuperview(_ file: String = #file, _ line: UInt = #line) -> ConstraintMakerEditable { 97 | guard let other = self.description.item.superview else { 98 | fatalError("Expected superview but found nil when attempting make constraint `lessThanOrEqualToSuperview`.") 99 | } 100 | return self.relatedTo(other, relation: .lessThanOrEqual, file: file, line: line) 101 | } 102 | 103 | @discardableResult 104 | public func greaterThanOrEqualTo(_ other: ConstraintRelatableTarget, _ file: String = #file, line: UInt = #line) -> ConstraintMakerEditable { 105 | return self.relatedTo(other, relation: .greaterThanOrEqual, file: file, line: line) 106 | } 107 | 108 | @discardableResult 109 | public func greaterThanOrEqualToSuperview(_ file: String = #file, line: UInt = #line) -> ConstraintMakerEditable { 110 | guard let other = self.description.item.superview else { 111 | fatalError("Expected superview but found nil when attempting make constraint `greaterThanOrEqualToSuperview`.") 112 | } 113 | return self.relatedTo(other, relation: .greaterThanOrEqual, file: file, line: line) 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /Demo/Pods/SnapKit/Source/ConstraintMultiplierTarget.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SnapKit 3 | // 4 | // Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit 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 14 | // all 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 22 | // THE SOFTWARE. 23 | 24 | #if os(iOS) || os(tvOS) 25 | import UIKit 26 | #else 27 | import AppKit 28 | #endif 29 | 30 | 31 | public protocol ConstraintMultiplierTarget { 32 | 33 | var constraintMultiplierTargetValue: CGFloat { get } 34 | 35 | } 36 | 37 | extension Int: ConstraintMultiplierTarget { 38 | 39 | public var constraintMultiplierTargetValue: CGFloat { 40 | return CGFloat(self) 41 | } 42 | 43 | } 44 | 45 | extension UInt: ConstraintMultiplierTarget { 46 | 47 | public var constraintMultiplierTargetValue: CGFloat { 48 | return CGFloat(self) 49 | } 50 | 51 | } 52 | 53 | extension Float: ConstraintMultiplierTarget { 54 | 55 | public var constraintMultiplierTargetValue: CGFloat { 56 | return CGFloat(self) 57 | } 58 | 59 | } 60 | 61 | extension Double: ConstraintMultiplierTarget { 62 | 63 | public var constraintMultiplierTargetValue: CGFloat { 64 | return CGFloat(self) 65 | } 66 | 67 | } 68 | 69 | extension CGFloat: ConstraintMultiplierTarget { 70 | 71 | public var constraintMultiplierTargetValue: CGFloat { 72 | return self 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /Demo/Pods/SnapKit/Source/ConstraintOffsetTarget.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SnapKit 3 | // 4 | // Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit 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 14 | // all 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 22 | // THE SOFTWARE. 23 | 24 | #if os(iOS) || os(tvOS) 25 | import UIKit 26 | #else 27 | import AppKit 28 | #endif 29 | 30 | 31 | public protocol ConstraintOffsetTarget: ConstraintConstantTarget { 32 | } 33 | 34 | extension Int: ConstraintOffsetTarget { 35 | } 36 | 37 | extension UInt: ConstraintOffsetTarget { 38 | } 39 | 40 | extension Float: ConstraintOffsetTarget { 41 | } 42 | 43 | extension Double: ConstraintOffsetTarget { 44 | } 45 | 46 | extension CGFloat: ConstraintOffsetTarget { 47 | } 48 | 49 | extension ConstraintOffsetTarget { 50 | 51 | internal var constraintOffsetTargetValue: CGFloat { 52 | let offset: CGFloat 53 | if let amount = self as? Float { 54 | offset = CGFloat(amount) 55 | } else if let amount = self as? Double { 56 | offset = CGFloat(amount) 57 | } else if let amount = self as? CGFloat { 58 | offset = CGFloat(amount) 59 | } else if let amount = self as? Int { 60 | offset = CGFloat(amount) 61 | } else if let amount = self as? UInt { 62 | offset = CGFloat(amount) 63 | } else { 64 | offset = 0.0 65 | } 66 | return offset 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /Demo/Pods/SnapKit/Source/ConstraintPriority.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SnapKit 3 | // 4 | // Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit 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 14 | // all 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 22 | // THE SOFTWARE. 23 | 24 | #if os(iOS) || os(tvOS) 25 | import UIKit 26 | #else 27 | import AppKit 28 | #endif 29 | 30 | public struct ConstraintPriority : ExpressibleByFloatLiteral, Equatable, Strideable { 31 | public typealias FloatLiteralType = Float 32 | 33 | public let value: Float 34 | 35 | public init(floatLiteral value: Float) { 36 | self.value = value 37 | } 38 | 39 | public init(_ value: Float) { 40 | self.value = value 41 | } 42 | 43 | public static var required: ConstraintPriority { 44 | return 1000.0 45 | } 46 | 47 | public static var high: ConstraintPriority { 48 | return 750.0 49 | } 50 | 51 | public static var medium: ConstraintPriority { 52 | #if os(OSX) 53 | return 501.0 54 | #else 55 | return 500.0 56 | #endif 57 | 58 | } 59 | 60 | public static var low: ConstraintPriority { 61 | return 250.0 62 | } 63 | 64 | public static func ==(lhs: ConstraintPriority, rhs: ConstraintPriority) -> Bool { 65 | return lhs.value == rhs.value 66 | } 67 | 68 | // MARK: Strideable 69 | 70 | public func advanced(by n: FloatLiteralType) -> ConstraintPriority { 71 | return ConstraintPriority(floatLiteral: value + n) 72 | } 73 | 74 | public func distance(to other: ConstraintPriority) -> FloatLiteralType { 75 | return other.value - value 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /Demo/Pods/SnapKit/Source/ConstraintPriorityTarget.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SnapKit 3 | // 4 | // Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit 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 14 | // all 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 22 | // THE SOFTWARE. 23 | 24 | #if os(iOS) || os(tvOS) 25 | import UIKit 26 | #else 27 | import AppKit 28 | #endif 29 | 30 | 31 | public protocol ConstraintPriorityTarget { 32 | 33 | var constraintPriorityTargetValue: Float { get } 34 | 35 | } 36 | 37 | extension Int: ConstraintPriorityTarget { 38 | 39 | public var constraintPriorityTargetValue: Float { 40 | return Float(self) 41 | } 42 | 43 | } 44 | 45 | extension UInt: ConstraintPriorityTarget { 46 | 47 | public var constraintPriorityTargetValue: Float { 48 | return Float(self) 49 | } 50 | 51 | } 52 | 53 | extension Float: ConstraintPriorityTarget { 54 | 55 | public var constraintPriorityTargetValue: Float { 56 | return self 57 | } 58 | 59 | } 60 | 61 | extension Double: ConstraintPriorityTarget { 62 | 63 | public var constraintPriorityTargetValue: Float { 64 | return Float(self) 65 | } 66 | 67 | } 68 | 69 | extension CGFloat: ConstraintPriorityTarget { 70 | 71 | public var constraintPriorityTargetValue: Float { 72 | return Float(self) 73 | } 74 | 75 | } 76 | 77 | #if os(iOS) || os(tvOS) 78 | extension UILayoutPriority: ConstraintPriorityTarget { 79 | 80 | public var constraintPriorityTargetValue: Float { 81 | return self.rawValue 82 | } 83 | 84 | } 85 | #endif 86 | -------------------------------------------------------------------------------- /Demo/Pods/SnapKit/Source/ConstraintRelatableTarget.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SnapKit 3 | // 4 | // Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit 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 14 | // all 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 22 | // THE SOFTWARE. 23 | 24 | #if os(iOS) || os(tvOS) 25 | import UIKit 26 | #else 27 | import AppKit 28 | #endif 29 | 30 | 31 | public protocol ConstraintRelatableTarget { 32 | } 33 | 34 | extension Int: ConstraintRelatableTarget { 35 | } 36 | 37 | extension UInt: ConstraintRelatableTarget { 38 | } 39 | 40 | extension Float: ConstraintRelatableTarget { 41 | } 42 | 43 | extension Double: ConstraintRelatableTarget { 44 | } 45 | 46 | extension CGFloat: ConstraintRelatableTarget { 47 | } 48 | 49 | extension CGSize: ConstraintRelatableTarget { 50 | } 51 | 52 | extension CGPoint: ConstraintRelatableTarget { 53 | } 54 | 55 | extension ConstraintInsets: ConstraintRelatableTarget { 56 | } 57 | 58 | #if os(iOS) || os(tvOS) 59 | @available(iOS 11.0, tvOS 11.0, *) 60 | extension ConstraintDirectionalInsets: ConstraintRelatableTarget { 61 | } 62 | #endif 63 | 64 | extension ConstraintItem: ConstraintRelatableTarget { 65 | } 66 | 67 | extension ConstraintView: ConstraintRelatableTarget { 68 | } 69 | 70 | @available(iOS 9.0, OSX 10.11, *) 71 | extension ConstraintLayoutGuide: ConstraintRelatableTarget { 72 | } 73 | -------------------------------------------------------------------------------- /Demo/Pods/SnapKit/Source/ConstraintRelation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SnapKit 3 | // 4 | // Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit 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 14 | // all 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 22 | // THE SOFTWARE. 23 | 24 | #if os(iOS) || os(tvOS) 25 | import UIKit 26 | #else 27 | import AppKit 28 | #endif 29 | 30 | 31 | internal enum ConstraintRelation : Int { 32 | case equal = 1 33 | case lessThanOrEqual 34 | case greaterThanOrEqual 35 | 36 | internal var layoutRelation: LayoutRelation { 37 | get { 38 | switch(self) { 39 | case .equal: 40 | return .equal 41 | case .lessThanOrEqual: 42 | return .lessThanOrEqual 43 | case .greaterThanOrEqual: 44 | return .greaterThanOrEqual 45 | } 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Demo/Pods/SnapKit/Source/ConstraintView+Extensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SnapKit 3 | // 4 | // Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit 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 14 | // all 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 22 | // THE SOFTWARE. 23 | 24 | #if os(iOS) || os(tvOS) 25 | import UIKit 26 | #else 27 | import AppKit 28 | #endif 29 | 30 | 31 | public extension ConstraintView { 32 | 33 | @available(*, deprecated, message:"Use newer snp.* syntax.") 34 | var snp_left: ConstraintItem { return self.snp.left } 35 | 36 | @available(*, deprecated, message:"Use newer snp.* syntax.") 37 | var snp_top: ConstraintItem { return self.snp.top } 38 | 39 | @available(*, deprecated, message:"Use newer snp.* syntax.") 40 | var snp_right: ConstraintItem { return self.snp.right } 41 | 42 | @available(*, deprecated, message:"Use newer snp.* syntax.") 43 | var snp_bottom: ConstraintItem { return self.snp.bottom } 44 | 45 | @available(*, deprecated, message:"Use newer snp.* syntax.") 46 | var snp_leading: ConstraintItem { return self.snp.leading } 47 | 48 | @available(*, deprecated, message:"Use newer snp.* syntax.") 49 | var snp_trailing: ConstraintItem { return self.snp.trailing } 50 | 51 | @available(*, deprecated, message:"Use newer snp.* syntax.") 52 | var snp_width: ConstraintItem { return self.snp.width } 53 | 54 | @available(*, deprecated, message:"Use newer snp.* syntax.") 55 | var snp_height: ConstraintItem { return self.snp.height } 56 | 57 | @available(*, deprecated, message:"Use newer snp.* syntax.") 58 | var snp_centerX: ConstraintItem { return self.snp.centerX } 59 | 60 | @available(*, deprecated, message:"Use newer snp.* syntax.") 61 | var snp_centerY: ConstraintItem { return self.snp.centerY } 62 | 63 | @available(*, deprecated, message:"Use newer snp.* syntax.") 64 | var snp_baseline: ConstraintItem { return self.snp.baseline } 65 | 66 | @available(*, deprecated, message:"Use newer snp.* syntax.") 67 | @available(iOS 8.0, OSX 10.11, *) 68 | var snp_lastBaseline: ConstraintItem { return self.snp.lastBaseline } 69 | 70 | @available(iOS, deprecated, message:"Use newer snp.* syntax.") 71 | @available(iOS 8.0, OSX 10.11, *) 72 | var snp_firstBaseline: ConstraintItem { return self.snp.firstBaseline } 73 | 74 | @available(iOS, deprecated, message:"Use newer snp.* syntax.") 75 | @available(iOS 8.0, *) 76 | var snp_leftMargin: ConstraintItem { return self.snp.leftMargin } 77 | 78 | @available(iOS, deprecated, message:"Use newer snp.* syntax.") 79 | @available(iOS 8.0, *) 80 | var snp_topMargin: ConstraintItem { return self.snp.topMargin } 81 | 82 | @available(iOS, deprecated, message:"Use newer snp.* syntax.") 83 | @available(iOS 8.0, *) 84 | var snp_rightMargin: ConstraintItem { return self.snp.rightMargin } 85 | 86 | @available(iOS, deprecated, message:"Use newer snp.* syntax.") 87 | @available(iOS 8.0, *) 88 | var snp_bottomMargin: ConstraintItem { return self.snp.bottomMargin } 89 | 90 | @available(iOS, deprecated, message:"Use newer snp.* syntax.") 91 | @available(iOS 8.0, *) 92 | var snp_leadingMargin: ConstraintItem { return self.snp.leadingMargin } 93 | 94 | @available(iOS, deprecated, message:"Use newer snp.* syntax.") 95 | @available(iOS 8.0, *) 96 | var snp_trailingMargin: ConstraintItem { return self.snp.trailingMargin } 97 | 98 | @available(iOS, deprecated, message:"Use newer snp.* syntax.") 99 | @available(iOS 8.0, *) 100 | var snp_centerXWithinMargins: ConstraintItem { return self.snp.centerXWithinMargins } 101 | 102 | @available(iOS, deprecated, message:"Use newer snp.* syntax.") 103 | @available(iOS 8.0, *) 104 | var snp_centerYWithinMargins: ConstraintItem { return self.snp.centerYWithinMargins } 105 | 106 | @available(*, deprecated, message:"Use newer snp.* syntax.") 107 | var snp_edges: ConstraintItem { return self.snp.edges } 108 | 109 | @available(*, deprecated, message:"Use newer snp.* syntax.") 110 | var snp_size: ConstraintItem { return self.snp.size } 111 | 112 | @available(*, deprecated, message:"Use newer snp.* syntax.") 113 | var snp_center: ConstraintItem { return self.snp.center } 114 | 115 | @available(iOS, deprecated, message:"Use newer snp.* syntax.") 116 | @available(iOS 8.0, *) 117 | var snp_margins: ConstraintItem { return self.snp.margins } 118 | 119 | @available(iOS, deprecated, message:"Use newer snp.* syntax.") 120 | @available(iOS 8.0, *) 121 | var snp_centerWithinMargins: ConstraintItem { return self.snp.centerWithinMargins } 122 | 123 | @available(*, deprecated, message:"Use newer snp.* syntax.") 124 | func snp_prepareConstraints(_ closure: (_ make: ConstraintMaker) -> Void) -> [Constraint] { 125 | return self.snp.prepareConstraints(closure) 126 | } 127 | 128 | @available(*, deprecated, message:"Use newer snp.* syntax.") 129 | func snp_makeConstraints(_ closure: (_ make: ConstraintMaker) -> Void) { 130 | self.snp.makeConstraints(closure) 131 | } 132 | 133 | @available(*, deprecated, message:"Use newer snp.* syntax.") 134 | func snp_remakeConstraints(_ closure: (_ make: ConstraintMaker) -> Void) { 135 | self.snp.remakeConstraints(closure) 136 | } 137 | 138 | @available(*, deprecated, message:"Use newer snp.* syntax.") 139 | func snp_updateConstraints(_ closure: (_ make: ConstraintMaker) -> Void) { 140 | self.snp.updateConstraints(closure) 141 | } 142 | 143 | @available(*, deprecated, message:"Use newer snp.* syntax.") 144 | func snp_removeConstraints() { 145 | self.snp.removeConstraints() 146 | } 147 | 148 | var snp: ConstraintViewDSL { 149 | return ConstraintViewDSL(view: self) 150 | } 151 | 152 | } 153 | -------------------------------------------------------------------------------- /Demo/Pods/SnapKit/Source/ConstraintView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SnapKit 3 | // 4 | // Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit 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 14 | // all 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 22 | // THE SOFTWARE. 23 | 24 | #if os(iOS) || os(tvOS) 25 | import UIKit 26 | #else 27 | import AppKit 28 | #endif 29 | 30 | 31 | #if os(iOS) || os(tvOS) 32 | public typealias ConstraintView = UIView 33 | #else 34 | public typealias ConstraintView = NSView 35 | #endif 36 | -------------------------------------------------------------------------------- /Demo/Pods/SnapKit/Source/ConstraintViewDSL.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SnapKit 3 | // 4 | // Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit 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 14 | // all 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 22 | // THE SOFTWARE. 23 | 24 | #if os(iOS) || os(tvOS) 25 | import UIKit 26 | #else 27 | import AppKit 28 | #endif 29 | 30 | 31 | public struct ConstraintViewDSL: ConstraintAttributesDSL { 32 | 33 | @discardableResult 34 | public func prepareConstraints(_ closure: (_ make: ConstraintMaker) -> Void) -> [Constraint] { 35 | return ConstraintMaker.prepareConstraints(item: self.view, closure: closure) 36 | } 37 | 38 | public func makeConstraints(_ closure: (_ make: ConstraintMaker) -> Void) { 39 | ConstraintMaker.makeConstraints(item: self.view, closure: closure) 40 | } 41 | 42 | public func remakeConstraints(_ closure: (_ make: ConstraintMaker) -> Void) { 43 | ConstraintMaker.remakeConstraints(item: self.view, closure: closure) 44 | } 45 | 46 | public func updateConstraints(_ closure: (_ make: ConstraintMaker) -> Void) { 47 | ConstraintMaker.updateConstraints(item: self.view, closure: closure) 48 | } 49 | 50 | public func removeConstraints() { 51 | ConstraintMaker.removeConstraints(item: self.view) 52 | } 53 | 54 | public var contentHuggingHorizontalPriority: Float { 55 | get { 56 | return self.view.contentHuggingPriority(for: .horizontal).rawValue 57 | } 58 | nonmutating set { 59 | self.view.setContentHuggingPriority(LayoutPriority(rawValue: newValue), for: .horizontal) 60 | } 61 | } 62 | 63 | public var contentHuggingVerticalPriority: Float { 64 | get { 65 | return self.view.contentHuggingPriority(for: .vertical).rawValue 66 | } 67 | nonmutating set { 68 | self.view.setContentHuggingPriority(LayoutPriority(rawValue: newValue), for: .vertical) 69 | } 70 | } 71 | 72 | public var contentCompressionResistanceHorizontalPriority: Float { 73 | get { 74 | return self.view.contentCompressionResistancePriority(for: .horizontal).rawValue 75 | } 76 | nonmutating set { 77 | self.view.setContentCompressionResistancePriority(LayoutPriority(rawValue: newValue), for: .horizontal) 78 | } 79 | } 80 | 81 | public var contentCompressionResistanceVerticalPriority: Float { 82 | get { 83 | return self.view.contentCompressionResistancePriority(for: .vertical).rawValue 84 | } 85 | nonmutating set { 86 | self.view.setContentCompressionResistancePriority(LayoutPriority(rawValue: newValue), for: .vertical) 87 | } 88 | } 89 | 90 | public var target: AnyObject? { 91 | return self.view 92 | } 93 | 94 | internal let view: ConstraintView 95 | 96 | internal init(view: ConstraintView) { 97 | self.view = view 98 | 99 | } 100 | 101 | } 102 | -------------------------------------------------------------------------------- /Demo/Pods/SnapKit/Source/Debugging.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SnapKit 3 | // 4 | // Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit 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 14 | // all 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 22 | // THE SOFTWARE. 23 | 24 | #if os(iOS) || os(tvOS) 25 | import UIKit 26 | #else 27 | import AppKit 28 | #endif 29 | 30 | public extension LayoutConstraint { 31 | 32 | override var description: String { 33 | var description = "<" 34 | 35 | description += descriptionForObject(self) 36 | 37 | if let firstItem = conditionalOptional(from: self.firstItem) { 38 | description += " \(descriptionForObject(firstItem))" 39 | } 40 | 41 | if self.firstAttribute != .notAnAttribute { 42 | description += ".\(descriptionForAttribute(self.firstAttribute))" 43 | } 44 | 45 | description += " \(descriptionForRelation(self.relation))" 46 | 47 | if let secondItem = self.secondItem { 48 | description += " \(descriptionForObject(secondItem))" 49 | } 50 | 51 | if self.secondAttribute != .notAnAttribute { 52 | description += ".\(descriptionForAttribute(self.secondAttribute))" 53 | } 54 | 55 | if self.multiplier != 1.0 { 56 | description += " * \(self.multiplier)" 57 | } 58 | 59 | if self.secondAttribute == .notAnAttribute { 60 | description += " \(self.constant)" 61 | } else { 62 | if self.constant > 0.0 { 63 | description += " + \(self.constant)" 64 | } else if self.constant < 0.0 { 65 | description += " - \(abs(self.constant))" 66 | } 67 | } 68 | 69 | if self.priority.rawValue != 1000.0 { 70 | description += " ^\(self.priority)" 71 | } 72 | 73 | description += ">" 74 | 75 | return description 76 | } 77 | 78 | } 79 | 80 | private func descriptionForRelation(_ relation: LayoutRelation) -> String { 81 | switch relation { 82 | case .equal: return "==" 83 | case .greaterThanOrEqual: return ">=" 84 | case .lessThanOrEqual: return "<=" 85 | #if swift(>=5.0) 86 | @unknown default: return "unknown" 87 | #endif 88 | } 89 | } 90 | 91 | private func descriptionForAttribute(_ attribute: LayoutAttribute) -> String { 92 | #if os(iOS) || os(tvOS) 93 | switch attribute { 94 | case .notAnAttribute: return "notAnAttribute" 95 | case .top: return "top" 96 | case .left: return "left" 97 | case .bottom: return "bottom" 98 | case .right: return "right" 99 | case .leading: return "leading" 100 | case .trailing: return "trailing" 101 | case .width: return "width" 102 | case .height: return "height" 103 | case .centerX: return "centerX" 104 | case .centerY: return "centerY" 105 | case .lastBaseline: return "lastBaseline" 106 | case .firstBaseline: return "firstBaseline" 107 | case .topMargin: return "topMargin" 108 | case .leftMargin: return "leftMargin" 109 | case .bottomMargin: return "bottomMargin" 110 | case .rightMargin: return "rightMargin" 111 | case .leadingMargin: return "leadingMargin" 112 | case .trailingMargin: return "trailingMargin" 113 | case .centerXWithinMargins: return "centerXWithinMargins" 114 | case .centerYWithinMargins: return "centerYWithinMargins" 115 | #if swift(>=5.0) 116 | @unknown default: return "unknown" 117 | #endif 118 | } 119 | #else 120 | switch attribute { 121 | case .notAnAttribute: return "notAnAttribute" 122 | case .top: return "top" 123 | case .left: return "left" 124 | case .bottom: return "bottom" 125 | case .right: return "right" 126 | case .leading: return "leading" 127 | case .trailing: return "trailing" 128 | case .width: return "width" 129 | case .height: return "height" 130 | case .centerX: return "centerX" 131 | case .centerY: return "centerY" 132 | case .lastBaseline: return "lastBaseline" 133 | case .firstBaseline: return "firstBaseline" 134 | #if swift(>=5.0) 135 | @unknown default: return "unknown" 136 | #endif 137 | } 138 | #endif 139 | } 140 | 141 | private func conditionalOptional(from object: Optional) -> Optional { 142 | return object 143 | } 144 | 145 | private func conditionalOptional(from object: T) -> Optional { 146 | return Optional.some(object) 147 | } 148 | 149 | private func descriptionForObject(_ object: AnyObject) -> String { 150 | let pointerDescription = String(format: "%p", UInt(bitPattern: ObjectIdentifier(object))) 151 | var desc = "" 152 | 153 | desc += type(of: object).description() 154 | 155 | if let object = object as? ConstraintView { 156 | desc += ":\(object.snp.label() ?? pointerDescription)" 157 | } else if let object = object as? LayoutConstraint { 158 | desc += ":\(object.label ?? pointerDescription)" 159 | } else { 160 | desc += ":\(pointerDescription)" 161 | } 162 | 163 | if let object = object as? LayoutConstraint, let file = object.constraint?.sourceLocation.0, let line = object.constraint?.sourceLocation.1 { 164 | desc += "@\((file as NSString).lastPathComponent)#\(line)" 165 | } 166 | 167 | desc += "" 168 | return desc 169 | } 170 | -------------------------------------------------------------------------------- /Demo/Pods/SnapKit/Source/LayoutConstraint.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SnapKit 3 | // 4 | // Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit 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 14 | // all 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 22 | // THE SOFTWARE. 23 | 24 | #if os(iOS) || os(tvOS) 25 | import UIKit 26 | #else 27 | import AppKit 28 | #endif 29 | 30 | 31 | public class LayoutConstraint : NSLayoutConstraint { 32 | 33 | public var label: String? { 34 | get { 35 | return self.identifier 36 | } 37 | set { 38 | self.identifier = newValue 39 | } 40 | } 41 | 42 | internal weak var constraint: Constraint? = nil 43 | 44 | } 45 | 46 | internal func ==(lhs: LayoutConstraint, rhs: LayoutConstraint) -> Bool { 47 | // If firstItem or secondItem on either constraint has a dangling pointer 48 | // this comparison can cause a crash. The solution for this is to ensure 49 | // your layout code hold strong references to things like Views, LayoutGuides 50 | // and LayoutAnchors as SnapKit will not keep strong references to any of these. 51 | guard lhs.firstAttribute == rhs.firstAttribute && 52 | lhs.secondAttribute == rhs.secondAttribute && 53 | lhs.relation == rhs.relation && 54 | lhs.priority == rhs.priority && 55 | lhs.multiplier == rhs.multiplier && 56 | lhs.secondItem === rhs.secondItem && 57 | lhs.firstItem === rhs.firstItem else { 58 | return false 59 | } 60 | return true 61 | } 62 | -------------------------------------------------------------------------------- /Demo/Pods/SnapKit/Source/LayoutConstraintItem.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SnapKit 3 | // 4 | // Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit 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 14 | // all 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 22 | // THE SOFTWARE. 23 | 24 | #if os(iOS) || os(tvOS) 25 | import UIKit 26 | #else 27 | import AppKit 28 | #endif 29 | 30 | 31 | public protocol LayoutConstraintItem: class { 32 | } 33 | 34 | @available(iOS 9.0, OSX 10.11, *) 35 | extension ConstraintLayoutGuide : LayoutConstraintItem { 36 | } 37 | 38 | extension ConstraintView : LayoutConstraintItem { 39 | } 40 | 41 | 42 | extension LayoutConstraintItem { 43 | 44 | internal func prepare() { 45 | if let view = self as? ConstraintView { 46 | view.translatesAutoresizingMaskIntoConstraints = false 47 | } 48 | } 49 | 50 | internal var superview: ConstraintView? { 51 | if let view = self as? ConstraintView { 52 | return view.superview 53 | } 54 | 55 | if #available(iOS 9.0, OSX 10.11, *), let guide = self as? ConstraintLayoutGuide { 56 | return guide.owningView 57 | } 58 | 59 | return nil 60 | } 61 | internal var constraints: [Constraint] { 62 | return self.constraintsSet.allObjects as! [Constraint] 63 | } 64 | 65 | internal func add(constraints: [Constraint]) { 66 | let constraintsSet = self.constraintsSet 67 | for constraint in constraints { 68 | constraintsSet.add(constraint) 69 | } 70 | } 71 | 72 | internal func remove(constraints: [Constraint]) { 73 | let constraintsSet = self.constraintsSet 74 | for constraint in constraints { 75 | constraintsSet.remove(constraint) 76 | } 77 | } 78 | 79 | private var constraintsSet: NSMutableSet { 80 | let constraintsSet: NSMutableSet 81 | 82 | if let existing = objc_getAssociatedObject(self, &constraintsKey) as? NSMutableSet { 83 | constraintsSet = existing 84 | } else { 85 | constraintsSet = NSMutableSet() 86 | objc_setAssociatedObject(self, &constraintsKey, constraintsSet, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) 87 | } 88 | return constraintsSet 89 | 90 | } 91 | 92 | } 93 | private var constraintsKey: UInt8 = 0 94 | -------------------------------------------------------------------------------- /Demo/Pods/SnapKit/Source/Typealiases.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SnapKit 3 | // 4 | // Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit 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 14 | // all 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 22 | // THE SOFTWARE. 23 | 24 | import Foundation 25 | 26 | #if os(iOS) || os(tvOS) 27 | import UIKit 28 | #if swift(>=4.2) 29 | typealias LayoutRelation = NSLayoutConstraint.Relation 30 | typealias LayoutAttribute = NSLayoutConstraint.Attribute 31 | #else 32 | typealias LayoutRelation = NSLayoutRelation 33 | typealias LayoutAttribute = NSLayoutAttribute 34 | #endif 35 | typealias LayoutPriority = UILayoutPriority 36 | #else 37 | import AppKit 38 | typealias LayoutRelation = NSLayoutConstraint.Relation 39 | typealias LayoutAttribute = NSLayoutConstraint.Attribute 40 | typealias LayoutPriority = NSLayoutConstraint.Priority 41 | #endif 42 | 43 | -------------------------------------------------------------------------------- /Demo/Pods/SnapKit/Source/UILayoutSupport+Extensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SnapKit 3 | // 4 | // Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit 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 14 | // all 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 22 | // THE SOFTWARE. 23 | 24 | #if os(iOS) || os(tvOS) 25 | import UIKit 26 | #endif 27 | 28 | 29 | @available(iOS 8.0, *) 30 | public extension ConstraintLayoutSupport { 31 | 32 | var snp: ConstraintLayoutSupportDSL { 33 | return ConstraintLayoutSupportDSL(support: self) 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/Apis/Apis-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | ${PODS_DEVELOPMENT_LANGUAGE} 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.2.2 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/Apis/Apis-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Apis : NSObject 3 | @end 4 | @implementation PodsDummy_Apis 5 | @end 6 | -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/Apis/Apis-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/Apis/Apis-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double ApisVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char ApisVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/Apis/Apis.debug.xcconfig: -------------------------------------------------------------------------------- 1 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO 2 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/Apis 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift 5 | OTHER_LDFLAGS = $(inherited) -framework "Foundation" -framework "UIKit" 6 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS -suppress-warnings 7 | PODS_BUILD_DIR = ${BUILD_DIR} 8 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 9 | PODS_DEVELOPMENT_LANGUAGE = ${DEVELOPMENT_LANGUAGE} 10 | PODS_ROOT = ${SRCROOT} 11 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/../.. 12 | PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates 13 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 14 | SKIP_INSTALL = YES 15 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 16 | -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/Apis/Apis.modulemap: -------------------------------------------------------------------------------- 1 | framework module Apis { 2 | umbrella header "Apis-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/Apis/Apis.release.xcconfig: -------------------------------------------------------------------------------- 1 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO 2 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/Apis 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift 5 | OTHER_LDFLAGS = $(inherited) -framework "Foundation" -framework "UIKit" 6 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS -suppress-warnings 7 | PODS_BUILD_DIR = ${BUILD_DIR} 8 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 9 | PODS_DEVELOPMENT_LANGUAGE = ${DEVELOPMENT_LANGUAGE} 10 | PODS_ROOT = ${SRCROOT} 11 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/../.. 12 | PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates 13 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 14 | SKIP_INSTALL = YES 15 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 16 | -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/Apis/ResourceBundle-Apis-Apis-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | ${PODS_DEVELOPMENT_LANGUAGE} 7 | CFBundleIdentifier 8 | ${PRODUCT_BUNDLE_IDENTIFIER} 9 | CFBundleInfoDictionaryVersion 10 | 6.0 11 | CFBundleName 12 | ${PRODUCT_NAME} 13 | CFBundlePackageType 14 | BNDL 15 | CFBundleShortVersionString 16 | 1.2.2 17 | CFBundleSignature 18 | ???? 19 | CFBundleVersion 20 | 1 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/Apis/ResourceBundle-Privacy-Apis-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | ${PODS_DEVELOPMENT_LANGUAGE} 7 | CFBundleIdentifier 8 | ${PRODUCT_BUNDLE_IDENTIFIER} 9 | CFBundleInfoDictionaryVersion 10 | 6.0 11 | CFBundleName 12 | ${PRODUCT_NAME} 13 | CFBundlePackageType 14 | BNDL 15 | CFBundleShortVersionString 16 | 1.2.0 17 | CFBundleSignature 18 | ???? 19 | CFBundleVersion 20 | 1 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/Pods-Demo/Pods-Demo-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | ${PODS_DEVELOPMENT_LANGUAGE} 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/Pods-Demo/Pods-Demo-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | 4 | ## Apis 5 | 6 | MIT License 7 | 8 | Copyright (c) 2021 LEE 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in all 18 | copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | 28 | 29 | ## SnapKit 30 | 31 | Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit 32 | 33 | Permission is hereby granted, free of charge, to any person obtaining a copy 34 | of this software and associated documentation files (the "Software"), to deal 35 | in the Software without restriction, including without limitation the rights 36 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 37 | copies of the Software, and to permit persons to whom the Software is 38 | furnished to do so, subject to the following conditions: 39 | 40 | The above copyright notice and this permission notice shall be included in 41 | all copies or substantial portions of the Software. 42 | 43 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 44 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 45 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 46 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 47 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 48 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 49 | THE SOFTWARE. 50 | 51 | Generated by CocoaPods - https://cocoapods.org 52 | -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/Pods-Demo/Pods-Demo-acknowledgements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreferenceSpecifiers 6 | 7 | 8 | FooterText 9 | This application makes use of the following third party libraries: 10 | Title 11 | Acknowledgements 12 | Type 13 | PSGroupSpecifier 14 | 15 | 16 | FooterText 17 | MIT License 18 | 19 | Copyright (c) 2021 LEE 20 | 21 | Permission is hereby granted, free of charge, to any person obtaining a copy 22 | of this software and associated documentation files (the "Software"), to deal 23 | in the Software without restriction, including without limitation the rights 24 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 25 | copies of the Software, and to permit persons to whom the Software is 26 | furnished to do so, subject to the following conditions: 27 | 28 | The above copyright notice and this permission notice shall be included in all 29 | copies or substantial portions of the Software. 30 | 31 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 32 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 33 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 34 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 35 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 36 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 37 | SOFTWARE. 38 | 39 | License 40 | MIT 41 | Title 42 | Apis 43 | Type 44 | PSGroupSpecifier 45 | 46 | 47 | FooterText 48 | Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit 49 | 50 | Permission is hereby granted, free of charge, to any person obtaining a copy 51 | of this software and associated documentation files (the "Software"), to deal 52 | in the Software without restriction, including without limitation the rights 53 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 54 | copies of the Software, and to permit persons to whom the Software is 55 | furnished to do so, subject to the following conditions: 56 | 57 | The above copyright notice and this permission notice shall be included in 58 | all copies or substantial portions of the Software. 59 | 60 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 61 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 62 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 63 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 64 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 65 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 66 | THE SOFTWARE. 67 | 68 | License 69 | MIT 70 | Title 71 | SnapKit 72 | Type 73 | PSGroupSpecifier 74 | 75 | 76 | FooterText 77 | Generated by CocoaPods - https://cocoapods.org 78 | Title 79 | 80 | Type 81 | PSGroupSpecifier 82 | 83 | 84 | StringsTable 85 | Acknowledgements 86 | Title 87 | Acknowledgements 88 | 89 | 90 | -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/Pods-Demo/Pods-Demo-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_Demo : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_Demo 5 | @end 6 | -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/Pods-Demo/Pods-Demo-frameworks-Debug-input-files.xcfilelist: -------------------------------------------------------------------------------- 1 | ${PODS_ROOT}/Target Support Files/Pods-Demo/Pods-Demo-frameworks.sh 2 | ${BUILT_PRODUCTS_DIR}/Apis/Apis.framework 3 | ${BUILT_PRODUCTS_DIR}/SnapKit/SnapKit.framework -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/Pods-Demo/Pods-Demo-frameworks-Debug-output-files.xcfilelist: -------------------------------------------------------------------------------- 1 | ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Apis.framework 2 | ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SnapKit.framework -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/Pods-Demo/Pods-Demo-frameworks-Release-input-files.xcfilelist: -------------------------------------------------------------------------------- 1 | ${PODS_ROOT}/Target Support Files/Pods-Demo/Pods-Demo-frameworks.sh 2 | ${BUILT_PRODUCTS_DIR}/Apis/Apis.framework 3 | ${BUILT_PRODUCTS_DIR}/SnapKit/SnapKit.framework -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/Pods-Demo/Pods-Demo-frameworks-Release-output-files.xcfilelist: -------------------------------------------------------------------------------- 1 | ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Apis.framework 2 | ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SnapKit.framework -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/Pods-Demo/Pods-Demo-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double Pods_DemoVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char Pods_DemoVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/Pods-Demo/Pods-Demo.debug.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES 2 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO 3 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Apis" "${PODS_CONFIGURATION_BUILD_DIR}/SnapKit" 4 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 5 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Apis/Apis.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SnapKit/SnapKit.framework/Headers" 6 | LD_RUNPATH_SEARCH_PATHS = $(inherited) /usr/lib/swift '@executable_path/Frameworks' '@loader_path/Frameworks' 7 | LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift 8 | OTHER_CFLAGS = $(inherited) -isystem "${PODS_CONFIGURATION_BUILD_DIR}/Apis/Apis.framework/Headers" -isystem "${PODS_CONFIGURATION_BUILD_DIR}/SnapKit/SnapKit.framework/Headers" -iframework "${PODS_CONFIGURATION_BUILD_DIR}/Apis" -iframework "${PODS_CONFIGURATION_BUILD_DIR}/SnapKit" 9 | OTHER_LDFLAGS = $(inherited) -framework "Apis" -framework "Foundation" -framework "SnapKit" -framework "UIKit" 10 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS 11 | PODS_BUILD_DIR = ${BUILD_DIR} 12 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 13 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 14 | PODS_ROOT = ${SRCROOT}/Pods 15 | PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates 16 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 17 | -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/Pods-Demo/Pods-Demo.modulemap: -------------------------------------------------------------------------------- 1 | framework module Pods_Demo { 2 | umbrella header "Pods-Demo-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/Pods-Demo/Pods-Demo.release.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES 2 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO 3 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Apis" "${PODS_CONFIGURATION_BUILD_DIR}/SnapKit" 4 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 5 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Apis/Apis.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SnapKit/SnapKit.framework/Headers" 6 | LD_RUNPATH_SEARCH_PATHS = $(inherited) /usr/lib/swift '@executable_path/Frameworks' '@loader_path/Frameworks' 7 | LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift 8 | OTHER_CFLAGS = $(inherited) -isystem "${PODS_CONFIGURATION_BUILD_DIR}/Apis/Apis.framework/Headers" -isystem "${PODS_CONFIGURATION_BUILD_DIR}/SnapKit/SnapKit.framework/Headers" -iframework "${PODS_CONFIGURATION_BUILD_DIR}/Apis" -iframework "${PODS_CONFIGURATION_BUILD_DIR}/SnapKit" 9 | OTHER_LDFLAGS = $(inherited) -framework "Apis" -framework "Foundation" -framework "SnapKit" -framework "UIKit" 10 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS 11 | PODS_BUILD_DIR = ${BUILD_DIR} 12 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 13 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 14 | PODS_ROOT = ${SRCROOT}/Pods 15 | PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates 16 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 17 | -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/SnapKit/SnapKit-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | ${PODS_DEVELOPMENT_LANGUAGE} 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 5.0.1 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/SnapKit/SnapKit-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_SnapKit : NSObject 3 | @end 4 | @implementation PodsDummy_SnapKit 5 | @end 6 | -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/SnapKit/SnapKit-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/SnapKit/SnapKit-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double SnapKitVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char SnapKitVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/SnapKit/SnapKit.debug.xcconfig: -------------------------------------------------------------------------------- 1 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO 2 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/SnapKit 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift 5 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS -suppress-warnings 6 | PODS_BUILD_DIR = ${BUILD_DIR} 7 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 8 | PODS_DEVELOPMENT_LANGUAGE = ${DEVELOPMENT_LANGUAGE} 9 | PODS_ROOT = ${SRCROOT} 10 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/SnapKit 11 | PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates 12 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 13 | SKIP_INSTALL = YES 14 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 15 | -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/SnapKit/SnapKit.modulemap: -------------------------------------------------------------------------------- 1 | framework module SnapKit { 2 | umbrella header "SnapKit-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/SnapKit/SnapKit.release.xcconfig: -------------------------------------------------------------------------------- 1 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO 2 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/SnapKit 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift 5 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS -suppress-warnings 6 | PODS_BUILD_DIR = ${BUILD_DIR} 7 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 8 | PODS_DEVELOPMENT_LANGUAGE = ${DEVELOPMENT_LANGUAGE} 9 | PODS_ROOT = ${SRCROOT} 10 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/SnapKit 11 | PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates 12 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 13 | SKIP_INSTALL = YES 14 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 LEE 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Apis 2 | 3 | [![License](https://img.shields.io/cocoapods/l/Apis.svg)](LICENSE)  4 | ![Swift](https://img.shields.io/badge/Swift-5.0-orange.svg)  5 | ![Platform](https://img.shields.io/cocoapods/p/Apis.svg?style=flat)  6 | [![Carthage](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)  7 | [![Cocoapods](https://img.shields.io/cocoapods/v/Apis.svg)](https://cocoapods.org) 8 | 9 | 10 | [URLNavigator](https://github.com/devxoul/URLNavigator) abstract routing component written in Swift, Inspired by [Moya](https://github.com/Moya/Moya). 11 | 12 | ## [天朝子民](README_CN.md) 13 | 14 | ## Features 15 | 16 | - [x] Support for different processing based on plugin mechanism. 17 | - [x] Configuration is independent and easy to manage. 18 | - [x] Good business scalability. 19 | - [x] Safer page management. 20 | - [x] Support for asynchronous completion of callbacks. 21 | 22 | 23 | ## Installation 24 | 25 | Apis officially supports CocoaPods only. 26 | 27 | **CocoaPods - Podfile** 28 | 29 | ```ruby 30 | pod 'Apis' 31 | ``` 32 | 33 | ## Usage 34 | 35 | First make sure to import the framework: 36 | 37 | ```swift 38 | import Apis 39 | ``` 40 | 41 | Here are some usage examples. All devices are also available as simulators: 42 | 43 | ### Create Apis 44 | 45 | ```swift 46 | let router = Apis.Provider( 47 | [RouterXXXXXXPlugin(), 48 | RouterXXXXXXPlugin(), 49 | RouterXXXXXXPlugin()] 50 | ) 51 | ``` 52 | 53 | ### TargetType 54 | 55 | ```swift 56 | enum RouterTarget { 57 | case open_http(url: URL) 58 | case open_https(url: URL) 59 | case open_none 60 | case open_live(id: String) 61 | case open_some 62 | } 63 | 64 | extension RouterTarget: Apis.TargetType { 65 | 66 | var task: Task { 67 | switch(self) { 68 | case let .open_http(url): 69 | return .controller(SFSafariViewController(url: url)) 70 | 71 | case let .open_https(url): 72 | return .controller(SFSafariViewController(url: url)) 73 | 74 | case .open_none: 75 | return .controller(NoneViewController()) 76 | 77 | case let .open_live(id): 78 | let controller = LiveViewController() 79 | controller.id = id 80 | return .controller(controller) 81 | 82 | case .open_some: 83 | return .handle { completion in 84 | DispatchQueue.main.asyncAfter(deadline: .now() + 2) { 85 | completion(true) 86 | } 87 | } 88 | } 89 | } 90 | } 91 | 92 | extension XXXXViewController: Routerable { } 93 | extension SFSafariViewController: Routerable { } 94 | ``` 95 | ### URLTargetType 96 | 97 | ```swift 98 | private let schemes = "router" 99 | 100 | extension RouterTarget: URLTargetType { 101 | 102 | static var bindings: [URLPatternBinding] { 103 | return [ 104 | .init("http://") { source in 105 | guard let url = source.url.value else { return .none } 106 | return .open_http(url: url) 107 | }, 108 | .init("https://") { source in 109 | guard let url = source.url.value else { return .none } 110 | return .open_https(url: url) 111 | }, 112 | .init(schemes + "://open/none") { 113 | return .open_none 114 | }, 115 | .init(schemes + "://open/live") { source in 116 | guard let id = source.url.queryParameters["id"] else { return nil } 117 | return .open_live(id: id) 118 | }, 119 | .init(schemes + "://open/some") { 120 | return .open_some 121 | } 122 | ] 123 | } 124 | } 125 | ``` 126 | 127 | ### Custom plugins 128 | 129 | ```swift 130 | class RouterXXXXPlugin: Apis.PluginType { 131 | 132 | func should(open target: TargetType) -> Bool { 133 | /* ... */ 134 | return true 135 | } 136 | 137 | func prepare(open target: TargetType, completion: @escaping (Bool) -> Void) { 138 | /* ... */ 139 | completion(true) 140 | } 141 | 142 | func will(open target: TargetType, controller: Routerable) { 143 | /* ... */ 144 | } 145 | 146 | func did(open target: TargetType, controller: Routerable) { 147 | /* ... */ 148 | } 149 | } 150 | ``` 151 | 152 | ### Open 153 | 154 | ```swift 155 | // Open page based on type 156 | router.open(.open_xxxx) 157 | 158 | // Open page based on url 159 | router.open("http://xxxxxxxx") 160 | 161 | // Result callback 162 | router.open("http://xxxxxxxx") { (result) in 163 | // Success or failure 164 | } 165 | 166 | ``` 167 | 168 | ## Contributing 169 | 170 | If you have the need for a specific feature that you want implemented or if you experienced a bug, please open an issue. 171 | If you extended the functionality of Apis yourself and want others to use it too, please submit a pull request. 172 | 173 | 174 | ## License 175 | 176 | Apis is under MIT license. See the [LICENSE](LICENSE) file for more info. 177 | -------------------------------------------------------------------------------- /README_CN.md: -------------------------------------------------------------------------------- 1 | # Apis 2 | 3 | [![License](https://img.shields.io/cocoapods/l/Apis.svg)](LICENSE)  4 | ![Swift](https://img.shields.io/badge/Swift-5.0-orange.svg)  5 | ![Platform](https://img.shields.io/cocoapods/p/Apis.svg?style=flat)  6 | [![Carthage](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)  7 | [![Cocoapods](https://img.shields.io/cocoapods/v/Apis.svg)](https://cocoapods.org) 8 | 9 | 基于[URLNavigator](https://github.com/devxoul/URLNavigator) 抽象的路由组件, 灵感来自 [Moya](https://github.com/Moya/Moya). 10 | 11 | ## Features 12 | 13 | - [x] 支持基于插件机制的不同处理 如登录拦截等. 14 | - [x] 配置独立且易于管理. 15 | - [x] 良好的业务可扩展性. 16 | - [x] 安全的页面管理. 17 | - [x] 支持异步完成结果回调. 18 | 19 | 20 | ## 安装 21 | 22 | Apis 仅支持CocoaPods. 23 | 24 | **CocoaPods - Podfile** 25 | 26 | ```ruby 27 | pod 'Apis' 28 | ``` 29 | 30 | ## 使用 31 | 32 | 首先导入framework: 33 | 34 | ```swift 35 | import Apis 36 | ``` 37 | 38 | 下面是一些简单示例. 支持所有设备和模拟器: 39 | 40 | ### 创建 Apis 41 | 42 | ```swift 43 | let router = Apis.Provider( 44 | [RouterXXXXXXPlugin(), 45 | RouterXXXXXXPlugin(), 46 | RouterXXXXXXPlugin()] 47 | ) 48 | ``` 49 | 50 | ### TargetType 51 | 52 | ```swift 53 | // 可以通过枚举声明所有类型 54 | enum RouterTarget { 55 | case open_http(url: URL) 56 | case open_https(url: URL) 57 | case open_none 58 | case open_live(id: String) 59 | case open_some 60 | } 61 | 62 | extension RouterTarget: Apis.TargetType { 63 | 64 | var task: Task { 65 | switch(self) { 66 | case let .open_http(url): 67 | return .controller(SFSafariViewController(url: url)) 68 | 69 | case let .open_https(url): 70 | return .controller(SFSafariViewController(url: url)) 71 | 72 | case .open_none: 73 | return .controller(NoneViewController()) 74 | 75 | case let .open_live(id): 76 | let controller = LiveViewController() 77 | controller.id = id 78 | return .controller(controller) 79 | 80 | case .open_some: 81 | return .handle { completion in 82 | // 处理一些其他事情, 结束后务必调用completion回调 83 | DispatchQueue.main.asyncAfter(deadline: .now() + 2) { 84 | // 巴拉巴拉.. 85 | completion(true) 86 | } 87 | } 88 | } 89 | } 90 | } 91 | 92 | // 每个支持路由的视图控制器需要实现 Routerable 协议 93 | extension XXXXViewController: Routerable { } 94 | extension SFSafariViewController: Routerable { } 95 | ``` 96 | 97 | ### URLTargetType 98 | 99 | ```swift 100 | private let schemes = "router" 101 | 102 | extension RouterTarget: URLTargetType { 103 | // URL模板绑定 不同的模板返回不同的Target 104 | static var bindings: [URLPatternBinding] { 105 | return [ 106 | .init("http://") { source in 107 | guard let url = source.url.value else { return .none } 108 | return .open_http(url: url) 109 | }, 110 | .init("https://") { source in 111 | guard let url = source.url.value else { return .none } 112 | return .open_https(url: url) 113 | }, 114 | .init(schemes + "://open/none") { 115 | return .open_none 116 | }, 117 | .init(schemes + "://open/live") { source in 118 | guard let id = source.url.queryParameters["id"] else { return nil } 119 | return .open_live(id: id) 120 | }, 121 | .init(schemes + "://open/some") { 122 | return .open_some 123 | } 124 | ] 125 | } 126 | } 127 | ``` 128 | 129 | ### 自定义插件 130 | 131 | ```swift 132 | // 实现需要的方法 你可以在整个打开过程中做一切你想做的事情 133 | class RouterXXXXPlugin: Apis.PluginType { 134 | 135 | // 能否打开 136 | func should(open target: TargetType) -> Bool { 137 | /* ... */ 138 | return true 139 | } 140 | 141 | // 准备打开时 142 | func prepare(open target: TargetType, completion: @escaping (Bool) -> Void) { 143 | /* ... */ 144 | completion(true) 145 | } 146 | 147 | // 即将打开 148 | func will(open target: TargetType, controller: Routerable) { 149 | /* ... */ 150 | } 151 | 152 | // 已经打开 153 | func did(open target: TargetType, controller: Routerable) { 154 | /* ... */ 155 | } 156 | } 157 | ``` 158 | 159 | ### 打开 160 | 161 | ```swift 162 | // 根据目标类型打开页面 163 | router.open(.open_xxxx) 164 | 165 | // 根据URL打开页面 166 | router.open("http://xxxxxxxx") 167 | 168 | // 打开结果回调 打开过程中可能由于各种原因导致打开失败 例如: 这个页面需要登录 但是当前没有登录之类的 169 | router.open("http://xxxxxxxx") { (result) in 170 | // 成功或失败 171 | } 172 | ``` 173 | 174 | ## 贡献 175 | 176 | 如果你需要实现特定功能或遇到错误,请打开issue。 如果你自己扩展了Apis的功能并希望其他人也使用它,请提交拉取请求。 177 | 178 | 179 | ## 协议 180 | 181 | Apis 使用 MIT 协议. 有关更多信息,请参阅 [LICENSE](LICENSE) 文件. 182 | -------------------------------------------------------------------------------- /Sources/Apis.h: -------------------------------------------------------------------------------- 1 | // 2 | // Apis.h 3 | // Apis 4 | // 5 | // Created by 李响 on 2021/12/15. 6 | // 7 | 8 | #import 9 | 10 | //! Project version number for Apis. 11 | FOUNDATION_EXPORT double ApisVersionNumber; 12 | 13 | //! Project version string for Apis. 14 | FOUNDATION_EXPORT const unsigned char ApisVersionString[]; 15 | 16 | // In this header, you should import all the public headers of your framework using statements like #import 17 | 18 | 19 | -------------------------------------------------------------------------------- /Sources/Context.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Context.swift 3 | // ┌─┐ ┌───────┐ ┌───────┐ 4 | // │ │ │ ┌─────┘ │ ┌─────┘ 5 | // │ │ │ └─────┐ │ └─────┐ 6 | // │ │ │ ┌─────┘ │ ┌─────┘ 7 | // │ └─────┐│ └─────┐ │ └─────┐ 8 | // └───────┘└───────┘ └───────┘ 9 | // 10 | // Created by lee on 2019/4/1. 11 | // Copyright © 2019年 lee. All rights reserved. 12 | // 13 | 14 | import Foundation 15 | 16 | struct Context { 17 | let callback: (Bool) -> Void 18 | 19 | init(_ completion: @escaping (Bool) -> Void = { _ in }) { 20 | callback = completion 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Sources/PrivacyInfo.xcprivacy: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NSPrivacyTracking 6 | 7 | NSPrivacyAccessedAPITypes 8 | 9 | NSPrivacyTrackingDomains 10 | 11 | NSPrivacyCollectedDataTypes 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /Sources/Protocol.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Protocol.swift 3 | // ┌─┐ ┌───────┐ ┌───────┐ 4 | // │ │ │ ┌─────┘ │ ┌─────┘ 5 | // │ │ │ └─────┐ │ └─────┐ 6 | // │ │ │ ┌─────┘ │ ┌─────┘ 7 | // │ └─────┐│ └─────┐ │ └─────┐ 8 | // └───────┘└───────┘ └───────┘ 9 | // 10 | // Created by lee on 2019/4/1. 11 | // Copyright © 2019年 lee. All rights reserved. 12 | // 13 | 14 | import UIKit 15 | 16 | public protocol PluginType { 17 | 18 | /// 是否可以打开 19 | /// 20 | /// - Parameter target: 类型 21 | /// - Returns: true or false 22 | func should(open target: TargetType) -> Bool 23 | 24 | /// 准备打开 25 | /// 26 | /// - Parameters: 27 | /// - target: 类型 28 | /// - completion: 准备完成回调 (无论结果如何必须回调) 29 | func prepare(open target: TargetType, completion: @escaping (Bool) -> Void) 30 | 31 | /// 即将打开 32 | /// 33 | /// - Parameters: 34 | /// - target: 类型 35 | /// - controller: 视图控制器 36 | func will(open target: TargetType, controller: Routerable) 37 | 38 | /// 已经打开 39 | /// 40 | /// - Parameters: 41 | /// - target: 类型 42 | /// - controller: 视图控制器 43 | func did(open target: TargetType, controller: Routerable) 44 | } 45 | 46 | public typealias URLPattern = String 47 | 48 | public enum Task { 49 | 50 | case controller(Routerable) 51 | 52 | case handle(working: (_ completion: (@escaping (Bool) -> Void)) -> Void) 53 | } 54 | 55 | public protocol TargetType { 56 | 57 | var task: Task { get } 58 | } 59 | 60 | public struct URLPatternBinding { 61 | 62 | public struct Source { 63 | public let url: URLConvertible 64 | public let values: [String: Any] 65 | } 66 | 67 | let pattern: URLPattern 68 | let target: (Source) -> Target? 69 | 70 | public init(_ pattern: URLPattern, binding: @escaping () -> Target?) { 71 | self.pattern = pattern 72 | self.target = { _ in binding() } 73 | } 74 | 75 | public init(_ pattern: URLPattern, binding: @escaping (Source) -> Target?) { 76 | self.pattern = pattern 77 | self.target = binding 78 | } 79 | } 80 | 81 | public protocol URLTargetType: TargetType { 82 | 83 | static var bindings: [URLPatternBinding] { get } 84 | } 85 | 86 | public protocol Routerable: UIViewController { 87 | 88 | /// 打开 89 | /// 90 | /// - Parameter completion: 打开完成回调 91 | func open(with completion: @escaping () -> Void) 92 | 93 | /// 关闭 94 | /// 95 | /// - Parameters: 96 | /// - completion: 关闭完成回调 97 | func close(with completion: @escaping () -> Void) 98 | } 99 | 100 | public extension URL { 101 | 102 | func appending(_ params: [String: String]) -> String { 103 | return absoluteString.appending(params) 104 | } 105 | } 106 | 107 | public extension String { 108 | 109 | func appending(_ params: [String: String]) -> String { 110 | return appending(self, params) 111 | } 112 | 113 | func appending(_ url: String, _ params: [String: String]) -> String { 114 | guard var components = URLComponents(string: url) else { 115 | return url 116 | } 117 | 118 | let query = components.percentEncodedQuery ?? "" 119 | let temp = params.compactMap({ 120 | guard !$0.isEmpty, !$1.isEmpty else { return nil } 121 | guard let _ = Foundation.URL(string: $1) else { 122 | let encoded = $1.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? $1 123 | return "\($0)=\(encoded)" 124 | } 125 | 126 | let string = "?!@#$^&%*+,:;='\"`<>()[]{}/\\| " 127 | let character = CharacterSet(charactersIn: string).inverted 128 | let encoded = $1.addingPercentEncoding(withAllowedCharacters: character) ?? $1 129 | return "\($0)=\(encoded)" 130 | }).joined(separator: "&") 131 | components.percentEncodedQuery = query.isEmpty ? temp : query + "&" + temp 132 | return components.url?.absoluteString ?? url 133 | } 134 | } 135 | 136 | extension Routerable { 137 | 138 | public func open(with completion: @escaping () -> Void = {}) { 139 | guard let controller = UIViewController.top() else { 140 | return 141 | } 142 | 143 | if let navigation = controller as? UINavigationController { 144 | CATransaction.begin() 145 | CATransaction.setCompletionBlock(completion) 146 | navigation.pushViewController(self, animated: true) 147 | CATransaction.commit() 148 | 149 | } else if let navigation = controller.navigationController { 150 | CATransaction.begin() 151 | CATransaction.setCompletionBlock(completion) 152 | navigation.pushViewController(self, animated: true) 153 | CATransaction.commit() 154 | 155 | } else { 156 | let navigation = UINavigationController(rootViewController: self) 157 | controller.present(navigation, animated: true, completion: completion) 158 | } 159 | } 160 | 161 | public func close(with completion: @escaping () -> Void = {}) { 162 | guard 163 | let navigation = navigationController, 164 | navigation.viewControllers.first != self else { 165 | let presenting = presentingViewController ?? self 166 | presenting.dismiss(animated: true, completion: completion) 167 | return 168 | } 169 | guard presentedViewController == nil else { 170 | dismiss(animated: true) { [weak self] in self?.close(with: completion) } 171 | return 172 | } 173 | 174 | func parents(_ controller: UIViewController) -> [UIViewController] { 175 | guard let parent = controller.parent else { 176 | return [controller] 177 | } 178 | return [controller] + parents(parent) 179 | } 180 | 181 | CATransaction.begin() 182 | CATransaction.setCompletionBlock(completion) 183 | if let top = navigation.topViewController, parents(self).contains(top) { 184 | navigation.popViewController(animated: true) 185 | 186 | } else { 187 | let temp = navigation.viewControllers.filter { !parents(self).contains($0) } 188 | navigation.setViewControllers(temp, animated: true) 189 | } 190 | CATransaction.commit() 191 | } 192 | } 193 | 194 | extension PluginType { 195 | 196 | public func should(open target: TargetType) -> Bool { 197 | return true 198 | } 199 | 200 | public func prepare(open target: TargetType, completion: @escaping (Bool) -> Void) { 201 | completion(true) 202 | } 203 | 204 | public func will(open target: TargetType, controller: Routerable) { 205 | } 206 | 207 | public func did(open target: TargetType, controller: Routerable) { 208 | } 209 | } 210 | -------------------------------------------------------------------------------- /Sources/Provider.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Provider.swift 3 | // ┌─┐ ┌───────┐ ┌───────┐ 4 | // │ │ │ ┌─────┘ │ ┌─────┘ 5 | // │ │ │ └─────┐ │ └─────┐ 6 | // │ │ │ ┌─────┘ │ ┌─────┘ 7 | // │ └─────┐│ └─────┐ │ └─────┐ 8 | // └───────┘└───────┘ └───────┘ 9 | // 10 | // Created by lee on 2019/4/1. 11 | // Copyright © 2019年 lee. All rights reserved. 12 | // 13 | 14 | import UIKit 15 | 16 | private let navigator = URLNavigator() 17 | 18 | /// 打开 (全局) 19 | /// 20 | /// - Parameters: 21 | /// - url: url 22 | /// - completion: 打开完成回调 23 | /// - Returns: true or false 24 | @discardableResult 25 | public func open(_ url: URLConvertible, 26 | completion: ((Bool) -> Void)? = .none) -> Bool { 27 | return navigator.open(url, context: Context(completion ?? { _ in })) 28 | } 29 | 30 | /// 获取视图控制器 (全局) 31 | /// 32 | /// - Parameters: 33 | /// - url: url 34 | /// - context: context 35 | /// - Returns: 视图控制器 36 | public func controller(_ url: URLConvertible) -> Routerable? { 37 | return navigator.viewController(for: url) as? Routerable 38 | } 39 | 40 | public class Provider { 41 | 42 | private let plugins: [PluginType] 43 | 44 | public init(plugins: [PluginType]) { 45 | self.plugins = plugins 46 | } 47 | } 48 | 49 | extension Provider where Target: TargetType { 50 | 51 | /// 打开 52 | /// 53 | /// - Parameters: 54 | /// - target: TargetType 55 | /// - completion: 打开完成回调 56 | /// - Returns: true or false 57 | @discardableResult 58 | public func open(_ target: Target, completion: ((Bool) -> Void)? = .none) -> Bool { 59 | if self.plugins.isEmpty { 60 | switch target.task { 61 | case .controller(let controller): 62 | controller.open { 63 | completion?(true) 64 | } 65 | 66 | case .handle(working: let closure): 67 | closure { result in 68 | completion?(result) 69 | } 70 | } 71 | 72 | return true 73 | 74 | } else { 75 | guard self.plugins.contains(where: { $0.should(open: target) }) else { 76 | return false 77 | } 78 | 79 | var result = true 80 | let total = self.plugins.count 81 | var count = 0 82 | let group = DispatchGroup() 83 | self.plugins.forEach { p in 84 | group.enter() 85 | p.prepare(open: target) { 86 | // 防止插件多次回调 87 | defer { count += 1 } 88 | guard count < total else { return } 89 | 90 | result = $0 ? result : false 91 | group.leave() 92 | } 93 | } 94 | 95 | group.notify(queue: .main) { [weak self] in 96 | guard let self = self else { 97 | completion?(false) 98 | return 99 | } 100 | guard result else { 101 | completion?(false) 102 | return 103 | } 104 | 105 | switch target.task { 106 | case .controller(let controller): 107 | self.plugins.forEach { 108 | $0.will(open: target, controller: controller) 109 | } 110 | 111 | controller.open { [weak self] in 112 | guard let self = self else { return } 113 | self.plugins.forEach { 114 | $0.did(open: target, controller: controller) 115 | } 116 | 117 | completion?(true) 118 | } 119 | 120 | case .handle(working: let closure): 121 | closure { result in 122 | completion?(result) 123 | } 124 | } 125 | } 126 | 127 | return true 128 | } 129 | } 130 | 131 | /// 获取视图控制器 132 | /// 133 | /// - Parameters: 134 | /// - target: TargetType 135 | /// - Returns: 视图控制器 136 | public func controller(_ target: Target) -> Routerable? { 137 | guard case .controller(let controller) = target.task else { 138 | return nil 139 | } 140 | return controller 141 | } 142 | } 143 | 144 | extension Provider where Target: URLTargetType { 145 | 146 | /// 打开 147 | /// 148 | /// - Parameters: 149 | /// - url: url 150 | /// - completion: 打开完成回调 151 | /// - Returns: true or false 152 | @discardableResult 153 | public func open(_ url: URLConvertible, 154 | completion: ((Bool) -> Void)? = .none) -> Bool { 155 | let patterns = Target.bindings.map({ $0.pattern }) 156 | guard let result = navigator.matcher.match(url, from: patterns) else { 157 | return false 158 | } 159 | guard let target = self.target(result.pattern, url, result.values) else { 160 | return false 161 | } 162 | return open(target, completion: completion) 163 | } 164 | 165 | /// 获取视图控制器 166 | /// 167 | /// - Parameters: 168 | /// - url: url 169 | /// - context: context 170 | /// - Returns: 视图控制器 171 | public func controller(_ url: URLConvertible) -> Routerable? { 172 | let patterns = Target.bindings.map({ $0.pattern }) 173 | guard let result = navigator.matcher.match(url, from: patterns) else { 174 | return nil 175 | } 176 | guard let target = self.target(result.pattern, url, result.values) else { 177 | return nil 178 | } 179 | return controller(target) 180 | } 181 | } 182 | 183 | extension Provider where Target: URLTargetType { 184 | 185 | /// 加入全局 186 | public func global() { 187 | Target.bindings.forEach { binding in 188 | self.binding(binding.pattern) 189 | } 190 | } 191 | 192 | private func target(_ pattern: URLPattern, _ url: URLConvertible, _ values: [String : Any]) -> Target? { 193 | guard let binding = Target.bindings.last(where: { $0.pattern == pattern }) else { 194 | return nil 195 | } 196 | return binding.target(.init(url: url, values: values)) 197 | } 198 | 199 | typealias ViewControllerFactory = (_ url: URLConvertible, _ values: [String: Any], _ context: Any?) -> Routerable? 200 | 201 | private func register(_ pattern: URLPattern, _ factory: @escaping ViewControllerFactory) { 202 | navigator.register(pattern) { (url, values, context) -> UIViewController? in 203 | return factory(url, values, context) 204 | } 205 | } 206 | 207 | private func handle(_ pattern: URLPattern, _ factory: @escaping URLOpenHandlerFactory) { 208 | navigator.handle(pattern) { (url, values, context) -> Bool in 209 | return factory(url, values, context) 210 | } 211 | } 212 | 213 | private func binding(_ pattern: URLPattern) { 214 | self.register(pattern) { [weak self] (url, values, context) -> Routerable? in 215 | guard let self = self else { return nil } 216 | guard let target = self.target(pattern, url, values) else { 217 | return nil 218 | } 219 | return self.controller(target) 220 | } 221 | self.handle(pattern) { [weak self] (url, values, context) -> Bool in 222 | guard let self = self else { return false } 223 | guard let target = self.target(pattern, url, values) else { 224 | return false 225 | } 226 | let context = context as? Context 227 | return self.open(target, completion: context?.callback) 228 | } 229 | } 230 | } 231 | -------------------------------------------------------------------------------- /Sources/UIApplicationExtension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIApplicationExtension.swift 3 | // ┌─┐ ┌───────┐ ┌───────┐ 4 | // │ │ │ ┌─────┘ │ ┌─────┘ 5 | // │ │ │ └─────┐ │ └─────┐ 6 | // │ │ │ ┌─────┘ │ ┌─────┘ 7 | // │ └─────┐│ └─────┐ │ └─────┐ 8 | // └───────┘└───────┘ └───────┘ 9 | // 10 | // Created by lee on 2021/12/10. 11 | // Copyright © 2021 lee. All rights reserved. 12 | // 13 | 14 | import UIKit 15 | 16 | extension UIApplication { 17 | 18 | var window: UIWindow? { 19 | if #available(iOS 13.0, *) { 20 | let windows: [UIWindow] = UIApplication.shared.connectedScenes.compactMap { scene in 21 | guard let scene = scene as? UIWindowScene else { return nil } 22 | guard scene.session.role == .windowApplication else { return nil } 23 | guard let delegate = scene.delegate as? UIWindowSceneDelegate else { return nil } 24 | guard let window = delegate.window else { return nil } 25 | guard let window = window else { return nil } 26 | return window 27 | } 28 | 29 | if windows.isEmpty { 30 | guard let delegate = UIApplication.shared.delegate else { return nil } 31 | guard let window = delegate.window else { return nil } 32 | return window 33 | 34 | } else { 35 | return windows.first 36 | } 37 | 38 | } else { 39 | guard let delegate = UIApplication.shared.delegate else { return nil } 40 | guard let window = delegate.window else { return nil } 41 | return window 42 | } 43 | } 44 | } 45 | 46 | -------------------------------------------------------------------------------- /Sources/UIViewControllerExtension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIViewControllerExtension.swift 3 | // ┌─┐ ┌───────┐ ┌───────┐ 4 | // │ │ │ ┌─────┘ │ ┌─────┘ 5 | // │ │ │ └─────┐ │ └─────┐ 6 | // │ │ │ ┌─────┘ │ ┌─────┘ 7 | // │ └─────┐│ └─────┐ │ └─────┐ 8 | // └───────┘└───────┘ └───────┘ 9 | // 10 | // Created by lee on 2021/12/10. 11 | // Copyright © 2021 lee. All rights reserved. 12 | // 13 | 14 | import UIKit 15 | 16 | extension UIViewController { 17 | 18 | /// 获取Window下的顶层控制器 19 | @objc 20 | open class func top(in window: UIWindow? = .none) -> UIViewController? { 21 | return (window ?? UIApplication.shared.window)?.rootViewController?.top() 22 | } 23 | 24 | @objc 25 | open func top() -> UIViewController { 26 | // presented view controller 27 | if let controller = presentedViewController { 28 | return controller.top() 29 | } 30 | 31 | // UITabBarController 32 | if let tabBarController = self as? UITabBarController, 33 | let controller = tabBarController.selectedViewController { 34 | return controller.top() 35 | } 36 | 37 | // UINavigationController 38 | if let navigationController = self as? UINavigationController, 39 | let controller = navigationController.visibleViewController { 40 | return controller.top() 41 | } 42 | 43 | // UIPageController 44 | if let pageViewController = self as? UIPageViewController, 45 | pageViewController.viewControllers?.count == 1 , 46 | let controller = pageViewController.viewControllers?.first { 47 | return controller.top() 48 | } 49 | 50 | // child view controller 51 | // for subview in self.view?.subviews ?? [] { 52 | // if let controller = subview.next as? UIViewController { 53 | // return controller.top() 54 | // } 55 | // } 56 | return self 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Sources/URLConvertible.swift: -------------------------------------------------------------------------------- 1 | // 2 | // URLConvertible.swift 3 | // ┌─┐ ┌───────┐ ┌───────┐ 4 | // │ │ │ ┌─────┘ │ ┌─────┘ 5 | // │ │ │ └─────┐ │ └─────┐ 6 | // │ │ │ ┌─────┘ │ ┌─────┘ 7 | // │ └─────┐│ └─────┐ │ └─────┐ 8 | // └───────┘└───────┘ └───────┘ 9 | // 10 | // Created by lee on 2021/11/30. 11 | // Copyright © 2021 lee. All rights reserved. 12 | // 13 | 14 | import Foundation 15 | 16 | /// A type which can be converted to an URL string. 17 | public protocol URLConvertible { 18 | var value: URL? { get } 19 | var stringValue: String { get } 20 | 21 | /// Returns URL query parameters. For convenience, this property will never return `nil` even if 22 | /// there's no query string in the URL. This property doesn't take care of the duplicated keys. 23 | /// For checking duplicated keys, use `queryItems` instead. 24 | /// 25 | /// - seealso: `queryItems` 26 | var queryParameters: [String: String] { get } 27 | 28 | /// Returns `queryItems` property of `URLComponents` instance. 29 | /// 30 | /// - seealso: `queryParameters` 31 | @available(iOS 8, *) 32 | var queryItems: [URLQueryItem]? { get } 33 | } 34 | 35 | extension URLConvertible { 36 | 37 | public var queryParameters: [String: String] { 38 | var parameters = [String: String]() 39 | self.value?.query?.components(separatedBy: "&").forEach { component in 40 | guard let separatorIndex = component.firstIndex(of: "=") else { return } 41 | let keyRange = component.startIndex.. Any? 22 | 23 | static let defaultURLValueConverters: [String: URLValueConverter] = [ 24 | "string": { pathComponents, index in 25 | return pathComponents[index] 26 | }, 27 | "int": { pathComponents, index in 28 | return Int(pathComponents[index]) 29 | }, 30 | "float": { pathComponents, index in 31 | return Float(pathComponents[index]) 32 | }, 33 | "uuid": { pathComponents, index in 34 | return UUID(uuidString: pathComponents[index]) 35 | }, 36 | "path": { pathComponents, index in 37 | return pathComponents[index.."]) 49 | /// 50 | /// The value of the `URLPattern` from an example above is `"myapp://user/"` and the 51 | /// value of the `values` is `["id": 123]`. 52 | /// 53 | /// - parameter url: The placeholder-filled URL. 54 | /// - parameter from: The array of URL patterns. 55 | /// 56 | /// - returns: A `URLMatchComponents` struct that holds the URL pattern string, a dictionary of 57 | /// the URL placeholder values. 58 | func match(_ url: URLConvertible, from candidates: [URLPattern]) -> URLMatchResult? { 59 | let url = self.normalizeURL(url) 60 | let scheme = url.value?.scheme 61 | let stringPathComponents = self.stringPathComponents(from :url) 62 | 63 | for candidate in candidates { 64 | guard scheme == candidate.value?.scheme else { continue } 65 | if let result = self.match(stringPathComponents, with: candidate) { 66 | return result 67 | } 68 | } 69 | 70 | return nil 71 | } 72 | 73 | func match(_ stringPathComponents: [String], with candidate: URLPattern) -> URLMatchResult? { 74 | let normalizedCandidate = self.normalizeURL(candidate).stringValue 75 | let candidatePathComponents = self.pathComponents(from: normalizedCandidate) 76 | guard self.ensurePathComponentsCount(stringPathComponents, candidatePathComponents) else { 77 | return nil 78 | } 79 | 80 | var urlValues: [String: Any] = [:] 81 | 82 | let pairCount = min(stringPathComponents.count, candidatePathComponents.count) 83 | for index in 0 ..< pairCount { 84 | let result = self.matchStringPathComponent( 85 | at: index, 86 | from: stringPathComponents, 87 | with: candidatePathComponents 88 | ) 89 | 90 | switch result { 91 | case let .matches(placeholderValue): 92 | if let (key, value) = placeholderValue { 93 | urlValues[key] = value 94 | } 95 | 96 | case .notMatches: 97 | return nil 98 | } 99 | } 100 | 101 | return URLMatchResult(pattern: candidate, values: urlValues) 102 | } 103 | 104 | private func normalizeURL(_ dirtyURL: URLConvertible) -> URLConvertible { 105 | guard dirtyURL.value != nil else { return dirtyURL } 106 | var urlString = dirtyURL.stringValue 107 | urlString = urlString.components(separatedBy: "?")[0].components(separatedBy: "#")[0] 108 | urlString = self.replaceRegex(":/{3,}", "://", urlString) 109 | urlString = self.replaceRegex("(? String { 115 | guard let regex = try? NSRegularExpression(pattern: pattern, options: []) else { return string } 116 | let range = NSMakeRange(0, string.count) 117 | return regex.stringByReplacingMatches(in: string, options: [], range: range, withTemplate: repl) 118 | } 119 | 120 | private func ensurePathComponentsCount( 121 | _ stringPathComponents: [String], 122 | _ candidatePathComponents: [URLPathComponent] 123 | ) -> Bool { 124 | let hasSameNumberOfComponents = (stringPathComponents.count == candidatePathComponents.count) 125 | let containsPathPlaceholderComponent = candidatePathComponents.contains { 126 | if case let .placeholder(type, _) = $0, type == "path" { 127 | return true 128 | } else { 129 | return false 130 | } 131 | } 132 | return hasSameNumberOfComponents || (containsPathPlaceholderComponent && stringPathComponents.count > candidatePathComponents.count) 133 | } 134 | 135 | private func stringPathComponents(from url: URLConvertible) -> [String] { 136 | return url.stringValue.components(separatedBy: "/").lazy 137 | .filter { !$0.isEmpty } 138 | .filter { !$0.hasSuffix(":") } 139 | } 140 | 141 | private func pathComponents(from url: URLPattern) -> [URLPathComponent] { 142 | return stringPathComponents(from: url).map(URLPathComponent.init) 143 | } 144 | 145 | private func matchStringPathComponent( 146 | at index: Int, 147 | from stringPathComponents: [String], 148 | with candidatePathComponents: [URLPathComponent] 149 | ) -> URLPathComponentMatchResult { 150 | let stringPathComponent = stringPathComponents[index] 151 | let urlPathComponent = candidatePathComponents[index] 152 | 153 | switch urlPathComponent { 154 | case let .plain(value): 155 | guard stringPathComponent == value else { return .notMatches } 156 | return .matches(nil) 157 | 158 | case let .placeholder(type, key): 159 | guard let type = type, let converter = self.valueConverters[type] else { 160 | return .matches((key, stringPathComponent)) 161 | } 162 | if let value = converter(stringPathComponents, index) { 163 | return .matches((key, value)) 164 | 165 | } else { 166 | return .notMatches 167 | } 168 | } 169 | } 170 | } 171 | 172 | 173 | /// Represents an URL match result. 174 | struct URLMatchResult { 175 | /// The url pattern that was matched. 176 | let pattern: String 177 | 178 | /// The values extracted from the URL placeholder. 179 | let values: [String: Any] 180 | } 181 | 182 | enum URLPathComponentMatchResult { 183 | case matches((key: String, value: Any)?) 184 | case notMatches 185 | } 186 | -------------------------------------------------------------------------------- /Sources/URLNavigator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Navigator.swift 3 | // ┌─┐ ┌───────┐ ┌───────┐ 4 | // │ │ │ ┌─────┘ │ ┌─────┘ 5 | // │ │ │ └─────┐ │ └─────┐ 6 | // │ │ │ ┌─────┘ │ ┌─────┘ 7 | // │ └─────┐│ └─────┐ │ └─────┐ 8 | // └───────┘└───────┘ └───────┘ 9 | // 10 | // Created by lee on 2021/12/10. 11 | // Copyright © 2021 lee. All rights reserved. 12 | // 13 | 14 | import UIKit 15 | 16 | typealias ViewControllerFactory = (_ url: URLConvertible, _ values: [String: Any], _ context: Any?) -> UIViewController? 17 | typealias URLOpenHandlerFactory = (_ url: URLConvertible, _ values: [String: Any], _ context: Any?) -> Bool 18 | typealias URLOpenHandler = () -> Bool 19 | 20 | class URLNavigator { 21 | 22 | let matcher = URLMatcher() 23 | 24 | private var controllerFactories = [URLPattern: ViewControllerFactory]() 25 | private var handlerFactories = [URLPattern: URLOpenHandlerFactory]() 26 | 27 | func register(_ pattern: URLPattern, _ factory: @escaping ViewControllerFactory) { 28 | self.controllerFactories[pattern] = factory 29 | } 30 | 31 | func handle(_ pattern: URLPattern, _ factory: @escaping URLOpenHandlerFactory) { 32 | self.handlerFactories[pattern] = factory 33 | } 34 | 35 | func viewController(for url: URLConvertible, context: Any? = nil) -> UIViewController? { 36 | let urlPatterns = Array(self.controllerFactories.keys) 37 | guard let match = self.matcher.match(url, from: urlPatterns) else { return nil } 38 | guard let factory = self.controllerFactories[match.pattern] else { return nil } 39 | return factory(url, match.values, context) 40 | } 41 | 42 | func handler(for url: URLConvertible, context: Any?) -> URLOpenHandler? { 43 | let urlPatterns = Array(self.handlerFactories.keys) 44 | guard let match = self.matcher.match(url, from: urlPatterns) else { return nil } 45 | guard let handler = self.handlerFactories[match.pattern] else { return nil } 46 | return { handler(url, match.values, context) } 47 | } 48 | 49 | @discardableResult 50 | func open(_ url: URLConvertible, context: Any? = nil) -> Bool { 51 | guard let handler = self.handler(for: url, context: context) else { return false } 52 | return handler() 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Sources/URLPathComponent.swift: -------------------------------------------------------------------------------- 1 | // 2 | // URLPathComponent.swift 3 | // ┌─┐ ┌───────┐ ┌───────┐ 4 | // │ │ │ ┌─────┘ │ ┌─────┘ 5 | // │ │ │ └─────┐ │ └─────┐ 6 | // │ │ │ ┌─────┘ │ ┌─────┘ 7 | // │ └─────┐│ └─────┐ │ └─────┐ 8 | // └───────┘└───────┘ └───────┘ 9 | // 10 | // Created by lee on 2021/11/30. 11 | // Copyright © 2021 lee. All rights reserved. 12 | // 13 | 14 | import Foundation 15 | 16 | enum URLPathComponent { 17 | case plain(String) 18 | case placeholder(type: String?, key: String) 19 | } 20 | 21 | extension URLPathComponent { 22 | 23 | init(_ value: String) { 24 | if value.hasPrefix("<") && value.hasSuffix(">") { 25 | let start = value.index(after: value.startIndex) 26 | let end = value.index(before: value.endIndex) 27 | let placeholder = value[start.." -> "int:id" 28 | let typeAndKey = placeholder.components(separatedBy: ":") 29 | if typeAndKey.count == 1 { // any-type placeholder 30 | self = .placeholder(type: nil, key: typeAndKey[0]) 31 | 32 | } else if typeAndKey.count == 2 { 33 | self = .placeholder(type: typeAndKey[0], key: typeAndKey[1]) 34 | 35 | } else { 36 | self = .plain(value) 37 | } 38 | 39 | } else { 40 | self = .plain(value) 41 | } 42 | } 43 | } 44 | 45 | extension URLPathComponent: Equatable { 46 | 47 | static func == (lhs: URLPathComponent, rhs: URLPathComponent) -> Bool { 48 | switch (lhs, rhs) { 49 | case let (.plain(leftValue), .plain(rightValue)): 50 | return leftValue == rightValue 51 | 52 | case let (.placeholder(leftType, leftKey), .placeholder(rightType, key: rightKey)): 53 | return (leftType == rightType) && (leftKey == rightKey) 54 | 55 | default: 56 | return false 57 | } 58 | } 59 | } 60 | --------------------------------------------------------------------------------