Browse Source

搭建swift pod框架环境

openlockPPP 1 year ago
parent
commit
ee1153db7b
100 changed files with 7375 additions and 323 deletions
  1. 623 7
      JiaPeiManage.xcodeproj/project.pbxproj
  2. 15 0
      JiaPeiManage/JSJP-Brigding-Header.h
  3. 0 16
      JiaPeiManage/SceneDelegate.swift
  4. 42 0
      JiaPeiManage/Sources/AppDelegate.swift
  5. 141 0
      JiaPeiManage/Sources/CompositionRoot.swift
  6. 48 0
      JiaPeiManage/Sources/Constants/NYFitReal.swift
  7. 44 0
      JiaPeiManage/Sources/Constants/NYFont.swift
  8. 69 0
      JiaPeiManage/Sources/Constants/NYImage.swift
  9. 46 0
      JiaPeiManage/Sources/Constants/NYMacros.swift
  10. 15 0
      JiaPeiManage/Sources/Constants/NYNumber.swift
  11. 60 0
      JiaPeiManage/Sources/Custom/Protocol/NetAnimationLoadable.swift
  12. 19 0
      JiaPeiManage/Sources/Custom/Protocol/NibLoadable.swift
  13. 94 0
      JiaPeiManage/Sources/Custom/Protocol/Refreshable.swift
  14. 47 0
      JiaPeiManage/Sources/Custom/VideoPlayer/Component/LWPlayerFullScreenViewController.swift
  15. 577 0
      JiaPeiManage/Sources/Custom/VideoPlayer/Core/LWPlayer.swift
  16. 163 0
      JiaPeiManage/Sources/Custom/VideoPlayer/Core/LWPlayerControlView.swift
  17. 25 0
      JiaPeiManage/Sources/Custom/VideoPlayer/Core/LWPlayerManager.swift
  18. 70 0
      JiaPeiManage/Sources/Custom/VideoPlayer/Core/LWPlayerSlider.swift
  19. 227 0
      JiaPeiManage/Sources/Custom/VideoPlayer/Core/LWPlayerView.swift
  20. 37 0
      JiaPeiManage/Sources/Custom/VideoPlayer/Extension/AVPlayer+LWPlayer.swift
  21. 33 0
      JiaPeiManage/Sources/Custom/VideoPlayer/Extension/Timer+LWPlayer.swift
  22. 48 0
      JiaPeiManage/Sources/Custom/VideoPlayer/Utiles/LWPlayerDelegate.swift
  23. 60 0
      JiaPeiManage/Sources/Custom/VideoPlayer/Utiles/LWPlayerEnum.swift
  24. 85 0
      JiaPeiManage/Sources/Custom/VideoPlayer/Utiles/LWPlayerUtils.swift
  25. 36 0
      JiaPeiManage/Sources/Custom/View/CollectionViewLayout/BilibiliCollectionViewLayout.swift
  26. 15 0
      JiaPeiManage/Sources/Custom/View/EmptyView/EmptyView.swift
  27. 62 0
      JiaPeiManage/Sources/Custom/View/EmptyView/EmptyView.xib
  28. 22 0
      JiaPeiManage/Sources/Custom/View/GesConflictCollectionView/GesConflictCollectionView.swift
  29. 104 0
      JiaPeiManage/Sources/Custom/View/ImageViewAnimation/NetAnimationView.swift
  30. 20 0
      JiaPeiManage/Sources/Custom/View/PopOverView/Action.swift
  31. 609 0
      JiaPeiManage/Sources/Custom/View/PopOverView/PopOverView.swift
  32. 50 0
      JiaPeiManage/Sources/Custom/View/PopOverView/PopOverViewCell.swift
  33. 62 0
      JiaPeiManage/Sources/Custom/View/PopOverView/PopOverViewCell.xib
  34. 77 0
      JiaPeiManage/Sources/Custom/View/PopOverView/PopOverViewSettings.swift
  35. 17 0
      JiaPeiManage/Sources/Custom/View/SearchBarView/SearchBarView.swift
  36. 47 0
      JiaPeiManage/Sources/Custom/View/SearchBarView/SearchBarView.xib
  37. 26 0
      JiaPeiManage/Sources/Custom/View/Toaster/NYSwToaster.swift
  38. 46 0
      JiaPeiManage/Sources/Extensions/Array+SectionModel.swift
  39. 29 0
      JiaPeiManage/Sources/Extensions/Data+Cache.swift
  40. 26 0
      JiaPeiManage/Sources/Extensions/DefaultsKeys+Key.swift
  41. 43 0
      JiaPeiManage/Sources/Extensions/DispatchQueue+Extension.swift
  42. 15 0
      JiaPeiManage/Sources/Extensions/NSAttributedString+BoundingRect.swift
  43. 38 0
      JiaPeiManage/Sources/Extensions/String+BoundingRect.swift
  44. 184 0
      JiaPeiManage/Sources/Extensions/UIBarButtonItem+FixSpace.swift
  45. 60 0
      JiaPeiManage/Sources/Extensions/UIBarButtonItem+Init.swift
  46. 56 0
      JiaPeiManage/Sources/Extensions/UIButton+Kingfisher.swift
  47. 27 0
      JiaPeiManage/Sources/Extensions/UIColor+Bilibili.swift
  48. 62 0
      JiaPeiManage/Sources/Extensions/UIColor+Hex.swift
  49. 41 0
      JiaPeiManage/Sources/Extensions/UIImage+Placeholder.swift
  50. 77 0
      JiaPeiManage/Sources/Extensions/UIImageView+Kingfisher.swift
  51. 66 0
      JiaPeiManage/Sources/Extensions/UIScrollView+Direction.swift
  52. 30 0
      JiaPeiManage/Sources/Extensions/UIScrollView+ScrollToBottom.swift
  53. 60 0
      JiaPeiManage/Sources/Extensions/UIView+CornerRadius.swift
  54. 41 0
      JiaPeiManage/Sources/Extensions/UIViewController+NetAnimation.swift
  55. 28 0
      JiaPeiManage/Sources/Extensions/VTContentView+Gesture.swift
  56. 135 0
      JiaPeiManage/Sources/Logging/Logger.swift
  57. 150 0
      JiaPeiManage/Sources/Main/Base/BaseCollectionViewController.swift
  58. 132 0
      JiaPeiManage/Sources/Main/Base/BaseTableViewController.swift
  59. 102 0
      JiaPeiManage/Sources/Main/Base/BaseViewController.swift
  60. 41 0
      JiaPeiManage/Sources/Main/MainNavigationController.swift
  61. 83 0
      JiaPeiManage/Sources/Main/MainTabBarController.swift
  62. 17 0
      JiaPeiManage/Sources/Main/MainTabBarViewReactor.swift
  63. 23 0
      JiaPeiManage/Sources/Main/NYArticleNavBar.swift
  64. 70 0
      JiaPeiManage/Sources/Main/NYArticleNavBar.xib
  65. 190 0
      JiaPeiManage/Sources/Main/NYArticleViewController.swift
  66. 143 0
      JiaPeiManage/Sources/Main/NYWebViewController.swift
  67. 286 0
      JiaPeiManage/Sources/Models/UserInfoModel.swift
  68. 17 0
      JiaPeiManage/Sources/Modulars/Login/Controllers/LoginViewController.swift
  69. 31 0
      JiaPeiManage/Sources/Modulars/Login/Controllers/LoginViewController.xib
  70. 99 0
      JiaPeiManage/Sources/Modulars/Splash/SplashModel.swift
  71. 64 0
      JiaPeiManage/Sources/Modulars/Splash/SplashViewController.swift
  72. 38 0
      JiaPeiManage/Sources/NYAppCongfigure.swift
  73. 53 0
      JiaPeiManage/Sources/Networking/NetEnvironment.swift
  74. 102 0
      JiaPeiManage/Sources/Networking/Networking.swift
  75. 34 0
      JiaPeiManage/Sources/Networking/Plugins/LoadingPlugin.swift
  76. 74 0
      JiaPeiManage/Sources/Networking/RequestError.swift
  77. 67 0
      JiaPeiManage/Sources/Rx/Moya+Rx.swift
  78. 46 0
      JiaPeiManage/Sources/Rx/NotificationCenter+Rx.swift
  79. 23 0
      JiaPeiManage/Sources/Rx/PullToRefresh+Rx.swift
  80. 20 0
      JiaPeiManage/Sources/Rx/UICollectionView+Rx.swift
  81. 11 0
      JiaPeiManage/Sources/Rx/UICollectionViewFlexLayout+Rx.swift
  82. 21 0
      JiaPeiManage/Sources/Rx/UILabel+Rx.swift
  83. 24 0
      JiaPeiManage/Sources/Rx/UIScrollView+Rx.swift
  84. 32 0
      JiaPeiManage/Sources/Rx/UIView+Rx.swift
  85. 118 0
      JiaPeiManage/Sources/Services/LoginAPI.swift
  86. 34 0
      JiaPeiManage/Sources/Services/LoginService.swift
  87. 23 0
      JiaPeiManage/Sources/Utils/LocalManager.swift
  88. 77 0
      JiaPeiManage/Sources/Utils/NYSwRouter.swift
  89. 27 0
      JiaPeiManage/Sources/Utils/Snap.swift
  90. 123 0
      JiaPeiManage/Sources/Utils/URLNavigationMap.swift
  91. 1 1
      Podfile
  92. 4 4
      Podfile.lock
  93. 4 4
      Pods/Manifest.lock
  94. 156 156
      Pods/Pods.xcodeproj/project.pbxproj
  95. 1 1
      Pods/Target Support Files/URLNavigator/URLNavigator-Info.plist
  96. 5 5
      Pods/URLNavigator/README.md
  97. 5 21
      Pods/URLNavigator/Sources/URLMatcher/URLMatcher.swift
  98. 2 105
      Pods/URLNavigator/Sources/URLNavigator/Navigator.swift
  99. 1 1
      Pods/URLNavigator/Sources/URLNavigator/NavigatorDelegate.swift
  100. 2 2
      Pods/URLNavigator/Sources/URLNavigator/UIViewControllerType.swift

+ 623 - 7
JiaPeiManage.xcodeproj/project.pbxproj

@@ -8,11 +8,96 @@
 
 /* Begin PBXBuildFile section */
 		90CE52192A36C0710033BD06 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90CE52182A36C0710033BD06 /* AppDelegate.swift */; };
-		90CE521B2A36C0710033BD06 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90CE521A2A36C0710033BD06 /* SceneDelegate.swift */; };
 		90CE521D2A36C0710033BD06 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90CE521C2A36C0710033BD06 /* ViewController.swift */; };
 		90CE52202A36C0710033BD06 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 90CE521E2A36C0710033BD06 /* Main.storyboard */; };
 		90CE52222A36C0720033BD06 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 90CE52212A36C0720033BD06 /* Assets.xcassets */; };
 		90CE52252A36C0720033BD06 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 90CE52232A36C0720033BD06 /* LaunchScreen.storyboard */; };
+		90CE52322A36E4600033BD06 /* BaseCollectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90CE522F2A36E4600033BD06 /* BaseCollectionViewController.swift */; };
+		90CE52332A36E4600033BD06 /* BaseViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90CE52302A36E4600033BD06 /* BaseViewController.swift */; };
+		90CE52342A36E4600033BD06 /* BaseTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90CE52312A36E4600033BD06 /* BaseTableViewController.swift */; };
+		90CE52382A36E4720033BD06 /* MainNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90CE52352A36E4710033BD06 /* MainNavigationController.swift */; };
+		90CE52392A36E4720033BD06 /* MainTabBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90CE52362A36E4720033BD06 /* MainTabBarController.swift */; };
+		90CE523A2A36E4720033BD06 /* MainTabBarViewReactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90CE52372A36E4720033BD06 /* MainTabBarViewReactor.swift */; };
+		90CE52432A36E5170033BD06 /* NYFont.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90CE523E2A36E5170033BD06 /* NYFont.swift */; };
+		90CE52442A36E5170033BD06 /* NYMacros.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90CE523F2A36E5170033BD06 /* NYMacros.swift */; };
+		90CE52452A36E5170033BD06 /* NYFitReal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90CE52402A36E5170033BD06 /* NYFitReal.swift */; };
+		90CE52462A36E5170033BD06 /* NYNumber.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90CE52412A36E5170033BD06 /* NYNumber.swift */; };
+		90CE52472A36E5170033BD06 /* NYImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90CE52422A36E5170033BD06 /* NYImage.swift */; };
+		90CE52722A36E5460033BD06 /* LWPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90CE524B2A36E5460033BD06 /* LWPlayer.swift */; };
+		90CE52732A36E5460033BD06 /* LWPlayerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90CE524C2A36E5460033BD06 /* LWPlayerView.swift */; };
+		90CE52742A36E5460033BD06 /* LWPlayerManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90CE524D2A36E5460033BD06 /* LWPlayerManager.swift */; };
+		90CE52752A36E5460033BD06 /* LWPlayerControlView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90CE524E2A36E5460033BD06 /* LWPlayerControlView.swift */; };
+		90CE52762A36E5460033BD06 /* LWPlayerSlider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90CE524F2A36E5460033BD06 /* LWPlayerSlider.swift */; };
+		90CE52772A36E5460033BD06 /* AVPlayer+LWPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90CE52512A36E5460033BD06 /* AVPlayer+LWPlayer.swift */; };
+		90CE52782A36E5460033BD06 /* Timer+LWPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90CE52522A36E5460033BD06 /* Timer+LWPlayer.swift */; };
+		90CE52792A36E5460033BD06 /* LWPlayerUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90CE52542A36E5460033BD06 /* LWPlayerUtils.swift */; };
+		90CE527A2A36E5460033BD06 /* LWPlayerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90CE52552A36E5460033BD06 /* LWPlayerDelegate.swift */; };
+		90CE527B2A36E5460033BD06 /* LWPlayerEnum.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90CE52562A36E5460033BD06 /* LWPlayerEnum.swift */; };
+		90CE527C2A36E5460033BD06 /* LWPlayerFullScreenViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90CE52582A36E5460033BD06 /* LWPlayerFullScreenViewController.swift */; };
+		90CE527D2A36E5460033BD06 /* Refreshable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90CE525A2A36E5460033BD06 /* Refreshable.swift */; };
+		90CE527E2A36E5460033BD06 /* NetAnimationLoadable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90CE525B2A36E5460033BD06 /* NetAnimationLoadable.swift */; };
+		90CE527F2A36E5460033BD06 /* NibLoadable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90CE525C2A36E5460033BD06 /* NibLoadable.swift */; };
+		90CE52802A36E5460033BD06 /* NYSwToaster.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90CE525F2A36E5460033BD06 /* NYSwToaster.swift */; };
+		90CE52812A36E5460033BD06 /* BilibiliCollectionViewLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90CE52612A36E5460033BD06 /* BilibiliCollectionViewLayout.swift */; };
+		90CE52822A36E5460033BD06 /* GesConflictCollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90CE52632A36E5460033BD06 /* GesConflictCollectionView.swift */; };
+		90CE52832A36E5460033BD06 /* SearchBarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90CE52652A36E5460033BD06 /* SearchBarView.swift */; };
+		90CE52842A36E5460033BD06 /* SearchBarView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 90CE52662A36E5460033BD06 /* SearchBarView.xib */; };
+		90CE52852A36E5460033BD06 /* EmptyView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 90CE52682A36E5460033BD06 /* EmptyView.xib */; };
+		90CE52862A36E5460033BD06 /* EmptyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90CE52692A36E5460033BD06 /* EmptyView.swift */; };
+		90CE52872A36E5460033BD06 /* NetAnimationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90CE526B2A36E5460033BD06 /* NetAnimationView.swift */; };
+		90CE52882A36E5460033BD06 /* PopOverView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90CE526D2A36E5460033BD06 /* PopOverView.swift */; };
+		90CE52892A36E5460033BD06 /* PopOverViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90CE526E2A36E5460033BD06 /* PopOverViewCell.swift */; };
+		90CE528A2A36E5460033BD06 /* PopOverViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 90CE526F2A36E5460033BD06 /* PopOverViewCell.xib */; };
+		90CE528B2A36E5460033BD06 /* PopOverViewSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90CE52702A36E5460033BD06 /* PopOverViewSettings.swift */; };
+		90CE528C2A36E5460033BD06 /* Action.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90CE52712A36E5460033BD06 /* Action.swift */; };
+		90CE52A02A36E59A0033BD06 /* Data+Cache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90CE528E2A36E5990033BD06 /* Data+Cache.swift */; };
+		90CE52A12A36E59A0033BD06 /* DefaultsKeys+Key.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90CE528F2A36E5990033BD06 /* DefaultsKeys+Key.swift */; };
+		90CE52A22A36E59A0033BD06 /* UIColor+Hex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90CE52902A36E5990033BD06 /* UIColor+Hex.swift */; };
+		90CE52A32A36E59A0033BD06 /* UIImageView+Kingfisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90CE52912A36E5990033BD06 /* UIImageView+Kingfisher.swift */; };
+		90CE52A42A36E59A0033BD06 /* DispatchQueue+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90CE52922A36E5990033BD06 /* DispatchQueue+Extension.swift */; };
+		90CE52A52A36E59A0033BD06 /* UIButton+Kingfisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90CE52932A36E5990033BD06 /* UIButton+Kingfisher.swift */; };
+		90CE52A62A36E59A0033BD06 /* String+BoundingRect.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90CE52942A36E5990033BD06 /* String+BoundingRect.swift */; };
+		90CE52A72A36E59A0033BD06 /* UIImage+Placeholder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90CE52952A36E5990033BD06 /* UIImage+Placeholder.swift */; };
+		90CE52A82A36E59A0033BD06 /* UIScrollView+Direction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90CE52962A36E5990033BD06 /* UIScrollView+Direction.swift */; };
+		90CE52A92A36E59A0033BD06 /* UIBarButtonItem+Init.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90CE52972A36E5990033BD06 /* UIBarButtonItem+Init.swift */; };
+		90CE52AA2A36E59A0033BD06 /* UIBarButtonItem+FixSpace.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90CE52982A36E5990033BD06 /* UIBarButtonItem+FixSpace.swift */; };
+		90CE52AB2A36E59A0033BD06 /* UIViewController+NetAnimation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90CE52992A36E5990033BD06 /* UIViewController+NetAnimation.swift */; };
+		90CE52AC2A36E59A0033BD06 /* Array+SectionModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90CE529A2A36E5990033BD06 /* Array+SectionModel.swift */; };
+		90CE52AD2A36E59A0033BD06 /* UIView+CornerRadius.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90CE529B2A36E5990033BD06 /* UIView+CornerRadius.swift */; };
+		90CE52AE2A36E59A0033BD06 /* UIScrollView+ScrollToBottom.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90CE529C2A36E5990033BD06 /* UIScrollView+ScrollToBottom.swift */; };
+		90CE52AF2A36E59A0033BD06 /* UIColor+Bilibili.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90CE529D2A36E5990033BD06 /* UIColor+Bilibili.swift */; };
+		90CE52B02A36E59A0033BD06 /* NSAttributedString+BoundingRect.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90CE529E2A36E5990033BD06 /* NSAttributedString+BoundingRect.swift */; };
+		90CE52B12A36E59A0033BD06 /* VTContentView+Gesture.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90CE529F2A36E5990033BD06 /* VTContentView+Gesture.swift */; };
+		90CE52B42A36E5A40033BD06 /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90CE52B32A36E5A40033BD06 /* Logger.swift */; };
+		90CE52BB2A36E5B70033BD06 /* LoadingPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90CE52B72A36E5B70033BD06 /* LoadingPlugin.swift */; };
+		90CE52BC2A36E5B70033BD06 /* Networking.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90CE52B82A36E5B70033BD06 /* Networking.swift */; };
+		90CE52BD2A36E5B70033BD06 /* NetEnvironment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90CE52B92A36E5B70033BD06 /* NetEnvironment.swift */; };
+		90CE52BE2A36E5B70033BD06 /* RequestError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90CE52BA2A36E5B70033BD06 /* RequestError.swift */; };
+		90CE52C92A36E5EC0033BD06 /* Moya+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90CE52C12A36E5EC0033BD06 /* Moya+Rx.swift */; };
+		90CE52CA2A36E5EC0033BD06 /* UIScrollView+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90CE52C22A36E5EC0033BD06 /* UIScrollView+Rx.swift */; };
+		90CE52CB2A36E5EC0033BD06 /* UILabel+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90CE52C32A36E5EC0033BD06 /* UILabel+Rx.swift */; };
+		90CE52CC2A36E5EC0033BD06 /* NotificationCenter+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90CE52C42A36E5EC0033BD06 /* NotificationCenter+Rx.swift */; };
+		90CE52CD2A36E5EC0033BD06 /* UIView+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90CE52C52A36E5EC0033BD06 /* UIView+Rx.swift */; };
+		90CE52CE2A36E5EC0033BD06 /* UICollectionViewFlexLayout+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90CE52C62A36E5EC0033BD06 /* UICollectionViewFlexLayout+Rx.swift */; };
+		90CE52CF2A36E5EC0033BD06 /* UICollectionView+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90CE52C72A36E5EC0033BD06 /* UICollectionView+Rx.swift */; };
+		90CE52D02A36E5EC0033BD06 /* PullToRefresh+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90CE52C82A36E5EC0033BD06 /* PullToRefresh+Rx.swift */; };
+		90CE52D62A36E6000033BD06 /* NYSwRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90CE52D22A36E6000033BD06 /* NYSwRouter.swift */; };
+		90CE52D72A36E6000033BD06 /* Snap.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90CE52D32A36E6000033BD06 /* Snap.swift */; };
+		90CE52D82A36E6000033BD06 /* URLNavigationMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90CE52D42A36E6000033BD06 /* URLNavigationMap.swift */; };
+		90CE52D92A36E6000033BD06 /* LocalManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90CE52D52A36E6000033BD06 /* LocalManager.swift */; };
+		90CE52DC2A36E6250033BD06 /* NYAppCongfigure.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90CE52DA2A36E6250033BD06 /* NYAppCongfigure.swift */; };
+		90CE52DD2A36E6250033BD06 /* CompositionRoot.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90CE52DB2A36E6250033BD06 /* CompositionRoot.swift */; };
+		90CE52E12A36E73A0033BD06 /* SplashModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90CE52DF2A36E73A0033BD06 /* SplashModel.swift */; };
+		90CE52E22A36E73A0033BD06 /* SplashViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90CE52E02A36E73A0033BD06 /* SplashViewController.swift */; };
+		90CE52E92A36E85C0033BD06 /* LoginViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90CE52E82A36E85C0033BD06 /* LoginViewController.swift */; };
+		90CE52EB2A36E8680033BD06 /* LoginViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 90CE52EA2A36E8680033BD06 /* LoginViewController.xib */; };
+		90CE52ED2A36EBE80033BD06 /* UserInfoModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90CE52EC2A36EBE70033BD06 /* UserInfoModel.swift */; };
+		90CE52EF2A36EC950033BD06 /* NYArticleViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90CE52EE2A36EC950033BD06 /* NYArticleViewController.swift */; };
+		90CE52F12A36ECC80033BD06 /* NYWebViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90CE52F02A36ECC80033BD06 /* NYWebViewController.swift */; };
+		90CE52F42A36EDCF0033BD06 /* NYArticleNavBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90CE52F22A36EDCE0033BD06 /* NYArticleNavBar.swift */; };
+		90CE52F52A36EDCF0033BD06 /* NYArticleNavBar.xib in Resources */ = {isa = PBXBuildFile; fileRef = 90CE52F32A36EDCF0033BD06 /* NYArticleNavBar.xib */; };
+		90CE52F82A36F0BD0033BD06 /* LoginAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90CE52F72A36F0BD0033BD06 /* LoginAPI.swift */; };
+		90CE52FA2A36F2570033BD06 /* LoginService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90CE52F92A36F2570033BD06 /* LoginService.swift */; };
 		FD091E28E4C2EF8E654AE2C6 /* Pods_JiaPeiManage.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1FC2879EF62A9D4DF6777C62 /* Pods_JiaPeiManage.framework */; };
 /* End PBXBuildFile section */
 
@@ -20,12 +105,98 @@
 		1FC2879EF62A9D4DF6777C62 /* Pods_JiaPeiManage.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_JiaPeiManage.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		90CE52152A36C0700033BD06 /* JiaPeiManage.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = JiaPeiManage.app; sourceTree = BUILT_PRODUCTS_DIR; };
 		90CE52182A36C0710033BD06 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
-		90CE521A2A36C0710033BD06 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
 		90CE521C2A36C0710033BD06 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
 		90CE521F2A36C0710033BD06 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
 		90CE52212A36C0720033BD06 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
 		90CE52242A36C0720033BD06 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
 		90CE52262A36C0720033BD06 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+		90CE522F2A36E4600033BD06 /* BaseCollectionViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BaseCollectionViewController.swift; sourceTree = "<group>"; };
+		90CE52302A36E4600033BD06 /* BaseViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BaseViewController.swift; sourceTree = "<group>"; };
+		90CE52312A36E4600033BD06 /* BaseTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BaseTableViewController.swift; sourceTree = "<group>"; };
+		90CE52352A36E4710033BD06 /* MainNavigationController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainNavigationController.swift; sourceTree = "<group>"; };
+		90CE52362A36E4720033BD06 /* MainTabBarController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainTabBarController.swift; sourceTree = "<group>"; };
+		90CE52372A36E4720033BD06 /* MainTabBarViewReactor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainTabBarViewReactor.swift; sourceTree = "<group>"; };
+		90CE523E2A36E5170033BD06 /* NYFont.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NYFont.swift; sourceTree = "<group>"; };
+		90CE523F2A36E5170033BD06 /* NYMacros.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NYMacros.swift; sourceTree = "<group>"; };
+		90CE52402A36E5170033BD06 /* NYFitReal.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NYFitReal.swift; sourceTree = "<group>"; };
+		90CE52412A36E5170033BD06 /* NYNumber.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NYNumber.swift; sourceTree = "<group>"; };
+		90CE52422A36E5170033BD06 /* NYImage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NYImage.swift; sourceTree = "<group>"; };
+		90CE524B2A36E5460033BD06 /* LWPlayer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LWPlayer.swift; sourceTree = "<group>"; };
+		90CE524C2A36E5460033BD06 /* LWPlayerView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LWPlayerView.swift; sourceTree = "<group>"; };
+		90CE524D2A36E5460033BD06 /* LWPlayerManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LWPlayerManager.swift; sourceTree = "<group>"; };
+		90CE524E2A36E5460033BD06 /* LWPlayerControlView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LWPlayerControlView.swift; sourceTree = "<group>"; };
+		90CE524F2A36E5460033BD06 /* LWPlayerSlider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LWPlayerSlider.swift; sourceTree = "<group>"; };
+		90CE52512A36E5460033BD06 /* AVPlayer+LWPlayer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "AVPlayer+LWPlayer.swift"; sourceTree = "<group>"; };
+		90CE52522A36E5460033BD06 /* Timer+LWPlayer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Timer+LWPlayer.swift"; sourceTree = "<group>"; };
+		90CE52542A36E5460033BD06 /* LWPlayerUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LWPlayerUtils.swift; sourceTree = "<group>"; };
+		90CE52552A36E5460033BD06 /* LWPlayerDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LWPlayerDelegate.swift; sourceTree = "<group>"; };
+		90CE52562A36E5460033BD06 /* LWPlayerEnum.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LWPlayerEnum.swift; sourceTree = "<group>"; };
+		90CE52582A36E5460033BD06 /* LWPlayerFullScreenViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LWPlayerFullScreenViewController.swift; sourceTree = "<group>"; };
+		90CE525A2A36E5460033BD06 /* Refreshable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Refreshable.swift; sourceTree = "<group>"; };
+		90CE525B2A36E5460033BD06 /* NetAnimationLoadable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetAnimationLoadable.swift; sourceTree = "<group>"; };
+		90CE525C2A36E5460033BD06 /* NibLoadable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NibLoadable.swift; sourceTree = "<group>"; };
+		90CE525F2A36E5460033BD06 /* NYSwToaster.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NYSwToaster.swift; sourceTree = "<group>"; };
+		90CE52612A36E5460033BD06 /* BilibiliCollectionViewLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BilibiliCollectionViewLayout.swift; sourceTree = "<group>"; };
+		90CE52632A36E5460033BD06 /* GesConflictCollectionView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GesConflictCollectionView.swift; sourceTree = "<group>"; };
+		90CE52652A36E5460033BD06 /* SearchBarView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SearchBarView.swift; sourceTree = "<group>"; };
+		90CE52662A36E5460033BD06 /* SearchBarView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = SearchBarView.xib; sourceTree = "<group>"; };
+		90CE52682A36E5460033BD06 /* EmptyView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = EmptyView.xib; sourceTree = "<group>"; };
+		90CE52692A36E5460033BD06 /* EmptyView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EmptyView.swift; sourceTree = "<group>"; };
+		90CE526B2A36E5460033BD06 /* NetAnimationView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetAnimationView.swift; sourceTree = "<group>"; };
+		90CE526D2A36E5460033BD06 /* PopOverView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PopOverView.swift; sourceTree = "<group>"; };
+		90CE526E2A36E5460033BD06 /* PopOverViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PopOverViewCell.swift; sourceTree = "<group>"; };
+		90CE526F2A36E5460033BD06 /* PopOverViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = PopOverViewCell.xib; sourceTree = "<group>"; };
+		90CE52702A36E5460033BD06 /* PopOverViewSettings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PopOverViewSettings.swift; sourceTree = "<group>"; };
+		90CE52712A36E5460033BD06 /* Action.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Action.swift; sourceTree = "<group>"; };
+		90CE528E2A36E5990033BD06 /* Data+Cache.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Data+Cache.swift"; sourceTree = "<group>"; };
+		90CE528F2A36E5990033BD06 /* DefaultsKeys+Key.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "DefaultsKeys+Key.swift"; sourceTree = "<group>"; };
+		90CE52902A36E5990033BD06 /* UIColor+Hex.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIColor+Hex.swift"; sourceTree = "<group>"; };
+		90CE52912A36E5990033BD06 /* UIImageView+Kingfisher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIImageView+Kingfisher.swift"; sourceTree = "<group>"; };
+		90CE52922A36E5990033BD06 /* DispatchQueue+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "DispatchQueue+Extension.swift"; sourceTree = "<group>"; };
+		90CE52932A36E5990033BD06 /* UIButton+Kingfisher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIButton+Kingfisher.swift"; sourceTree = "<group>"; };
+		90CE52942A36E5990033BD06 /* String+BoundingRect.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+BoundingRect.swift"; sourceTree = "<group>"; };
+		90CE52952A36E5990033BD06 /* UIImage+Placeholder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIImage+Placeholder.swift"; sourceTree = "<group>"; };
+		90CE52962A36E5990033BD06 /* UIScrollView+Direction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIScrollView+Direction.swift"; sourceTree = "<group>"; };
+		90CE52972A36E5990033BD06 /* UIBarButtonItem+Init.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIBarButtonItem+Init.swift"; sourceTree = "<group>"; };
+		90CE52982A36E5990033BD06 /* UIBarButtonItem+FixSpace.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIBarButtonItem+FixSpace.swift"; sourceTree = "<group>"; };
+		90CE52992A36E5990033BD06 /* UIViewController+NetAnimation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIViewController+NetAnimation.swift"; sourceTree = "<group>"; };
+		90CE529A2A36E5990033BD06 /* Array+SectionModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Array+SectionModel.swift"; sourceTree = "<group>"; };
+		90CE529B2A36E5990033BD06 /* UIView+CornerRadius.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIView+CornerRadius.swift"; sourceTree = "<group>"; };
+		90CE529C2A36E5990033BD06 /* UIScrollView+ScrollToBottom.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIScrollView+ScrollToBottom.swift"; sourceTree = "<group>"; };
+		90CE529D2A36E5990033BD06 /* UIColor+Bilibili.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIColor+Bilibili.swift"; sourceTree = "<group>"; };
+		90CE529E2A36E5990033BD06 /* NSAttributedString+BoundingRect.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSAttributedString+BoundingRect.swift"; sourceTree = "<group>"; };
+		90CE529F2A36E5990033BD06 /* VTContentView+Gesture.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "VTContentView+Gesture.swift"; sourceTree = "<group>"; };
+		90CE52B32A36E5A40033BD06 /* Logger.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Logger.swift; sourceTree = "<group>"; };
+		90CE52B72A36E5B70033BD06 /* LoadingPlugin.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoadingPlugin.swift; sourceTree = "<group>"; };
+		90CE52B82A36E5B70033BD06 /* Networking.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Networking.swift; sourceTree = "<group>"; };
+		90CE52B92A36E5B70033BD06 /* NetEnvironment.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetEnvironment.swift; sourceTree = "<group>"; };
+		90CE52BA2A36E5B70033BD06 /* RequestError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RequestError.swift; sourceTree = "<group>"; };
+		90CE52C12A36E5EC0033BD06 /* Moya+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Moya+Rx.swift"; sourceTree = "<group>"; };
+		90CE52C22A36E5EC0033BD06 /* UIScrollView+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIScrollView+Rx.swift"; sourceTree = "<group>"; };
+		90CE52C32A36E5EC0033BD06 /* UILabel+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UILabel+Rx.swift"; sourceTree = "<group>"; };
+		90CE52C42A36E5EC0033BD06 /* NotificationCenter+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NotificationCenter+Rx.swift"; sourceTree = "<group>"; };
+		90CE52C52A36E5EC0033BD06 /* UIView+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIView+Rx.swift"; sourceTree = "<group>"; };
+		90CE52C62A36E5EC0033BD06 /* UICollectionViewFlexLayout+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UICollectionViewFlexLayout+Rx.swift"; sourceTree = "<group>"; };
+		90CE52C72A36E5EC0033BD06 /* UICollectionView+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UICollectionView+Rx.swift"; sourceTree = "<group>"; };
+		90CE52C82A36E5EC0033BD06 /* PullToRefresh+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "PullToRefresh+Rx.swift"; sourceTree = "<group>"; };
+		90CE52D22A36E6000033BD06 /* NYSwRouter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NYSwRouter.swift; sourceTree = "<group>"; };
+		90CE52D32A36E6000033BD06 /* Snap.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Snap.swift; sourceTree = "<group>"; };
+		90CE52D42A36E6000033BD06 /* URLNavigationMap.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = URLNavigationMap.swift; sourceTree = "<group>"; };
+		90CE52D52A36E6000033BD06 /* LocalManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocalManager.swift; sourceTree = "<group>"; };
+		90CE52DA2A36E6250033BD06 /* NYAppCongfigure.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NYAppCongfigure.swift; sourceTree = "<group>"; };
+		90CE52DB2A36E6250033BD06 /* CompositionRoot.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CompositionRoot.swift; sourceTree = "<group>"; };
+		90CE52DF2A36E73A0033BD06 /* SplashModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SplashModel.swift; sourceTree = "<group>"; };
+		90CE52E02A36E73A0033BD06 /* SplashViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SplashViewController.swift; sourceTree = "<group>"; };
+		90CE52E82A36E85C0033BD06 /* LoginViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginViewController.swift; sourceTree = "<group>"; };
+		90CE52EA2A36E8680033BD06 /* LoginViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = LoginViewController.xib; sourceTree = "<group>"; };
+		90CE52EC2A36EBE70033BD06 /* UserInfoModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserInfoModel.swift; sourceTree = "<group>"; };
+		90CE52EE2A36EC950033BD06 /* NYArticleViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NYArticleViewController.swift; sourceTree = "<group>"; };
+		90CE52F02A36ECC80033BD06 /* NYWebViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NYWebViewController.swift; sourceTree = "<group>"; };
+		90CE52F22A36EDCE0033BD06 /* NYArticleNavBar.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NYArticleNavBar.swift; sourceTree = "<group>"; };
+		90CE52F32A36EDCF0033BD06 /* NYArticleNavBar.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = NYArticleNavBar.xib; sourceTree = "<group>"; };
+		90CE52F62A36EF540033BD06 /* JSJP-Brigding-Header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "JSJP-Brigding-Header.h"; sourceTree = "<group>"; };
+		90CE52F72A36F0BD0033BD06 /* LoginAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginAPI.swift; sourceTree = "<group>"; };
+		90CE52F92A36F2570033BD06 /* LoginService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginService.swift; sourceTree = "<group>"; };
 		A8171123E8F401C7CF842A4C /* Pods-JiaPeiManage.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JiaPeiManage.debug.xcconfig"; path = "Target Support Files/Pods-JiaPeiManage/Pods-JiaPeiManage.debug.xcconfig"; sourceTree = "<group>"; };
 		D9F65764266FE5C95D1940F5 /* Pods-JiaPeiManage.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JiaPeiManage.release.xcconfig"; path = "Target Support Files/Pods-JiaPeiManage/Pods-JiaPeiManage.release.xcconfig"; sourceTree = "<group>"; };
 /* End PBXFileReference section */
@@ -72,17 +243,375 @@
 		90CE52172A36C0700033BD06 /* JiaPeiManage */ = {
 			isa = PBXGroup;
 			children = (
-				90CE52182A36C0710033BD06 /* AppDelegate.swift */,
+				90CE522C2A36E3C20033BD06 /* Sources */,
 				90CE521C2A36C0710033BD06 /* ViewController.swift */,
-				90CE521A2A36C0710033BD06 /* SceneDelegate.swift */,
 				90CE521E2A36C0710033BD06 /* Main.storyboard */,
 				90CE52212A36C0720033BD06 /* Assets.xcassets */,
 				90CE52232A36C0720033BD06 /* LaunchScreen.storyboard */,
 				90CE52262A36C0720033BD06 /* Info.plist */,
+				90CE52F62A36EF540033BD06 /* JSJP-Brigding-Header.h */,
 			);
 			path = JiaPeiManage;
 			sourceTree = "<group>";
 		};
+		90CE522C2A36E3C20033BD06 /* Sources */ = {
+			isa = PBXGroup;
+			children = (
+				90CE52C02A36E5EC0033BD06 /* Rx */,
+				90CE52BF2A36E5D80033BD06 /* Services */,
+				90CE523C2A36E4FD0033BD06 /* Models */,
+				90CE523B2A36E4EE0033BD06 /* Modulars */,
+				90CE522D2A36E3F10033BD06 /* Main */,
+				90CE52182A36C0710033BD06 /* AppDelegate.swift */,
+				90CE52DB2A36E6250033BD06 /* CompositionRoot.swift */,
+				90CE52DA2A36E6250033BD06 /* NYAppCongfigure.swift */,
+				90CE52D12A36E6000033BD06 /* Utils */,
+				90CE52B52A36E5B70033BD06 /* Networking */,
+				90CE52B22A36E5A40033BD06 /* Logging */,
+				90CE528D2A36E5990033BD06 /* Extensions */,
+				90CE52482A36E5460033BD06 /* Custom */,
+				90CE523D2A36E5170033BD06 /* Constants */,
+			);
+			path = Sources;
+			sourceTree = "<group>";
+		};
+		90CE522D2A36E3F10033BD06 /* Main */ = {
+			isa = PBXGroup;
+			children = (
+				90CE52F22A36EDCE0033BD06 /* NYArticleNavBar.swift */,
+				90CE52F32A36EDCF0033BD06 /* NYArticleNavBar.xib */,
+				90CE52F02A36ECC80033BD06 /* NYWebViewController.swift */,
+				90CE52EE2A36EC950033BD06 /* NYArticleViewController.swift */,
+				90CE52352A36E4710033BD06 /* MainNavigationController.swift */,
+				90CE52362A36E4720033BD06 /* MainTabBarController.swift */,
+				90CE52372A36E4720033BD06 /* MainTabBarViewReactor.swift */,
+				90CE522E2A36E4600033BD06 /* Base */,
+			);
+			path = Main;
+			sourceTree = "<group>";
+		};
+		90CE522E2A36E4600033BD06 /* Base */ = {
+			isa = PBXGroup;
+			children = (
+				90CE522F2A36E4600033BD06 /* BaseCollectionViewController.swift */,
+				90CE52302A36E4600033BD06 /* BaseViewController.swift */,
+				90CE52312A36E4600033BD06 /* BaseTableViewController.swift */,
+			);
+			path = Base;
+			sourceTree = "<group>";
+		};
+		90CE523B2A36E4EE0033BD06 /* Modulars */ = {
+			isa = PBXGroup;
+			children = (
+				90CE52E52A36E7D00033BD06 /* Mine */,
+				90CE52E42A36E7BD0033BD06 /* Home */,
+				90CE52E32A36E7B20033BD06 /* Login */,
+				90CE52DE2A36E73A0033BD06 /* Splash */,
+			);
+			path = Modulars;
+			sourceTree = "<group>";
+		};
+		90CE523C2A36E4FD0033BD06 /* Models */ = {
+			isa = PBXGroup;
+			children = (
+				90CE52EC2A36EBE70033BD06 /* UserInfoModel.swift */,
+			);
+			path = Models;
+			sourceTree = "<group>";
+		};
+		90CE523D2A36E5170033BD06 /* Constants */ = {
+			isa = PBXGroup;
+			children = (
+				90CE523E2A36E5170033BD06 /* NYFont.swift */,
+				90CE523F2A36E5170033BD06 /* NYMacros.swift */,
+				90CE52402A36E5170033BD06 /* NYFitReal.swift */,
+				90CE52412A36E5170033BD06 /* NYNumber.swift */,
+				90CE52422A36E5170033BD06 /* NYImage.swift */,
+			);
+			path = Constants;
+			sourceTree = "<group>";
+		};
+		90CE52482A36E5460033BD06 /* Custom */ = {
+			isa = PBXGroup;
+			children = (
+				90CE52492A36E5460033BD06 /* VideoPlayer */,
+				90CE52592A36E5460033BD06 /* Protocol */,
+				90CE525D2A36E5460033BD06 /* View */,
+			);
+			path = Custom;
+			sourceTree = "<group>";
+		};
+		90CE52492A36E5460033BD06 /* VideoPlayer */ = {
+			isa = PBXGroup;
+			children = (
+				90CE524A2A36E5460033BD06 /* Core */,
+				90CE52502A36E5460033BD06 /* Extension */,
+				90CE52532A36E5460033BD06 /* Utiles */,
+				90CE52572A36E5460033BD06 /* Component */,
+			);
+			path = VideoPlayer;
+			sourceTree = "<group>";
+		};
+		90CE524A2A36E5460033BD06 /* Core */ = {
+			isa = PBXGroup;
+			children = (
+				90CE524B2A36E5460033BD06 /* LWPlayer.swift */,
+				90CE524C2A36E5460033BD06 /* LWPlayerView.swift */,
+				90CE524D2A36E5460033BD06 /* LWPlayerManager.swift */,
+				90CE524E2A36E5460033BD06 /* LWPlayerControlView.swift */,
+				90CE524F2A36E5460033BD06 /* LWPlayerSlider.swift */,
+			);
+			path = Core;
+			sourceTree = "<group>";
+		};
+		90CE52502A36E5460033BD06 /* Extension */ = {
+			isa = PBXGroup;
+			children = (
+				90CE52512A36E5460033BD06 /* AVPlayer+LWPlayer.swift */,
+				90CE52522A36E5460033BD06 /* Timer+LWPlayer.swift */,
+			);
+			path = Extension;
+			sourceTree = "<group>";
+		};
+		90CE52532A36E5460033BD06 /* Utiles */ = {
+			isa = PBXGroup;
+			children = (
+				90CE52542A36E5460033BD06 /* LWPlayerUtils.swift */,
+				90CE52552A36E5460033BD06 /* LWPlayerDelegate.swift */,
+				90CE52562A36E5460033BD06 /* LWPlayerEnum.swift */,
+			);
+			path = Utiles;
+			sourceTree = "<group>";
+		};
+		90CE52572A36E5460033BD06 /* Component */ = {
+			isa = PBXGroup;
+			children = (
+				90CE52582A36E5460033BD06 /* LWPlayerFullScreenViewController.swift */,
+			);
+			path = Component;
+			sourceTree = "<group>";
+		};
+		90CE52592A36E5460033BD06 /* Protocol */ = {
+			isa = PBXGroup;
+			children = (
+				90CE525A2A36E5460033BD06 /* Refreshable.swift */,
+				90CE525B2A36E5460033BD06 /* NetAnimationLoadable.swift */,
+				90CE525C2A36E5460033BD06 /* NibLoadable.swift */,
+			);
+			path = Protocol;
+			sourceTree = "<group>";
+		};
+		90CE525D2A36E5460033BD06 /* View */ = {
+			isa = PBXGroup;
+			children = (
+				90CE525E2A36E5460033BD06 /* Toaster */,
+				90CE52602A36E5460033BD06 /* CollectionViewLayout */,
+				90CE52622A36E5460033BD06 /* GesConflictCollectionView */,
+				90CE52642A36E5460033BD06 /* SearchBarView */,
+				90CE52672A36E5460033BD06 /* EmptyView */,
+				90CE526A2A36E5460033BD06 /* ImageViewAnimation */,
+				90CE526C2A36E5460033BD06 /* PopOverView */,
+			);
+			path = View;
+			sourceTree = "<group>";
+		};
+		90CE525E2A36E5460033BD06 /* Toaster */ = {
+			isa = PBXGroup;
+			children = (
+				90CE525F2A36E5460033BD06 /* NYSwToaster.swift */,
+			);
+			path = Toaster;
+			sourceTree = "<group>";
+		};
+		90CE52602A36E5460033BD06 /* CollectionViewLayout */ = {
+			isa = PBXGroup;
+			children = (
+				90CE52612A36E5460033BD06 /* BilibiliCollectionViewLayout.swift */,
+			);
+			path = CollectionViewLayout;
+			sourceTree = "<group>";
+		};
+		90CE52622A36E5460033BD06 /* GesConflictCollectionView */ = {
+			isa = PBXGroup;
+			children = (
+				90CE52632A36E5460033BD06 /* GesConflictCollectionView.swift */,
+			);
+			path = GesConflictCollectionView;
+			sourceTree = "<group>";
+		};
+		90CE52642A36E5460033BD06 /* SearchBarView */ = {
+			isa = PBXGroup;
+			children = (
+				90CE52652A36E5460033BD06 /* SearchBarView.swift */,
+				90CE52662A36E5460033BD06 /* SearchBarView.xib */,
+			);
+			path = SearchBarView;
+			sourceTree = "<group>";
+		};
+		90CE52672A36E5460033BD06 /* EmptyView */ = {
+			isa = PBXGroup;
+			children = (
+				90CE52682A36E5460033BD06 /* EmptyView.xib */,
+				90CE52692A36E5460033BD06 /* EmptyView.swift */,
+			);
+			path = EmptyView;
+			sourceTree = "<group>";
+		};
+		90CE526A2A36E5460033BD06 /* ImageViewAnimation */ = {
+			isa = PBXGroup;
+			children = (
+				90CE526B2A36E5460033BD06 /* NetAnimationView.swift */,
+			);
+			path = ImageViewAnimation;
+			sourceTree = "<group>";
+		};
+		90CE526C2A36E5460033BD06 /* PopOverView */ = {
+			isa = PBXGroup;
+			children = (
+				90CE526D2A36E5460033BD06 /* PopOverView.swift */,
+				90CE526E2A36E5460033BD06 /* PopOverViewCell.swift */,
+				90CE526F2A36E5460033BD06 /* PopOverViewCell.xib */,
+				90CE52702A36E5460033BD06 /* PopOverViewSettings.swift */,
+				90CE52712A36E5460033BD06 /* Action.swift */,
+			);
+			path = PopOverView;
+			sourceTree = "<group>";
+		};
+		90CE528D2A36E5990033BD06 /* Extensions */ = {
+			isa = PBXGroup;
+			children = (
+				90CE528E2A36E5990033BD06 /* Data+Cache.swift */,
+				90CE528F2A36E5990033BD06 /* DefaultsKeys+Key.swift */,
+				90CE52902A36E5990033BD06 /* UIColor+Hex.swift */,
+				90CE52912A36E5990033BD06 /* UIImageView+Kingfisher.swift */,
+				90CE52922A36E5990033BD06 /* DispatchQueue+Extension.swift */,
+				90CE52932A36E5990033BD06 /* UIButton+Kingfisher.swift */,
+				90CE52942A36E5990033BD06 /* String+BoundingRect.swift */,
+				90CE52952A36E5990033BD06 /* UIImage+Placeholder.swift */,
+				90CE52962A36E5990033BD06 /* UIScrollView+Direction.swift */,
+				90CE52972A36E5990033BD06 /* UIBarButtonItem+Init.swift */,
+				90CE52982A36E5990033BD06 /* UIBarButtonItem+FixSpace.swift */,
+				90CE52992A36E5990033BD06 /* UIViewController+NetAnimation.swift */,
+				90CE529A2A36E5990033BD06 /* Array+SectionModel.swift */,
+				90CE529B2A36E5990033BD06 /* UIView+CornerRadius.swift */,
+				90CE529C2A36E5990033BD06 /* UIScrollView+ScrollToBottom.swift */,
+				90CE529D2A36E5990033BD06 /* UIColor+Bilibili.swift */,
+				90CE529E2A36E5990033BD06 /* NSAttributedString+BoundingRect.swift */,
+				90CE529F2A36E5990033BD06 /* VTContentView+Gesture.swift */,
+			);
+			path = Extensions;
+			sourceTree = "<group>";
+		};
+		90CE52B22A36E5A40033BD06 /* Logging */ = {
+			isa = PBXGroup;
+			children = (
+				90CE52B32A36E5A40033BD06 /* Logger.swift */,
+			);
+			path = Logging;
+			sourceTree = "<group>";
+		};
+		90CE52B52A36E5B70033BD06 /* Networking */ = {
+			isa = PBXGroup;
+			children = (
+				90CE52B62A36E5B70033BD06 /* Plugins */,
+				90CE52B82A36E5B70033BD06 /* Networking.swift */,
+				90CE52B92A36E5B70033BD06 /* NetEnvironment.swift */,
+				90CE52BA2A36E5B70033BD06 /* RequestError.swift */,
+			);
+			path = Networking;
+			sourceTree = "<group>";
+		};
+		90CE52B62A36E5B70033BD06 /* Plugins */ = {
+			isa = PBXGroup;
+			children = (
+				90CE52B72A36E5B70033BD06 /* LoadingPlugin.swift */,
+			);
+			path = Plugins;
+			sourceTree = "<group>";
+		};
+		90CE52BF2A36E5D80033BD06 /* Services */ = {
+			isa = PBXGroup;
+			children = (
+				90CE52F72A36F0BD0033BD06 /* LoginAPI.swift */,
+				90CE52F92A36F2570033BD06 /* LoginService.swift */,
+			);
+			path = Services;
+			sourceTree = "<group>";
+		};
+		90CE52C02A36E5EC0033BD06 /* Rx */ = {
+			isa = PBXGroup;
+			children = (
+				90CE52C12A36E5EC0033BD06 /* Moya+Rx.swift */,
+				90CE52C22A36E5EC0033BD06 /* UIScrollView+Rx.swift */,
+				90CE52C32A36E5EC0033BD06 /* UILabel+Rx.swift */,
+				90CE52C42A36E5EC0033BD06 /* NotificationCenter+Rx.swift */,
+				90CE52C52A36E5EC0033BD06 /* UIView+Rx.swift */,
+				90CE52C62A36E5EC0033BD06 /* UICollectionViewFlexLayout+Rx.swift */,
+				90CE52C72A36E5EC0033BD06 /* UICollectionView+Rx.swift */,
+				90CE52C82A36E5EC0033BD06 /* PullToRefresh+Rx.swift */,
+			);
+			path = Rx;
+			sourceTree = "<group>";
+		};
+		90CE52D12A36E6000033BD06 /* Utils */ = {
+			isa = PBXGroup;
+			children = (
+				90CE52D22A36E6000033BD06 /* NYSwRouter.swift */,
+				90CE52D32A36E6000033BD06 /* Snap.swift */,
+				90CE52D42A36E6000033BD06 /* URLNavigationMap.swift */,
+				90CE52D52A36E6000033BD06 /* LocalManager.swift */,
+			);
+			path = Utils;
+			sourceTree = "<group>";
+		};
+		90CE52DE2A36E73A0033BD06 /* Splash */ = {
+			isa = PBXGroup;
+			children = (
+				90CE52DF2A36E73A0033BD06 /* SplashModel.swift */,
+				90CE52E02A36E73A0033BD06 /* SplashViewController.swift */,
+			);
+			path = Splash;
+			sourceTree = "<group>";
+		};
+		90CE52E32A36E7B20033BD06 /* Login */ = {
+			isa = PBXGroup;
+			children = (
+				90CE52E72A36E81E0033BD06 /* Views */,
+				90CE52E62A36E8100033BD06 /* Controllers */,
+			);
+			path = Login;
+			sourceTree = "<group>";
+		};
+		90CE52E42A36E7BD0033BD06 /* Home */ = {
+			isa = PBXGroup;
+			children = (
+			);
+			path = Home;
+			sourceTree = "<group>";
+		};
+		90CE52E52A36E7D00033BD06 /* Mine */ = {
+			isa = PBXGroup;
+			children = (
+			);
+			path = Mine;
+			sourceTree = "<group>";
+		};
+		90CE52E62A36E8100033BD06 /* Controllers */ = {
+			isa = PBXGroup;
+			children = (
+				90CE52E82A36E85C0033BD06 /* LoginViewController.swift */,
+				90CE52EA2A36E8680033BD06 /* LoginViewController.xib */,
+			);
+			path = Controllers;
+			sourceTree = "<group>";
+		};
+		90CE52E72A36E81E0033BD06 /* Views */ = {
+			isa = PBXGroup;
+			children = (
+			);
+			path = Views;
+			sourceTree = "<group>";
+		};
 		A24D99DBE7A92F9485B15E3A /* Frameworks */ = {
 			isa = PBXGroup;
 			children = (
@@ -151,9 +680,14 @@
 			isa = PBXResourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				90CE52842A36E5460033BD06 /* SearchBarView.xib in Resources */,
+				90CE52F52A36EDCF0033BD06 /* NYArticleNavBar.xib in Resources */,
 				90CE52252A36C0720033BD06 /* LaunchScreen.storyboard in Resources */,
+				90CE528A2A36E5460033BD06 /* PopOverViewCell.xib in Resources */,
 				90CE52222A36C0720033BD06 /* Assets.xcassets in Resources */,
 				90CE52202A36C0710033BD06 /* Main.storyboard in Resources */,
+				90CE52EB2A36E8680033BD06 /* LoginViewController.xib in Resources */,
+				90CE52852A36E5460033BD06 /* EmptyView.xib in Resources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -206,9 +740,89 @@
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				90CE52A22A36E59A0033BD06 /* UIColor+Hex.swift in Sources */,
+				90CE52CA2A36E5EC0033BD06 /* UIScrollView+Rx.swift in Sources */,
+				90CE52F82A36F0BD0033BD06 /* LoginAPI.swift in Sources */,
+				90CE52D82A36E6000033BD06 /* URLNavigationMap.swift in Sources */,
+				90CE52752A36E5460033BD06 /* LWPlayerControlView.swift in Sources */,
+				90CE52A92A36E59A0033BD06 /* UIBarButtonItem+Init.swift in Sources */,
+				90CE528C2A36E5460033BD06 /* Action.swift in Sources */,
+				90CE52CE2A36E5EC0033BD06 /* UICollectionViewFlexLayout+Rx.swift in Sources */,
+				90CE52DD2A36E6250033BD06 /* CompositionRoot.swift in Sources */,
+				90CE52BE2A36E5B70033BD06 /* RequestError.swift in Sources */,
+				90CE52342A36E4600033BD06 /* BaseTableViewController.swift in Sources */,
+				90CE52892A36E5460033BD06 /* PopOverViewCell.swift in Sources */,
+				90CE52332A36E4600033BD06 /* BaseViewController.swift in Sources */,
+				90CE52A32A36E59A0033BD06 /* UIImageView+Kingfisher.swift in Sources */,
+				90CE527D2A36E5460033BD06 /* Refreshable.swift in Sources */,
+				90CE52472A36E5170033BD06 /* NYImage.swift in Sources */,
+				90CE52A42A36E59A0033BD06 /* DispatchQueue+Extension.swift in Sources */,
+				90CE52782A36E5460033BD06 /* Timer+LWPlayer.swift in Sources */,
+				90CE52A02A36E59A0033BD06 /* Data+Cache.swift in Sources */,
+				90CE52A12A36E59A0033BD06 /* DefaultsKeys+Key.swift in Sources */,
+				90CE52462A36E5170033BD06 /* NYNumber.swift in Sources */,
+				90CE527C2A36E5460033BD06 /* LWPlayerFullScreenViewController.swift in Sources */,
+				90CE52E92A36E85C0033BD06 /* LoginViewController.swift in Sources */,
+				90CE52392A36E4720033BD06 /* MainTabBarController.swift in Sources */,
+				90CE52A82A36E59A0033BD06 /* UIScrollView+Direction.swift in Sources */,
+				90CE52CD2A36E5EC0033BD06 /* UIView+Rx.swift in Sources */,
+				90CE52AA2A36E59A0033BD06 /* UIBarButtonItem+FixSpace.swift in Sources */,
+				90CE52B42A36E5A40033BD06 /* Logger.swift in Sources */,
+				90CE52EF2A36EC950033BD06 /* NYArticleViewController.swift in Sources */,
+				90CE52D72A36E6000033BD06 /* Snap.swift in Sources */,
+				90CE52882A36E5460033BD06 /* PopOverView.swift in Sources */,
+				90CE527F2A36E5460033BD06 /* NibLoadable.swift in Sources */,
+				90CE52FA2A36F2570033BD06 /* LoginService.swift in Sources */,
+				90CE52812A36E5460033BD06 /* BilibiliCollectionViewLayout.swift in Sources */,
+				90CE52AE2A36E59A0033BD06 /* UIScrollView+ScrollToBottom.swift in Sources */,
+				90CE52AF2A36E59A0033BD06 /* UIColor+Bilibili.swift in Sources */,
+				90CE52B12A36E59A0033BD06 /* VTContentView+Gesture.swift in Sources */,
+				90CE52722A36E5460033BD06 /* LWPlayer.swift in Sources */,
 				90CE521D2A36C0710033BD06 /* ViewController.swift in Sources */,
+				90CE52CB2A36E5EC0033BD06 /* UILabel+Rx.swift in Sources */,
+				90CE52E12A36E73A0033BD06 /* SplashModel.swift in Sources */,
+				90CE52862A36E5460033BD06 /* EmptyView.swift in Sources */,
+				90CE52452A36E5170033BD06 /* NYFitReal.swift in Sources */,
+				90CE52A52A36E59A0033BD06 /* UIButton+Kingfisher.swift in Sources */,
+				90CE52B02A36E59A0033BD06 /* NSAttributedString+BoundingRect.swift in Sources */,
+				90CE52C92A36E5EC0033BD06 /* Moya+Rx.swift in Sources */,
+				90CE52772A36E5460033BD06 /* AVPlayer+LWPlayer.swift in Sources */,
+				90CE52732A36E5460033BD06 /* LWPlayerView.swift in Sources */,
+				90CE52442A36E5170033BD06 /* NYMacros.swift in Sources */,
 				90CE52192A36C0710033BD06 /* AppDelegate.swift in Sources */,
-				90CE521B2A36C0710033BD06 /* SceneDelegate.swift in Sources */,
+				90CE52432A36E5170033BD06 /* NYFont.swift in Sources */,
+				90CE52ED2A36EBE80033BD06 /* UserInfoModel.swift in Sources */,
+				90CE528B2A36E5460033BD06 /* PopOverViewSettings.swift in Sources */,
+				90CE52D02A36E5EC0033BD06 /* PullToRefresh+Rx.swift in Sources */,
+				90CE52382A36E4720033BD06 /* MainNavigationController.swift in Sources */,
+				90CE52832A36E5460033BD06 /* SearchBarView.swift in Sources */,
+				90CE52AC2A36E59A0033BD06 /* Array+SectionModel.swift in Sources */,
+				90CE52762A36E5460033BD06 /* LWPlayerSlider.swift in Sources */,
+				90CE52F12A36ECC80033BD06 /* NYWebViewController.swift in Sources */,
+				90CE52AD2A36E59A0033BD06 /* UIView+CornerRadius.swift in Sources */,
+				90CE52792A36E5460033BD06 /* LWPlayerUtils.swift in Sources */,
+				90CE527B2A36E5460033BD06 /* LWPlayerEnum.swift in Sources */,
+				90CE52D92A36E6000033BD06 /* LocalManager.swift in Sources */,
+				90CE52822A36E5460033BD06 /* GesConflictCollectionView.swift in Sources */,
+				90CE523A2A36E4720033BD06 /* MainTabBarViewReactor.swift in Sources */,
+				90CE52BD2A36E5B70033BD06 /* NetEnvironment.swift in Sources */,
+				90CE52BB2A36E5B70033BD06 /* LoadingPlugin.swift in Sources */,
+				90CE52F42A36EDCF0033BD06 /* NYArticleNavBar.swift in Sources */,
+				90CE52A62A36E59A0033BD06 /* String+BoundingRect.swift in Sources */,
+				90CE527E2A36E5460033BD06 /* NetAnimationLoadable.swift in Sources */,
+				90CE52D62A36E6000033BD06 /* NYSwRouter.swift in Sources */,
+				90CE52AB2A36E59A0033BD06 /* UIViewController+NetAnimation.swift in Sources */,
+				90CE527A2A36E5460033BD06 /* LWPlayerDelegate.swift in Sources */,
+				90CE52CC2A36E5EC0033BD06 /* NotificationCenter+Rx.swift in Sources */,
+				90CE52A72A36E59A0033BD06 /* UIImage+Placeholder.swift in Sources */,
+				90CE52CF2A36E5EC0033BD06 /* UICollectionView+Rx.swift in Sources */,
+				90CE52742A36E5460033BD06 /* LWPlayerManager.swift in Sources */,
+				90CE52BC2A36E5B70033BD06 /* Networking.swift in Sources */,
+				90CE52E22A36E73A0033BD06 /* SplashViewController.swift in Sources */,
+				90CE52802A36E5460033BD06 /* NYSwToaster.swift in Sources */,
+				90CE52DC2A36E6250033BD06 /* NYAppCongfigure.swift in Sources */,
+				90CE52322A36E4600033BD06 /* BaseCollectionViewController.swift in Sources */,
+				90CE52872A36E5460033BD06 /* NetAnimationView.swift in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -352,7 +966,7 @@
 			isa = XCBuildConfiguration;
 			baseConfigurationReference = A8171123E8F401C7CF842A4C /* Pods-JiaPeiManage.debug.xcconfig */;
 			buildSettings = {
-				ASSETCATALOG_COMPILER_APPICON_NAME = App;
+				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon_NewRule;
 				ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
 				CODE_SIGN_STYLE = Automatic;
 				CURRENT_PROJECT_VERSION = 1;
@@ -373,6 +987,7 @@
 				PRODUCT_BUNDLE_IDENTIFIER = zhongZheng.com.jsmanage.JiaPeiManage;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SWIFT_EMIT_LOC_STRINGS = YES;
+				SWIFT_OBJC_BRIDGING_HEADER = "${SRCROOT}/JiaPeiManage/JSJP-Brigding-Header.h";
 				SWIFT_VERSION = 5.0;
 				TARGETED_DEVICE_FAMILY = "1,2";
 			};
@@ -382,7 +997,7 @@
 			isa = XCBuildConfiguration;
 			baseConfigurationReference = D9F65764266FE5C95D1940F5 /* Pods-JiaPeiManage.release.xcconfig */;
 			buildSettings = {
-				ASSETCATALOG_COMPILER_APPICON_NAME = App;
+				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon_NewRule;
 				ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
 				CODE_SIGN_STYLE = Automatic;
 				CURRENT_PROJECT_VERSION = 1;
@@ -403,6 +1018,7 @@
 				PRODUCT_BUNDLE_IDENTIFIER = zhongZheng.com.jsmanage.JiaPeiManage;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SWIFT_EMIT_LOC_STRINGS = YES;
+				SWIFT_OBJC_BRIDGING_HEADER = "${SRCROOT}/JiaPeiManage/JSJP-Brigding-Header.h";
 				SWIFT_VERSION = 5.0;
 				TARGETED_DEVICE_FAMILY = "1,2";
 			};

+ 15 - 0
JiaPeiManage/JSJP-Brigding-Header.h

@@ -0,0 +1,15 @@
+//
+//  JSJP-Brigding-Header.h
+//  JSJP_Student_sw
+//
+//  Created by Ning.ge on 2023/5/30.
+//
+
+#ifndef JSJP_Brigding_Header_h
+#define JSJP_Brigding_Header_h
+
+//第三方
+#import "VTMagic.h"
+#import <QMUIKit/QMUIKit.h>
+
+#endif /* JSJP_Brigding_Header_h */

+ 0 - 16
JiaPeiManage/SceneDelegate.swift

@@ -1,16 +0,0 @@
-//
-//  SceneDelegate.swift
-//  JiaPeiManage
-//
-//  Created by Ning.ge on 2023/6/12.
-//
-
-import UIKit
-
-class SceneDelegate: UIResponder, UIWindowSceneDelegate {
-
-    var window: UIWindow?
-
-
-}
-

+ 42 - 0
JiaPeiManage/Sources/AppDelegate.swift

@@ -0,0 +1,42 @@
+//
+//  AppDelegate.swift
+//  JiaPeiManage
+//
+//  Created by Ning.ge on 2023/6/12.
+//
+
+import UIKit
+
+@main
+class AppDelegate: UIResponder, UIApplicationDelegate {
+
+    // MARK: Properties
+    
+    var dependency: NYAppDependency!
+    
+    var window: UIWindow?
+
+    // MARK: UIApplicationDelegate
+
+    func application(_ application:UIApplication, didFinishLaunchingWithOptions launchOptions:[UIApplication.LaunchOptionsKey: Any]?) -> Bool {
+        
+        self.dependency = self.dependency ?? CompositionRoot.resolve()
+        self.window = self.dependency.window
+        self.dependency.configureSDKs()
+        self.dependency.configureAppearance()
+        self.dependency.configureUserAgent()
+        self.dependency.congigurePerformance()
+        
+        return true
+    }
+
+    func application(
+        _ app: UIApplication,
+        open url: URL,
+        options: [UIApplication.OpenURLOptionsKey: Any] = [:]
+    ) -> Bool {
+        return self.dependency.openURL(url, options)
+    }
+
+
+}

+ 141 - 0
JiaPeiManage/Sources/CompositionRoot.swift

@@ -0,0 +1,141 @@
+//
+//  CompositionRoot.swift
+//  JSJP_Student_sw
+//
+//  Created by ningye on 2023/5/22.
+//
+
+import UIKit
+
+import CGFloatLiteral
+import Kingfisher
+import RxGesture
+import RxOptional
+import RxViewController
+import SnapKit
+import SwiftyColor
+import SwiftyImage
+import SwiftyUserDefaults
+import Then
+import URLNavigator
+import NSObject_Rx
+import ManualLayout
+import GDPerformanceView_Swift
+import Toaster
+import Dollar
+import WebKit
+import SwiftDate
+
+struct NYAppDependency {
+    typealias OpenURLHandler = (_ url: URL, _ options: [UIApplication.OpenURLOptionsKey: Any]) -> Bool
+    
+    let window: UIWindow
+    let configureSDKs: () -> Void
+    let configureAppearance: () -> Void
+    let configureUserAgent: () -> Void
+    let congigurePerformance: () -> Void
+    let openURL: OpenURLHandler
+}
+
+let navigator = Navigator()
+
+final class CompositionRoot {
+    
+    /// Builds a dependency graph and returns an entry view controller.
+    static func resolve() -> NYAppDependency {
+        let window = UIWindow(frame: UIScreen.main.bounds)
+        window.backgroundColor = .white
+        window.makeKeyAndVisible()
+        
+        URLNavigationMap.initialize(navigator: navigator)
+
+//        let homeService = HomeService(networking: HomeNetworking())
+
+        var presentMainScreen: (() -> Void)!
+
+        presentMainScreen = {
+
+//            let mainTabBarController = MainTabBarController(reactor: MainTabBarViewReactor(),
+//                                                            homeParentViewController: HomeParentViewController(service: homeService),
+//                                                            timekeepViewController: TimeKeepParentViewController(),
+//                                                            mineListViewController: MineParentViewController())
+
+//            window.rootViewController = mainTabBarController
+            
+            //判断用户登录
+            let loginController = LoginViewController(nibName: "LoginViewController")
+            window.rootViewController = loginController
+        }
+
+        let splashViewController = SplashViewController(presentMainScreen: presentMainScreen)
+
+        window.rootViewController = splashViewController
+        
+        return NYAppDependency(
+            window: window,
+            configureSDKs: self.configureSDKs,
+            configureAppearance: self.configureAppearance,
+            configureUserAgent: self.configureUserAgent,
+            congigurePerformance: self.congigurePerformance,
+            openURL: self.openURLFactory(navigator: navigator)
+        )
+    }
+    static func configureSDKs() {
+        
+    }
+    
+    static func configureAppearance() {
+        //设置时区
+        let _ = Region(calendar: Calendars.gregorian, zone: Zones.asiaShanghai, locale: Locales.chinese)
+
+        //打开app次数
+        LocalManager.userInfo.openTimes += 1
+
+        //设置环境  --默认是线上环境
+        Defaults.currentEnvironment = .res
+
+        //ToastView
+        ToastView.appearance().font = NYFont.SysFont.sys_15
+//        ToastView.appearance().textColor = UIColor.db_white
+        
+    }
+    
+    static func configureUserAgent() {
+        
+//        let webView = WKWebView(frame: .zero)
+//        webView.evaluateJavaScript("navigator.userAgent") { (oldAgent, error) in
+//
+//            guard let oldAgent = oldAgent as? String else { return }
+//
+//            let newAgent = "\(oldAgent) BiliApp/StudioApp/6560"
+//            let newAgentDic = ["UserAgent":newAgent]
+//            UserDefaults.standard.register(defaults: newAgentDic)
+//            if #available(iOS 9.0, *) {
+//                //局部更新,即可以在其他用到的webView页面重新修改userAgent
+//                webView.customUserAgent = newAgent
+//            }
+//        }
+//        webView.load(URLRequest(url: URL(string:"http//www.baidu.com")!))
+    }
+    
+    static func congigurePerformance() {
+        
+//#if DEBUG
+//        PerformanceMonitor.sharedInstance.startMonitoring()
+//        PerformanceMonitor.sharedInstance.appVersionHidden = true
+//        PerformanceMonitor.sharedInstance.deviceVersionHidden = true
+//#endif
+    }
+    
+    static func openURLFactory(navigator: NavigatorType) -> NYAppDependency.OpenURLHandler {
+        return { url, options -> Bool in
+            if navigator.open(url) {
+                return true
+            }
+            if navigator.present(url, wrap: MainNavigationController.self) != nil {
+                return true
+            }
+            return false
+        }
+    }
+}

+ 48 - 0
JiaPeiManage/Sources/Constants/NYFitReal.swift

@@ -0,0 +1,48 @@
+//
+//  NYFitReal.swift
+//  JSJP_Student_sw
+//
+//  Created by Ning.ge on 2023/6/1.
+//
+
+import UIKit
+
+struct NYFitReal {
+    //以iphone6为基础缩放系数
+    static let screenHeight = UIScreen.main.bounds.height
+    static let screenWidth = UIScreen.main.bounds.width
+    
+    //竖屏
+    static func isPortrait() -> Bool {
+        let orientation = UIDevice.current.orientation
+        return orientation.isPortrait
+    }
+    //横屏
+    static func isLandscape() -> Bool {
+        let orientation = UIDevice.current.orientation
+        return orientation.isLandscape
+    }
+    //根据ip6的屏幕来拉伸宽
+    static func RealValue(x:CGFloat) -> CGFloat{
+        return ((x)*((isPortrait() ? screenWidth : screenHeight)/375.0))
+    }
+    //根据ip6的屏幕来拉伸宽Int
+    static func RealValueInt(x:CGFloat) -> Int{
+        return ((NSInteger)((x)*((isPortrait() ? screenWidth : screenHeight)/375.0)))
+    }
+    //iphone14高拉伸
+    static func RealHeightValue(height:CGFloat) -> CGFloat{
+        return ((height)*((isPortrait() ? kScreenHeight : screenWidth)/812.0))
+    }
+    
+    //iphone6缩放系数
+    static let Ip6ScaleWidth = screenWidth/375.0
+    static let Ip6ScaleHeight = screenHeight/667.0
+
+    
+    //以iphone14为基础缩放系数
+    static let Ip14ScaleWidth = screenWidth/375.0
+    static let Ip14ScaleHeight = screenHeight/812.0
+
+    
+}

+ 44 - 0
JiaPeiManage/Sources/Constants/NYFont.swift

@@ -0,0 +1,44 @@
+//
+//  NYFont.swift
+//  JSJP_Student_sw
+//
+//  Created by ningye on 2023/5/22.
+//
+
+import UIKit
+
+struct NYFont {
+    
+    struct SysFont {
+        
+        static let sys_10 = UIFont.systemFont(ofSize: 10)
+        static let sys_10_heave = UIFont.systemFont(ofSize: 10, weight: .heavy)
+        
+        static let sys_11 = UIFont.systemFont(ofSize: 11)
+        static let sys_11_heave = UIFont.systemFont(ofSize: 11, weight: .heavy)
+        
+        static let sys_12 = UIFont.systemFont(ofSize: 12)
+        static let sys_12_heave = UIFont.systemFont(ofSize: 12, weight: .heavy)
+        
+        static let sys_13 = UIFont.systemFont(ofSize: 13)
+        static let sys_13_heave = UIFont.systemFont(ofSize: 13, weight: .heavy)
+        
+        static let sys_14 = UIFont.systemFont(ofSize: 14)
+        static let sys_14_heave = UIFont.systemFont(ofSize: 14, weight: .heavy)
+        
+        static let sys_15 = UIFont.systemFont(ofSize: 15)
+        static let sys_15_heave = UIFont.systemFont(ofSize: 15, weight: .heavy)
+        
+        static let sys_16 = UIFont.systemFont(ofSize: 16)
+        static let sys_16_heave = UIFont.systemFont(ofSize: 16, weight: .heavy)
+        
+        static let sys_17 = UIFont.systemFont(ofSize: 17)
+        static let sys_17_heave = UIFont.systemFont(ofSize: 17, weight: .heavy)
+        
+        static let sys_18 = UIFont.systemFont(ofSize: 18)
+        static let sys_18_heave = UIFont.systemFont(ofSize: 18, weight: .heavy)
+        
+    }
+    
+}
+

+ 69 - 0
JiaPeiManage/Sources/Constants/NYImage.swift

@@ -0,0 +1,69 @@
+//
+//  NYImage.swift
+//  JSJP_Student_sw
+//
+//  Created by ningye on 2023/5/22.
+//
+
+import UIKit
+
+struct NYImage {
+    
+    struct Launch {
+        static let splash = UIImage(named: "launch_splash")
+        static let background = UIImage(named: "launch_background")
+    }
+    struct TabBar {
+        static let home = UIImage(named: "tabBar_exam")
+        static let home_s = UIImage(named: "tabBar_exam_s")
+        static let timeKeep =  UIImage(named: "tabBar_timer")
+        static let timeKeep_s = UIImage(named: "tabBar_timer_s")
+        static let mine = UIImage(named: "tabBar_mine")
+        static let mine_s = UIImage(named: "tabBar_mine_s")
+    }
+    struct Home {
+        static let shopping = UIImage(named: "home_shopping")
+        static let search_rec = UIImage(named: "home_search_rec")
+        static let search_live = UIImage(named: "home_search_live")
+        static let downArrow = UIImage(named: "home_downArrow")
+        static let rightArrow = UIImage(named: "home_right_arrow")
+        static let rank = UIImage(named: "home_rank")
+        static let tag = UIImage(named: "home_tag")
+        static let shadow = UIImage(named: "home_shadow")
+        static let dislike = UIImage(named: "home_dislike")
+        static let playTime = UIImage(named: "home_playTime")
+        static let play = UIImage(named: "home_player_play")
+        static let danmakus = UIImage(named: "home_danmakus")
+        static let favorite = UIImage(named: "home_favorite")
+        static let default_img = UIImage(named: "default_img")
+        static let refresh = UIImage(named: "home_refresh")
+        static let noData = UIImage(named: "home_noData")
+        static let video = UIImage(named: "home_video")
+        static let smallWindow = UIImage(named: "home_smallWindow")
+        static let noInterest = UIImage(named: "home_noInterest")
+        static let online = UIImage(named: "home_online")
+        static let back = UIImage(named: "home_back")
+        static let more = UIImage(named: "home_more")
+        static let countryRcmd = UIImage(named: "home_countryRcmd")
+        static let dramaRcmd = UIImage(named: "home_dramaRcmd")
+        static let editRcmd = UIImage(named: "home_editRcmd")
+        static let reviewRcmd = UIImage(named: "home_reviewRcmd")
+        static let new = UIImage(named: "home_new")
+        static let smallLogin = UIImage(named: "home_smallLogin")
+        static let bigLogin = UIImage(named: "home_bigLogin")
+        static let noDrama = UIImage(named: "home_noDrama")
+        static let avatar = UIImage(named: "home_avatar")
+        static let region_game = UIImage(named: "home_region_game")
+        static let region_mobile = UIImage(named: "home_region_mobile")
+        static let region_enterment = UIImage(named: "home_region_enterment")
+        static let region_common = UIImage(named: "home_region_common")
+    }
+    
+    struct Article {
+        static let more = UIImage(named: "article_more")
+    }
+    
+    
+    
+}
+

+ 46 - 0
JiaPeiManage/Sources/Constants/NYMacros.swift

@@ -0,0 +1,46 @@
+//
+//  NYMacros.swift
+//  JSJP_Student_sw
+//
+//  Created by Ning.ge on 2023/6/6.
+//
+
+struct NYMacros {
+    //应用名
+    static let appName = Bundle.main.object(forInfoDictionaryKey: "CFBundleDisplayName")
+    //应用版本
+    static let appVersion = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString")
+    //应用build
+    static let appBuild = Bundle.main.object(forInfoDictionaryKey: "CFBundleVersion")
+
+    ///------
+    /// iOS Version
+    ///------
+    static let iOSVersion = Float(UIDevice.current.systemVersion) ?? 0.0
+    static let iOS7OrLater = iOSVersion >= 7.0
+    static let iOS8OrLater = iOSVersion >= 8.0
+    static let iOS9OrLater = iOSVersion >= 9.0
+    static let iOS10OrLater = iOSVersion >= 10.0
+    static let iOS11OrLater = iOSVersion >= 11.0
+    static let iOS12OrLater = iOSVersion >= 12.0
+    static let iOS13OrLater = iOSVersion >= 13.0
+    static let iOS14OrLater = iOSVersion >= 14.0
+    static let iOS15OrLater = iOSVersion >= 15.0
+    
+//    //时间戳
+//    static func t() -> String {
+//        return String(format: "%.f", Date().timeIntervalSince1970)
+//    }
+//    static func ts() -> String {
+//        return String(format: "%.f", Date().timeIntervalSince1970*1000)
+//    }
+//    //生成设备ID
+//    static func deviceID() -> String {
+//        var deviceIDStr: String? = nil
+//        if deviceIDStr == nil {
+//            deviceIDStr = SAMKeychain.deviceId()
+//        }
+//        return deviceIDStr?.isEmpty == false ? deviceIDStr! : ""
+//    }
+
+}

+ 15 - 0
JiaPeiManage/Sources/Constants/NYNumber.swift

@@ -0,0 +1,15 @@
+//
+//  NYNumber.swift
+//  JSJP_Student_sw
+//
+//  Created by ningye on 2023/5/22.
+//
+
+import Foundation
+
+struct Number {
+    
+    
+    
+    
+}

+ 60 - 0
JiaPeiManage/Sources/Custom/Protocol/NetAnimationLoadable.swift

@@ -0,0 +1,60 @@
+//
+//  NetAnimationLoadable.swift
+//  SwiftBilibili
+//
+//  Created by 罗文 on 2021/1/15.
+//  Copyright © 2021年 罗文. All rights reserved.
+//
+
+import UIKit
+
+private var animationKey: Void?
+
+protocol NetAnimationLoadable {}
+
+extension NetAnimationLoadable where Self: UIViewController {
+    
+    private var animationType: AnimationType? {
+        get { return objc_getAssociatedObject(self, &animationKey) as? AnimationType }
+        set { objc_setAssociatedObject(self, &animationKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) }
+    }
+    
+    func showAnimationView(_ superView:UIView,animationType:AnimationType = .loading) {
+        
+        let insert = self
+        
+        if insert.animationType == animationType { return }
+        
+        if let existView = superView.subviews.filter({ $0.isKind(of: NetAnimationView.self) }).first as? NetAnimationView {
+
+            existView.removeFromSuperview()
+        }
+        
+        insert.animationType = animationType
+        
+        let animationView = NetAnimationView(animationType: animationType)
+        superView.addSubview(animationView)
+        superView.bringSubviewToFront(animationView)
+        
+        animationView.snp.makeConstraints { (make) in
+            make.width.equalTo(200)
+            make.height.equalTo(220)
+            make.centerX.equalToSuperview()
+            make.centerY.equalTo(superView).offset(-50*kScreenRatio)
+        }
+        
+        animationView.startAnimation(animationType:animationType)
+        
+    }
+    
+    func hideAnimationView(_ superView:UIView) {
+        
+        guard let animationView = superView.subviews.filter({ $0.isKind(of: NetAnimationView.self) }).first as? NetAnimationView else { return }
+        
+        let insert = self
+        
+        animationView.stopAnimation(animationType:insert.animationType!)
+        insert.animationType = nil
+        animationView.removeFromSuperview()
+    }
+}

+ 19 - 0
JiaPeiManage/Sources/Custom/Protocol/NibLoadable.swift

@@ -0,0 +1,19 @@
+//
+//  NibLoadable.swift
+//  SwiftBilibili
+//
+//  Created by 罗文 on 2021/1/15.
+//  Copyright © 2021年 罗文. All rights reserved.
+//
+
+import UIKit
+
+protocol NibLoadable{}
+
+extension NibLoadable where Self : UIView {
+    
+    static func loadFromNib(_ nibname : String? = nil) -> Self {
+        let loadName = nibname == nil ? "\(self)" : nibname!
+        return Bundle.main.loadNibNamed(loadName, owner: nil, options: nil)?.first as! Self
+    }
+}

+ 94 - 0
JiaPeiManage/Sources/Custom/Protocol/Refreshable.swift

@@ -0,0 +1,94 @@
+//
+//  Refreshable.swift
+//  SwiftBilibili
+//
+//  Created by 罗文 on 2021/2/24.
+//  Copyright © 2021年 罗文. All rights reserved.
+//
+
+import RxSwift
+import ESPullToRefresh
+import RxCocoa
+
+enum PullRefreshType {
+    case dance
+    case rabbit
+    case tv
+    case none
+}
+
+enum BilibiliRefreshStatus {
+    case none
+    case beginHeaderRefresh
+    case endHeaderRefresh
+    case beginFooterRefresh
+    case endFooterRefresh
+    case noMoreData
+}
+
+protocol Refreshable {}
+
+extension Refreshable where Self : UIViewController {
+    
+    @discardableResult
+    func setupRefreshHeader(_ scrollView: UIScrollView,_ headerRefrehType:PullRefreshType = .tv,_ refreshHandler: @escaping () -> Void) -> ESRefreshHeaderView? {
+        
+//        switch headerRefrehType {
+//        case .tv:
+//           return scrollView.es.addPullToRefresh(animator: TVHeaderAnimator(), handler: refreshHandler)
+//        case .dance:
+//           return scrollView.es.addPullToRefresh(animator: DanceHeaderAnimator(), handler: refreshHandler)
+//        case .rabbit:
+//           return scrollView.es.addPullToRefresh(animator: RabbitHeaderAnimator(), handler: refreshHandler)
+//        default: return nil
+//        }
+        return nil
+    }
+}
+
+extension Refreshable where Self : UIScrollView {
+    
+    @discardableResult
+    func setupRefreshHeader(_ headerRefrehType:PullRefreshType = .tv,_ refreshHandler: @escaping () -> Void) -> ESRefreshHeaderView? {
+//        switch headerRefrehType {
+//        case .tv:
+//            return self.es.addPullToRefresh(animator: TVHeaderAnimator(), handler: refreshHandler)
+//        case .dance:
+//            return self.es.addPullToRefresh(animator: DanceHeaderAnimator(), handler: refreshHandler)
+//        case .rabbit:
+//            return self.es.addPullToRefresh(animator: RabbitHeaderAnimator(), handler: refreshHandler)
+//        default: return nil
+//        }
+        return nil
+    }
+}
+
+protocol OutputRefreshProtocol {
+    
+    var refreshStatus: BehaviorRelay<BilibiliRefreshStatus>{get}
+}
+
+extension OutputRefreshProtocol {
+    
+    func autoSetRefreshStatus(header: ESRefreshHeaderView? = nil,footer: ESRefreshFooterView? = nil) -> Disposable {
+        
+        return refreshStatus.subscribe(onNext: { (status) in
+            switch status {
+            case .beginHeaderRefresh:
+                header?.startRefreshing()
+            case .endHeaderRefresh:
+                header?.stopRefreshing()
+            case .beginFooterRefresh:
+                footer?.startRefreshing()
+            case .endFooterRefresh:
+                footer?.stopRefreshing()
+            case .noMoreData:
+                footer?.resetNoMoreData()
+            default:break
+            }
+            
+        })
+    }
+}
+
+

+ 47 - 0
JiaPeiManage/Sources/Custom/VideoPlayer/Component/LWPlayerFullScreenViewController.swift

@@ -0,0 +1,47 @@
+//
+//  LWPlayerFullScreenViewController.swift
+//  SwiftBilibili
+//
+//  Created by 罗文 on 2021/3/29.
+//  Copyright © 2021年 罗文. All rights reserved.
+//
+
+import UIKit
+
+class LWPlayerFullScreenViewController: UIViewController {
+
+    weak var player: LWPlayer!
+    
+    var preferredlandscapeForPresentation = UIInterfaceOrientation.landscapeLeft
+    
+    var currentOrientation = UIDevice.current.orientation
+    
+    private var statusBarHiddenAnimated = true
+    
+    lazy var statusbarBackgroundView : UIView = {
+        
+        let view = UIView(frame: CGRect(x: 0, y: 0, width: self.view.bounds.width, height: UIApplication.shared.statusBarFrame.height))
+        view.backgroundColor = player.fullScreenStatusbarBackgroundColor
+        view.autoresizingMask = [ .flexibleWidth,.flexibleLeftMargin,.flexibleRightMargin,.flexibleBottomMargin]
+        return view
+    }()
+    
+    override func viewDidLoad() {
+        super.viewDidLoad()
+
+//        NotificationCenter.default.addObserver(self, selector: <#T##Selector#>, name: <#T##NSNotification.Name?#>, object: <#T##Any?#>)
+        
+        
+        
+    }
+    
+    @objc func playerControlsHiddenDidChange(_ notifiaction: Notification) {
+//        self.statusBarHiddenAnimated = notifiaction.userInfo?[Notification.Key.EZPlayerControlsHiddenDidChangeByAnimatedKey] as? Bool ?? true
+//        self.setNeedsStatusBarAppearanceUpdate()
+//        if #available(iOS 11.0, *) {
+//            self.setNeedsUpdateOfHomeIndicatorAutoHidden()
+//        }
+    }
+    
+    
+}

+ 577 - 0
JiaPeiManage/Sources/Custom/VideoPlayer/Core/LWPlayer.swift

@@ -0,0 +1,577 @@
+//
+//  LWPlayer.swift
+//  SwiftBilibili
+//
+//  Created by 罗文 on 2021/3/28.
+//  Copyright © 2021年 罗文. All rights reserved.
+//
+
+import UIKit
+import AVFoundation
+import MediaPlayer
+
+open class LWPlayer: NSObject {
+
+    public static var showLog = true
+    
+    open weak var delegate: LWPlayerDelegate?
+
+    open var videoGravity = LWPlayerVideoGravity.aspectFill{
+        didSet {
+            if let layer = self.playerView?.layer as? AVPlayerLayer{
+                layer.videoGravity = AVLayerVideoGravity(rawValue: videoGravity.rawValue)
+            }
+        }
+    }
+    
+    /// 视频类型
+    open var videoType = LWPlayerVideoType.av
+    
+    /// 设置url会自动播放
+    open var autoPlay = true
+    
+    /// 设备横屏时自动旋转(phone)
+    open var autoLandscapeFullScreenLandscape = UIDevice.current.userInterfaceIdiom == .phone
+    
+    /// 全屏的模式
+    open var fullScreenMode = LWPlayerFullScreenMode.landscape
+    
+    /// 全屏时status bar的样式
+    open var fullScreenPreferredStatusBarStyle = UIStatusBarStyle.lightContent
+    
+    /// 全屏时status bar的背景色
+    open var fullScreenStatusbarBackgroundColor = UIColor.black.withAlphaComponent(0.3)
+    
+    /// 上下滑动屏幕的控制类型
+    open var slideTrigger = (left:LWPlayerSlideTrigger.volume,right:LWPlayerSlideTrigger.brightness)
+    
+    /// 左右滑动屏幕改变视频进度
+    open var canSlideProgress = true
+    
+    /// 嵌入模式的控制皮肤
+    open var controlViewForEmbedded : UIView?
+    
+    /// 浮动模式的控制皮肤
+    open var controlViewForFloat : UIView?
+    
+    /// 全屏模式的控制皮肤
+    open var controlViewForFullscreen : UIView?
+    
+    /// 嵌入模式的容器
+    open weak var embeddedContentView: UIView?
+    
+    /// 嵌入模式的显示隐藏
+    open  private(set)  var controlsHidden = false
+    
+    /// 过多久自动消失控件,设置为<=0不消失
+    open var autohiddenTimeInterval: TimeInterval = 5
+    
+    /// 返回按钮block
+    open var backButtonBlock:(( _ fromDisplayMode: LWPlayerDisplayMode) -> Void)?
+    
+    open  var controlViewForIntercept : UIView? {
+        didSet{
+            self.updateCustomView()
+        }
+    }
+    
+    private var playerView: LWPlayerView?
+    open var view: UIView{
+        if self.playerView == nil {
+            self.playerView = LWPlayerView(controlView: self.controlView)
+        }
+        return self.playerView!
+    }
+    private var timeObserver: Any?
+    private var timer       : Timer?
+    open private(set) var isM3U8 = false
+    
+    /// 视频截图
+    open private(set) var imageGenerator: AVAssetImageGenerator?
+    
+    /// 视频截图m3u8
+    open private(set) var videoOutput: AVPlayerItemVideoOutput?
+    
+    open private(set) var contentURL :URL?{
+        didSet{
+            guard let url = contentURL else {
+                return
+            }
+            self.isM3U8 = url.absoluteString.hasSuffix(".m3u8")
+        }
+    }
+
+    open var isLive: Bool? {
+        if let duration = self.duration {
+            return duration.isNaN
+        }
+        return nil
+    }
+    
+    //放置于playerView之上
+    open var controlView : UIView?{
+        if let view = self.controlViewForIntercept{
+            return view
+        }
+        switch self.displayMode {
+        case .embedded:
+            return self.controlViewForEmbedded
+        case .fullscreen:
+            return self.controlViewForFullscreen
+        case .float:
+            return self.controlViewForFloat
+        case .none:
+            return self.controlViewForEmbedded
+        }
+    }
+    
+    open private(set) var player: AVPlayer? {
+        willSet{
+            removePlayerObserver()
+        }
+        didSet{
+            addPlayerObserver()
+        }
+    }
+
+    open private(set) var playerAsset: AVAsset?{
+        didSet{
+            if oldValue != playerAsset{
+                if let playerAsset = playerAsset {
+                    self.imageGenerator = AVAssetImageGenerator(asset: playerAsset)
+                }else{
+                    self.imageGenerator = nil
+                }
+            }
+        }
+    }
+    
+    open private(set) var playerItem: AVPlayerItem?{
+        willSet{
+            if playerItem != newValue{
+                 removePlayerItemObserver()
+                 removePlayerNotifications()
+            }
+        }
+        didSet {
+            if playerItem != oldValue{
+                 addPlayerItemObserver()
+                 addPlayerNotifications()
+            }
+        }
+    }
+    
+    open fileprivate(set) var state = LWPlayerState.unknown {
+        
+        didSet{
+            if oldValue != state {
+                playStateDidChange()
+            }
+        }
+    }
+    
+    open private(set)  var displayMode = LWPlayerDisplayMode.none{
+        didSet{
+            if oldValue != displayMode{
+                (self.controlView as? LWPlayerDelegate)?.player(self, playerDisplayModeDidChange: displayMode)
+                self.delegate?.player(self, playerDisplayModeDidChange: displayMode)
+            }
+        }
+    }
+    
+    open private(set)  var lastDisplayMode = LWPlayerDisplayMode.none
+
+    /// 视频是否正在播放
+    open var isPlaying:Bool{
+        guard let player = self.player else {
+            return false
+        }
+        return player.rate > Float(0) && player.error == nil
+    }
+    
+    /// 视频长度
+    open var duration: TimeInterval? {
+        if let  duration = self.player?.duration  {
+            return duration
+        }
+        return nil
+    }
+    
+    /// 视频进度
+    open var currentTime: TimeInterval? {
+        if let  currentTime = self.player?.currentTime {
+            return currentTime
+        }
+        return nil
+    }
+    
+    /// 视频播放速率
+    open var rate: Float{
+        get {
+            if let player = self.player {
+                return player.rate
+            }
+            return .nan
+        }
+        set {
+            if let player = self.player {
+                player.rate = newValue
+            }
+        }
+    }
+    
+    /// 系统音量
+    open var systemVolume: Float{
+        get {
+            return LWPlayerUtils.systemVolumeSlider.value
+        }
+        set {
+            LWPlayerUtils.systemVolumeSlider.value = newValue
+        }
+    }
+    
+    //life cycle
+    deinit {
+        NotificationCenter.default.removeObserver(self)
+        self.timer?.invalidate()
+        self.timer = nil
+        self.releasePlayerResource()
+    }
+    
+    public override init() {
+        super.init()
+        self.commonInit()
+    }
+    
+    public init(controlView: UIView?) {
+        super.init()
+        if controlView == nil{
+            self.controlViewForEmbedded = UIView()
+        }else{
+            self.controlViewForEmbedded = controlView
+        }
+        self.commonInit()
+    }
+    
+    //MARK: - Public
+    open func playWithURL(_ url: URL?,embeddedContentView contentView: UIView? = nil) {
+        self.contentURL = url
+        self.prepareToPlay()
+        if let contentView = contentView {
+            self.embeddedContentView = contentView
+            self.embeddedContentView!.addSubview(self.view)
+            self.view.frame = self.embeddedContentView!.bounds
+            self.displayMode = .embedded
+        }
+    }
+    
+    open func play(){
+        self.state = .playing
+        self.player?.play()
+    }
+    
+    open func pause(){
+        self.state = .pause
+        self.player?.pause()
+    }
+    
+    open func stop() {
+        //let lastState = self.state
+        self.state = .stopped
+        self.player?.pause()
+        self.releasePlayerResource()
+    }
+    
+    open func seek(to time: TimeInterval, completionHandler: ((Bool) -> Swift.Void )? = nil) {
+        guard let player = self.player else { return }
+        let lastState = self.state
+        if let currentTime = self.currentTime {
+            if currentTime > time {
+                self.state = .seekingBackward
+            }else if currentTime < time {
+                self.state = .seekingForward
+            }
+        }
+        player.seek(to: CMTimeMakeWithSeconds(time, preferredTimescale:CMTimeScale(NSEC_PER_SEC)), toleranceBefore:.zero, toleranceAfter:.zero, completionHandler: {  [weak self]  (finished) in
+            guard let `self` = self else { return }
+            switch self.state {
+            case .seekingBackward,.seekingForward:
+                self.state = lastState
+            default: break
+            }
+            completionHandler?(finished)
+        })
+    }
+    
+    open func updateCustomView(toDisplayMode: LWPlayerDisplayMode? = nil) {
+        var nextDisplayMode = self.displayMode
+        defer { self.displayMode = nextDisplayMode }
+        if toDisplayMode != nil{
+            nextDisplayMode = toDisplayMode!
+        }
+        if let view = self.controlViewForIntercept{
+            self.playerView?.controlView = view
+            self.displayMode = nextDisplayMode
+        }else{
+            switch nextDisplayMode {
+            case .embedded:
+                //playerView加问号,其实不关心playerView存不存在,存在就更新
+                if self.playerView?.controlView == nil || self.playerView?.controlView != self.controlViewForEmbedded{
+                    if self.controlViewForEmbedded == nil {
+//                        self.controlViewForEmbedded = self.controlViewForFullscreen ?? Bundle(for: EZPlayerControlView.self).loadNibNamed(String(describing: EZPlayerControlView.self), owner: self, options: nil)?.last as? EZPlayerControlView
+                    }
+                }
+                self.playerView?.controlView = self.controlViewForEmbedded
+                
+            case .fullscreen:
+                if self.playerView?.controlView == nil || self.playerView?.controlView != self.controlViewForFullscreen{
+                    if self.controlViewForFullscreen == nil {
+//                        self.controlViewForFullscreen = self.controlViewForEmbedded ?? Bundle(for: EZPlayerControlView.self).loadNibNamed(String(describing: EZPlayerControlView.self), owner: self, options: nil)?.last as? EZPlayerControlView
+                    }
+                }
+                self.playerView?.controlView = self.controlViewForFullscreen
+                
+            case .float:
+                if self.playerView?.controlView == nil || self.playerView?.controlView != self.controlViewForFloat{
+                    if self.controlViewForFloat == nil {
+//                        self.controlViewForFloat = Bundle(for: EZPlayerFloatView.self).loadNibNamed(String(describing: EZPlayerFloatView.self), owner: self, options: nil)?.last as? UIView
+                    }
+                }
+                self.playerView?.controlView = self.controlViewForFloat
+                
+                break
+            case .none:
+                //初始化的时候
+                if self.controlView == nil {
+                    self.controlViewForEmbedded = LWPlayerControlView()
+                }
+            }
+        }
+       
+    }
+    //MARK: - Private
+    private func commonInit() {
+        
+        updateCustomView()
+        
+        self.timer?.invalidate()
+        self.timer = nil
+        
+        self.timer = Timer.timerWithTimeInterval(0.5, block: {[weak self] in
+            
+            guard let `self` = self,
+                  let _ = self.player,
+                  let playerItem = self.playerItem
+            else { return }
+            
+            if playerItem.isPlaybackLikelyToKeepUp && self.state == .playing {
+                self.state = .buffering
+            }
+            
+            if playerItem.isPlaybackLikelyToKeepUp && (self.state == .buffering || self.state == .readyToPlay) {
+                self.state = .playing
+            }
+        }, repeats: true)
+        
+        RunLoop.current.add(self.timer!, forMode: .common)
+    }
+    
+    private func prepareToPlay() {
+        guard let url = self.contentURL else {
+            self.state = .error(.invalidContentURL)
+            return
+        }
+        self.releasePlayerResource()
+        self.playerAsset = AVAsset(url: url)
+        let keys = ["tracks","duration","commonMetadata","availableMediaCharacteristicsWithMediaSelectionOptions"]
+        self.playerItem = AVPlayerItem(asset: self.playerAsset!, automaticallyLoadedAssetKeys: keys)
+        self.player = AVPlayer(playerItem: playerItem!)
+        if self.playerView == nil {
+            self.playerView = LWPlayerView(controlView:self.controlView )
+        }
+        (self.playerView?.layer as! AVPlayerLayer).videoGravity = AVLayerVideoGravity(rawValue: self.videoGravity.rawValue)
+        self.playerView?.config(player: self)
+        (self.controlView as? LWPlayerDelegate)?.player(self, showLoading: true)
+        self.delegate?.player(self, showLoading: true)
+    }
+    
+    private func resetPlayerResource() {
+        self.contentURL = nil
+        if let videoOutput = self.videoOutput {
+            self.playerItem?.remove(videoOutput)
+            self.videoOutput = nil
+        }
+        self.playerAsset = nil
+        self.playerItem = nil
+        self.player?.replaceCurrentItem(with: nil)
+        self.playerView?.layer.removeAllAnimations()
+        (self.controlView as? LWPlayerDelegate)?.player(self, loadedTimeDidChange: 0, totalDuration: 0)
+        self.delegate?.player(self, loadedTimeDidChange: 0, totalDuration: 0)
+        
+        (self.controlView as? LWPlayerDelegate)?.player(self, playedTimeDidChange:0, totalDuration: 0)
+        self.delegate?.player(self, playedTimeDidChange: 0, totalDuration: 0)
+        
+    }
+    
+    private func releasePlayerResource() {
+        if let videoOutput = self.videoOutput {
+            self.playerItem?.remove(videoOutput)
+            self.videoOutput = nil
+        }
+        self.playerAsset = nil
+        self.playerItem = nil
+        self.player?.replaceCurrentItem(with: nil)
+        self.playerView?.layer.removeAllAnimations()
+        self.playerView?.removeFromSuperview()
+        self.playerView = nil
+        
+        if  let timeObserver = self.timeObserver{
+            self.player?.removeTimeObserver(timeObserver)
+            self.timeObserver = nil
+        }
+        
+    }
+}
+
+
+// MARK: -  Notifation Selector & KVO
+extension LWPlayer {
+    
+    private func removePlayerObserver() {
+        
+        if let timeObserver = timeObserver {
+            player?.removeTimeObserver(timeObserver)
+        }
+    }
+    
+    private func removePlayerItemObserver() {
+
+        playerItem?.removeObserver(self, forKeyPath: #keyPath(AVPlayerItem.status))
+        playerItem?.removeObserver(self, forKeyPath: #keyPath(AVPlayerItem.loadedTimeRanges))
+        playerItem?.removeObserver(self, forKeyPath: #keyPath(AVPlayerItem.isPlaybackBufferEmpty))
+        playerItem?.removeObserver(self, forKeyPath: #keyPath(AVPlayerItem.isPlaybackLikelyToKeepUp))
+    }
+    
+    private func removePlayerNotifications() {
+        NotificationCenter.default.removeObserver(self, name: .AVPlayerItemDidPlayToEndTime, object: nil)
+        NotificationCenter.default.removeObserver(self, name: UIApplication.willEnterForegroundNotification, object: nil)
+        NotificationCenter.default.removeObserver(self, name: UIApplication.didEnterBackgroundNotification, object: nil)
+    }
+    
+    private func addPlayerObserver() {
+        
+        timeObserver = player?.addPeriodicTimeObserver(forInterval: CMTimeMakeWithSeconds(1, preferredTimescale: CMTimeScale(NSEC_PER_SEC)), queue: DispatchQueue.main, using: {[weak self] (time) in
+            
+            guard let `self` = self,
+                  let currentTime = self.currentTime,
+                  let duration = self.duration
+            else { return }
+            
+            (self.controlView as? LWPlayerDelegate)?.player(self, playedTimeDidChange: currentTime, totalDuration: duration)
+            self.delegate?.player(self, playedTimeDidChange: currentTime, totalDuration: duration)
+            
+        })
+    }
+    
+    private func addPlayerItemObserver() {
+        
+        playerItem?.addObserver(self, forKeyPath: #keyPath(AVPlayerItem.status), options: NSKeyValueObservingOptions.new, context: nil)
+        playerItem?.addObserver(self, forKeyPath: #keyPath(AVPlayerItem.loadedTimeRanges), options: NSKeyValueObservingOptions.new, context: nil)
+        // 缓冲区空了,需要等待数据
+        playerItem?.addObserver(self, forKeyPath: #keyPath(AVPlayerItem.isPlaybackBufferEmpty), options: NSKeyValueObservingOptions.new, context: nil)
+        // 缓冲区有足够数据可以播放了
+        playerItem?.addObserver(self, forKeyPath: #keyPath(AVPlayerItem.isPlaybackLikelyToKeepUp), options: NSKeyValueObservingOptions.new, context: nil)
+    }
+    
+    private func addPlayerNotifications() {
+        NotificationCenter.default.addObserver(self, selector: #selector(playerItemDidPlayToEnd), name: .AVPlayerItemDidPlayToEndTime, object: nil)
+        NotificationCenter.default.addObserver(self, selector: #selector(applicationWillEnterForeground), name: UIApplication.willEnterForegroundNotification, object: nil)
+        NotificationCenter.default.addObserver(self, selector: #selector(applicationDidEnterBackground), name: UIApplication.didEnterBackgroundNotification, object: nil)
+    }
+    
+    @objc private func playerItemDidPlayToEnd(_ notification: Notification) {
+
+         self.state = .stopped
+        
+        
+    }
+    
+    @objc private func applicationWillEnterForeground(_ notification: Notification) {
+        
+
+    }
+    
+    @objc private func applicationDidEnterBackground(_ notification: Notification) {
+        
+    }
+    
+    open override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
+        
+        if let item = object as? AVPlayerItem, let keyPath = keyPath {
+            
+            if item == self.playerItem {
+                switch keyPath {
+                case #keyPath(AVPlayerItem.status):
+                    print("AVPlayerItem's status is changed: \(item.status.rawValue)")
+                    if item.status == .readyToPlay {
+                        let lastState = self.state
+                        if self.state != .playing{
+                            self.state = .readyToPlay
+                        }
+                        //自动播放
+                        if self.autoPlay && lastState == .unknown{
+                            self.play()
+                        }
+                    } else if item.status == .failed {
+                        self.state = .error(.playerFail)
+                    }
+                    
+                case #keyPath(AVPlayerItem.loadedTimeRanges):
+                    print("AVPlayerItem's loadedTimeRanges is changed")
+                    
+                    let loadedTimeRanges = item.loadedTimeRanges
+                    
+                    if let bufferTimeRange = loadedTimeRanges.first?.timeRangeValue {
+                        
+                        let star = bufferTimeRange.start.seconds         // The start time of the time range.
+                        let duration = bufferTimeRange.duration.seconds  // The duration of the time range.
+                        let bufferTime = star + duration
+                        
+                        (self.controlView as? LWPlayerDelegate)?.player(self, loadedTimeDidChange: bufferTime, totalDuration: self.duration ?? 0)
+                        self.delegate?.player(self, loadedTimeDidChange: bufferTime, totalDuration: self.duration ?? 0)
+                    }
+                    
+                case #keyPath(AVPlayerItem.isPlaybackBufferEmpty):
+                    print("AVPlayerItem's playbackBufferEmpty is changed")
+                case #keyPath(AVPlayerItem.isPlaybackLikelyToKeepUp):
+                    print("AVPlayerItem's playbackLikelyToKeepUp is changed")
+                default:
+                    break
+                }
+            }
+            
+        }
+    }
+}
+
+// MARK: -  State change
+extension LWPlayer {
+    
+    private func playStateDidChange() {
+        
+        (self.controlView as? LWPlayerDelegate)?.player(self, playerStateDidChange: state)
+        self.delegate?.player(self, playerStateDidChange: state)
+        switch state {
+        case .buffering:
+            (self.controlView as? LWPlayerDelegate)?.player(self, showLoading: true)
+            self.delegate?.player(self, showLoading: true)
+        case .error(_):
+            (self.controlView as? LWPlayerDelegate)?.player(self, showLoading: false)
+            self.delegate?.player(self, showLoading: false)
+        default:
+            (self.controlView as? LWPlayerDelegate)?.player(self, showLoading: false)
+            self.delegate?.player(self, showLoading: false)
+        }
+    }
+    
+}

+ 163 - 0
JiaPeiManage/Sources/Custom/VideoPlayer/Core/LWPlayerControlView.swift

@@ -0,0 +1,163 @@
+//
+//  LWPlayerControlView.swift
+//  SwiftBilibili
+//
+//  Created by 罗文 on 2021/3/30.
+//  Copyright © 2021年 罗文. All rights reserved.
+//
+
+import UIKit
+
+class LWPlayerControlView: UIView {
+
+    lazy var topView: UIImageView = {
+        let topView = UIImageView()
+        topView.backgroundColor = UIColor.black.withAlphaComponent(0.5)
+        return topView
+    }()
+    
+    lazy var bottomView: UIImageView = {
+        let bottomView = UIImageView()
+        bottomView.backgroundColor = UIColor.black.withAlphaComponent(0.5)
+        return bottomView
+    }()
+    
+    lazy var backButton: UIButton = {
+        let backButton = UIButton()
+        return backButton
+    }()
+    
+    lazy var titleLabel: UILabel = {
+        let titleLabel = UILabel()
+        titleLabel.textColor = UIColor.white
+        return titleLabel
+    }()
+    
+    lazy var pauseButton: UIButton = {
+        let pauseButton = UIButton()
+        return pauseButton
+    }()
+    
+    lazy var currentTimeLabel: UILabel = {
+        let currentTimeLabel = UILabel()
+        currentTimeLabel.textColor = UIColor.white
+        currentTimeLabel.font = UIFont.systemFont(ofSize: 13)
+        return currentTimeLabel
+    }()
+    
+    lazy var totalTimeLabel: UILabel = {
+        let totalTimeLabel = UILabel()
+        totalTimeLabel.textColor = UIColor.white
+        totalTimeLabel.font = UIFont.systemFont(ofSize: 13)
+        return totalTimeLabel
+    }()
+    
+    lazy var timeSlider: LWPlayerSlider = {
+        let timeSlider = LWPlayerSlider()
+        return timeSlider
+    }()
+    
+    lazy var fullScreenButton: UIButton = {
+        let fullScreenButton = UIButton()
+        return fullScreenButton
+    }()
+    
+    weak var player: LWPlayer?
+    
+    override init(frame: CGRect) {
+        super.init(frame: frame)
+        addSubview(topView)
+        addSubview(bottomView)
+        topView.addSubview(backButton)
+        topView.addSubview(titleLabel)
+        bottomView.addSubview(pauseButton)
+        bottomView.addSubview(currentTimeLabel)
+        bottomView.addSubview(timeSlider)
+        bottomView.addSubview(totalTimeLabel)
+        bottomView.addSubview(fullScreenButton)
+    }
+    
+    required init?(coder aDecoder: NSCoder) {
+        fatalError("init(coder:) has not been implemented")
+    }
+    
+    override func layoutSubviews() {
+        super.layoutSubviews()
+        
+        topView.snp.makeConstraints { (make) in
+            make.left.right.top.equalToSuperview()
+            make.height.equalTo(50)
+        }
+        
+        bottomView.snp.makeConstraints { (make) in
+            make.left.right.bottom.equalToSuperview()
+            make.height.equalTo(50)
+        }
+        
+        backButton.snp.makeConstraints { (make) in
+            make.left.equalTo(10).priority(750)
+            make.centerY.equalToSuperview()
+        }
+        
+        titleLabel.snp.makeConstraints { (make) in
+            make.left.equalTo(backButton.snp.right).offset(10).priority(1000)
+            make.centerY.equalToSuperview()
+        }
+        
+        pauseButton.snp.makeConstraints { (make) in
+            make.left.equalTo(backButton)
+            make.centerY.equalToSuperview()
+        }
+        
+        currentTimeLabel.snp.makeConstraints { (make) in
+            make.left.equalTo(pauseButton.snp.right).offset(10)
+            make.centerY.equalToSuperview()
+        }
+        
+        fullScreenButton.snp.makeConstraints { (make) in
+            make.right.equalTo(-10)
+            make.centerY.equalToSuperview()
+        }
+        
+        totalTimeLabel.snp.makeConstraints { (make) in
+            make.right.equalTo(fullScreenButton.snp.left).offset(-10)
+            make.centerY.equalToSuperview()
+        }
+        
+        timeSlider.snp.makeConstraints { (make) in
+            make.left.equalTo(currentTimeLabel.snp.right).offset(5)
+            make.right.equalTo(totalTimeLabel.snp.left).offset(-5)
+            make.centerY.equalToSuperview()
+        }
+    }
+}
+
+extension LWPlayerControlView: LWPlayerDelegate {
+    
+    func player(_ player: LWPlayer, playerStateDidChange state: LWPlayerState) {
+        
+    }
+    
+    func player(_ player: LWPlayer, playerDisplayModeDidChange displayMode: LWPlayerDisplayMode) {
+        
+    }
+    
+    func player(_ player: LWPlayer, loadedTimeDidChange bufferDuration: TimeInterval, totalDuration: TimeInterval) {
+        
+        timeSlider.setProgress(Float(bufferDuration/totalDuration), animated: true)
+    }
+    
+    func player(_ player: LWPlayer, playedTimeDidChange currentTime: TimeInterval, totalDuration: TimeInterval) {
+        timeSlider.maximumValue = Float(totalDuration)
+        timeSlider.value = Float(currentTime)
+    }
+    
+    func player(_ player: LWPlayer, showLoading: Bool) {
+        
+    }
+    
+    
+    
+    
+}
+

+ 25 - 0
JiaPeiManage/Sources/Custom/VideoPlayer/Core/LWPlayerManager.swift

@@ -0,0 +1,25 @@
+//
+//  LWPlayerManager.swift
+//  SwiftBilibili
+//
+//  Created by 罗文 on 2021/4/2.
+//  Copyright © 2021年 罗文. All rights reserved.
+//
+
+import UIKit
+
+class LWPlayerManager {
+
+    var player: LWPlayer?
+    var embeddedContentView: UIView?
+    
+    static let sharedInstance = LWPlayerManager()
+    
+    func playEmbeddedVideo(url: URL?, embeddedContentView contentView: UIView? = nil) {
+        
+        self.player = LWPlayer()
+        self.embeddedContentView = contentView
+        self.player!.playWithURL(url, embeddedContentView: embeddedContentView)
+    }
+    
+}

+ 70 - 0
JiaPeiManage/Sources/Custom/VideoPlayer/Core/LWPlayerSlider.swift

@@ -0,0 +1,70 @@
+//
+//  LWPlayerSlider.swift
+//  SwiftBilibili
+//
+//  Created by 罗文 on 2021/4/2.
+//  Copyright © 2021年 罗文. All rights reserved.
+//
+
+import UIKit
+
+open class LWPlayerSlider: UISlider {
+
+    open var progressView : UIProgressView
+    
+    public override init(frame: CGRect) {
+        self.progressView = UIProgressView()
+        super.init(frame: frame)
+        configureSlider()
+        
+    }
+    
+    convenience init() {
+        self.init(frame: CGRect.zero)
+    }
+    
+    required public init?(coder aDecoder: NSCoder) {
+        fatalError("init(coder:) has not been implemented")
+    }
+    
+    override open func thumbRect(forBounds bounds: CGRect, trackRect rect: CGRect, value: Float) -> CGRect {
+        let rect = super.thumbRect(forBounds: bounds, trackRect: rect, value: value)
+        let newRect = CGRect(x: rect.origin.x, y: rect.origin.y + 1, width: rect.width, height: rect.height)
+        return newRect
+    }
+    
+    override open func trackRect(forBounds bounds: CGRect) -> CGRect {
+        let rect = super.trackRect(forBounds: bounds)
+        let newRect = CGRect(origin: rect.origin, size: CGSize(width: rect.size.width, height: 2.0))
+        configureProgressView(newRect)
+        return newRect
+    }
+    
+    func configureSlider() {
+        minimumValue = 0.0
+        value = 0.0
+        maximumTrackTintColor = UIColor.clear
+        minimumTrackTintColor = UIColor.white
+        
+//        let thumbImage = VGPlayerUtils.imageResource("VGPlayer_ic_slider_thumb")
+//        let normalThumbImage = VGPlayerUtils.imageSize(image: thumbImage!, scaledToSize: CGSize(width: 15, height: 15))
+//        setThumbImage(normalThumbImage, for: .normal)
+//        let highlightedThumbImage = VGPlayerUtils.imageSize(image: thumbImage!, scaledToSize: CGSize(width: 20, height: 20))
+//        setThumbImage(highlightedThumbImage, for: .highlighted)
+        
+        backgroundColor = UIColor.clear
+        progressView.tintColor = UIColor.white
+        progressView.trackTintColor = UIColor.red
+    }
+    
+    func configureProgressView(_ frame: CGRect) {
+        progressView.frame = frame
+        insertSubview(progressView, at: 0)
+    }
+    
+    open func setProgress(_ progress: Float, animated: Bool) {
+        progressView.setProgress(progress, animated: animated)
+    }
+    
+
+}

+ 227 - 0
JiaPeiManage/Sources/Custom/VideoPlayer/Core/LWPlayerView.swift

@@ -0,0 +1,227 @@
+//
+//  LWPlayerView.swift
+//  SwiftBilibili
+//
+//  Created by 罗文 on 2021/3/28.
+//  Copyright © 2021年 罗文. All rights reserved.
+//
+
+import UIKit
+import AVFoundation
+
+final class LWPlayerView: UIView {
+
+    //播放器属性
+    weak private var player: LWPlayer?
+    
+    weak var controlView: UIView? {
+        didSet {
+            if oldValue != controlView{
+               resetControlView()
+            }
+        }
+    }
+    
+    override class var layerClass: AnyClass {
+        return AVPlayerLayer.self
+    }
+    
+    //手势属性
+    public var panGesture: UIPanGestureRecognizer!
+    public var singleTapGesture: UITapGestureRecognizer!
+    public var doubleTapGesture: UITapGestureRecognizer!
+    private var trigger = LWPlayerSlideTrigger.none
+    private var isHorizontalPan = true
+    private var currentPosition : TimeInterval?
+    private var volumeSlider : UISlider!
+    // MARK: - Life cycle
+    override init(frame: CGRect) {
+        super.init(frame: frame)
+        commonInit()
+        
+    }
+    
+    init(controlView: UIView?) {
+        super.init(frame: .zero)
+        self.controlView = controlView
+        commonInit()
+    }
+    
+    required init?(coder aDecoder: NSCoder) {
+        fatalError("init(coder:) has not been implemented")
+    }
+    
+    override func layoutSubviews() {
+        super.layoutSubviews()
+        self.controlView?.frame = self.bounds
+    }
+    
+    // MARK: - public
+    func config(player:LWPlayer? = nil) {
+        
+        if let player = player {
+            (self.layer as! AVPlayerLayer).player = player.player
+            if let customAction =  self.controlView as? LWPlayerCustomAction{
+                customAction.player = player
+            }
+            self.player = player
+        }
+    }
+    
+    
+    // MARK: - private
+    private func commonInit() {
+        
+        self.clipsToBounds = true
+        self.backgroundColor = UIColor.black
+        
+        self.autoresizingMask = [.flexibleHeight, .flexibleWidth,.flexibleLeftMargin,.flexibleTopMargin,.flexibleRightMargin,.flexibleBottomMargin]
+        
+        self.controlView?.autoresizingMask = [.flexibleLeftMargin,.flexibleTopMargin,.flexibleRightMargin,.flexibleBottomMargin]
+        self.addSubview(self.controlView!)
+        
+        self.panGesture = UIPanGestureRecognizer(target: self, action: #selector(self.panDirection(_:)))
+        self.addGestureRecognizer(self.panGesture)
+        self.panGesture.delegate = self
+        
+        self.singleTapGesture = UITapGestureRecognizer(target: self, action: #selector(self.singleTapGestureTapped(_:)))
+        self.singleTapGesture.delegate = self
+        self.singleTapGesture.numberOfTapsRequired = 1
+        self.singleTapGesture.numberOfTouchesRequired = 1
+        self.addGestureRecognizer(self.singleTapGesture)
+        
+        self.doubleTapGesture = UITapGestureRecognizer(target: self, action: #selector(self.doubleTapGestureTapped(_:)))
+        self.doubleTapGesture.delegate = self
+        self.doubleTapGesture.numberOfTapsRequired = 2
+        self.doubleTapGesture.numberOfTouchesRequired = 1
+        self.addGestureRecognizer(self.doubleTapGesture)
+        
+        self.singleTapGesture.require(toFail: self.doubleTapGesture)
+    }
+    
+    private func resetControlView() {
+        controlView?.removeFromSuperview()
+        self.addSubview(controlView!)
+        self.setNeedsDisplay()
+        if let customAction = controlView as? LWPlayerCustomAction {
+            customAction.player = self.player
+        }
+    }
+}
+
+//MARK: - 手势方法
+extension LWPlayerView {
+    
+    @objc private func panDirection(_ pan: UIPanGestureRecognizer) {
+        guard let player = self.player else { return }
+        let velocityPoint = pan.velocity(in: self)
+        switch pan.state {
+        case .began:
+            let x = abs(velocityPoint.x)
+            let y = abs(velocityPoint.y)
+            if x > y {
+               if let horizontalPanDelegate = controlView as? LWPlayerHorizontalPan,player.canSlideProgress {
+                  isHorizontalPan = true
+                  currentPosition = player.currentTime
+                  horizontalPanDelegate.player(player, progressWillChange: currentPosition ?? 0)
+               }
+            }else {
+                isHorizontalPan = false
+                if pan.location(in: self).x > self.bounds.size.width / 2 {
+                    trigger = player.slideTrigger.right
+                } else {
+                    trigger = player.slideTrigger.left
+                }
+            }
+        case .changed:
+            if isHorizontalPan {
+                horizontalMoved(velocityPoint.x)
+            } else {
+                verticalMoved(velocityPoint.y,player: player, slideType: trigger)
+            }
+        case .ended:
+            if isHorizontalPan {
+                if let horizontalPanDelegate = controlView as? LWPlayerHorizontalPan, player.canSlideProgress{
+                    if let currentPosition = currentPosition , !currentPosition.isNaN {
+                        horizontalPanDelegate.player(player, progressDidChange: currentPosition)
+                    }
+                }
+            }
+        default:
+            break
+        }
+    }
+    
+    @objc private func singleTapGestureTapped(_ sender: UIGestureRecognizer) {
+        guard let player = self.player else { return }
+        if let gestureRecognizer = controlView as? LWPlayerGestureRecognizer{
+            gestureRecognizer.player(player, singleTapGestureTapped: sender as! UITapGestureRecognizer)
+        }
+    }
+    
+    @objc private func doubleTapGestureTapped(_ sender: UIGestureRecognizer) {
+        guard let player = self.player else { return }
+        if let gestureRecognizer = controlView as? LWPlayerGestureRecognizer{
+            gestureRecognizer.player(player, doubleTapGestureTapped: sender as! UITapGestureRecognizer)
+        }
+    }
+    
+    private func verticalMoved(_ value: CGFloat, player: LWPlayer, slideType: LWPlayerSlideTrigger) {
+        
+        switch slideType {
+        case .volume:
+            player.systemVolume -= Float(value / 10000)
+        case .brightness:
+            UIScreen.main.brightness -= value / 10000
+        default:
+            break
+        }
+    }
+    
+    private func horizontalMoved(_ value: CGFloat) {
+        
+        guard let player = player,
+              let horizontalPanDelegate = controlView as? LWPlayerHorizontalPan,
+              player.canSlideProgress
+        else {
+            return
+        }
+        
+        if let currentPosition = currentPosition,!currentPosition.isNaN,
+           let duration = player.duration,!duration.isNaN {
+    
+           let nextPosition = currentPosition + TimeInterval(value) / 100.0 * (duration/400)
+           if nextPosition > duration {
+                self.currentPosition = duration
+           } else if nextPosition < 0 {
+                self.currentPosition = 0
+           } else {
+                self.currentPosition = nextPosition
+           }
+           horizontalPanDelegate.player(player, progressDidChange: nextPosition)
+        }
+    }
+}
+extension LWPlayerView: UIGestureRecognizerDelegate {
+    
+    public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
+        guard  let player = self.player else {
+            return false
+        }
+        
+        if self.singleTapGesture == gestureRecognizer || self.doubleTapGesture == gestureRecognizer {
+            if let customAction =  self.controlView as? LWPlayerCustomAction{//点击控制条
+                return  !customAction.autoHidedControlViews.contains(touch.view!) && !customAction.autoHidedControlViews.contains(touch.view!.superview!)
+            }
+        } else if self.panGesture == gestureRecognizer {
+            
+            if player.displayMode == .float || player.isLive ?? true {
+                return false
+            }
+            
+            return touch.view == self.controlView
+        }
+        return true
+    }
+}
+

+ 37 - 0
JiaPeiManage/Sources/Custom/VideoPlayer/Extension/AVPlayer+LWPlayer.swift

@@ -0,0 +1,37 @@
+//
+//  AVPlayer+LWPlayer.swift
+//  SwiftBilibili
+//
+//  Created by 罗文 on 2021/3/28.
+//  Copyright © 2021年 罗文. All rights reserved.
+//
+
+import AVFoundation
+
+public extension AVPlayer {
+    
+    // 观看了的时长(不包括暂停等)
+    var durationWatched: TimeInterval {
+        var duration: TimeInterval = 0
+        if let events = self.currentItem?.accessLog()?.events {
+            for event in events {
+                duration += event.durationWatched
+            }
+        }
+        return duration
+    }
+    
+    // 总时长
+    var duration: TimeInterval? {
+        if let  duration = self.currentItem?.duration  {
+            return CMTimeGetSeconds(duration)
+        }
+        return nil
+    }
+    
+    // 播放进度
+    var currentTime: TimeInterval? {
+        return CMTimeGetSeconds(self.currentTime())
+    }
+    
+}

+ 33 - 0
JiaPeiManage/Sources/Custom/VideoPlayer/Extension/Timer+LWPlayer.swift

@@ -0,0 +1,33 @@
+//
+//  Timer+LWPlayer.swift
+//  SwiftBilibili
+//
+//  Created by 罗文 on 2021/4/2.
+//  Copyright © 2021年 罗文. All rights reserved.
+//
+
+import Foundation
+
+public extension Timer {
+    class func scheduledTimerWithTimeInterval(_ timeInterval: TimeInterval, block: ()->(),  repeats: Bool) -> Timer {
+        return self.scheduledTimer(timeInterval: timeInterval, target: self, selector: #selector(Timer.executeBlockWithTimer(_:)), userInfo: AnyObject.self, repeats: repeats)
+    }
+    
+    class func timerWithTimeInterval(_ timeInterval: TimeInterval, block: ()->(),  repeats: Bool) -> Timer {
+        return Timer(timeInterval: timeInterval, target: self, selector: #selector(Timer.executeBlockWithTimer(_:)), userInfo: AnyObject.self, repeats: repeats)
+    }
+    
+    @objc private class func executeBlockWithTimer(_ timer: Timer) {
+//        let block: ()->() = timer.userInfo as! ()->()
+//        block()
+    }
+    
+    static func executeOnMainQueueAfterTimeInterval(_ seconds: TimeInterval,block: @escaping ()->()) {
+        executeAfterTimeInterval(seconds, queue: DispatchQueue.main, block: block)
+    }
+    
+    static func executeAfterTimeInterval(_ seconds: TimeInterval, queue: DispatchQueue, block: @escaping ()->()) {
+        let time = DispatchTime.now() + Double(Int64(seconds * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC)
+        queue.asyncAfter(deadline: time, execute: block)
+    }
+}

+ 48 - 0
JiaPeiManage/Sources/Custom/VideoPlayer/Utiles/LWPlayerDelegate.swift

@@ -0,0 +1,48 @@
+//
+//  LWPlayerDelegate.swift
+//  SwiftBilibili
+//
+//  Created by 罗文 on 2021/3/28.
+//  Copyright © 2021年 罗文. All rights reserved.
+//
+
+import UIKit
+
+public protocol LWPlayerDelegate : AnyObject {
+
+   func player(_ player: LWPlayer ,playerStateDidChange state: LWPlayerState)
+   
+   func player(_ player: LWPlayer ,playerDisplayModeDidChange displayMode: LWPlayerDisplayMode)
+    
+   func player(_ player: LWPlayer ,loadedTimeDidChange bufferDuration: TimeInterval, totalDuration: TimeInterval)
+    
+   func player(_ player: LWPlayer ,playedTimeDidChange currentTime: TimeInterval, totalDuration: TimeInterval)
+    
+   func player(_ player: LWPlayer ,showLoading: Bool)
+}
+
+public protocol LWPlayerHorizontalPan: AnyObject {
+    func player(_ player: LWPlayer ,progressWillChange value: TimeInterval)
+    func player(_ player: LWPlayer ,progressChanging value: TimeInterval)
+    func player(_ player: LWPlayer ,progressDidChange value: TimeInterval)
+}
+
+public protocol LWPlayerGestureRecognizer: AnyObject {
+    func player(_ player: LWPlayer ,singleTapGestureTapped singleTap: UITapGestureRecognizer)
+    func player(_ player: LWPlayer ,doubleTapGestureTapped doubleTap: UITapGestureRecognizer)
+}
+
+
+public protocol LWPlayerCustomAction: AnyObject {
+    var player: LWPlayer? { get set }
+    var autoHidedControlViews: [UIView] { get set }
+    
+    func playPauseButtonPressed(_ sender: Any)
+    func fullEmbeddedScreenButtonPressed(_ sender: Any)
+    func audioSubtitleCCButtonPressed(_ sender: Any)
+    func backButtonPressed(_ sender: Any)
+}
+
+public protocol LWPlayerCustom: LWPlayerDelegate,LWPlayerCustomAction,LWPlayerHorizontalPan,LWPlayerGestureRecognizer {
+}
+

+ 60 - 0
JiaPeiManage/Sources/Custom/VideoPlayer/Utiles/LWPlayerEnum.swift

@@ -0,0 +1,60 @@
+//
+//  LWPlayerEnum.swift
+//  SwiftBilibili
+//
+//  Created by 罗文 on 2021/3/28.
+//  Copyright © 2021年 罗文. All rights reserved.
+//
+
+public enum LWPlayerError: Error {
+    case invalidContentURL
+    case playerFail
+}
+
+public enum LWPlayerVideoType {
+    case live
+    case av
+}
+
+public enum LWPlayerState {
+    case unknown      // 播放前
+    case error(LWPlayerError)      // 出现错误
+    case readyToPlay    // 可以播放
+    case buffering      // 缓冲中
+    case bufferFinished // 缓冲完毕
+    case playing // 播放
+    case seekingForward // 快进
+    case seekingBackward // 快退
+    case pause // 播放暂停
+    case stopped // 播放结束
+}
+
+public enum LWPlayerDisplayMode  {
+    case none
+    case embedded
+    case fullscreen
+    case float       //小窗
+}
+
+public enum LWPlayerFullScreenMode  {
+    case portrait
+    case landscape
+}
+
+public enum LWPlayerVideoGravity : String {
+    case aspect = "AVLayerVideoGravityResizeAspect"    //视频值 ,等比例填充,直到一个维度到达区域边界
+    case aspectFill = "AVLayerVideoGravityResizeAspectFill"   //等比例填充,直到填充满整个视图区域,其中一个维度的部分区域会被裁剪
+    case scaleFill = "AVLayerVideoGravityResize"     //非均匀模式。两个维度完全填充至整个视图区域
+}
+
+public enum LWPlayerPlaybackDidFinishReason  {
+    case playbackEndTime
+    case playbackError
+    case stopByUser
+}
+
+public enum LWPlayerSlideTrigger{
+    case none
+    case volume
+    case brightness
+}

+ 85 - 0
JiaPeiManage/Sources/Custom/VideoPlayer/Utiles/LWPlayerUtils.swift

@@ -0,0 +1,85 @@
+//
+//  LWPlayerUtils.swift
+//  SwiftBilibili
+//
+//  Created by 罗文 on 2021/3/30.
+//  Copyright © 2021年 罗文. All rights reserved.
+//
+
+import UIKit
+import MediaPlayer
+
+
+/// EZPlayerState的相等判断
+///
+/// - Parameters:
+///   - lhs: 左值
+///   - rhs: 右值
+/// - Returns: 比较结果
+public func ==(lhs: LWPlayerState, rhs: LWPlayerState) -> Bool {
+    switch (lhs, rhs) {
+    case (.unknown,   .unknown): return true
+    case (.readyToPlay,   .readyToPlay): return true
+    case (.buffering,   .buffering): return true
+    case (.bufferFinished,   .bufferFinished): return true
+    case (.playing,   .playing): return true
+    case (.seekingForward,   .seekingForward): return true
+    case (.seekingBackward,   .seekingBackward): return true
+    case (.pause,   .pause): return true
+    case (.stopped,   .stopped): return true
+    case (.error(let a), .error(let b)) where a == b: return true
+    default: return false
+    }
+}
+
+/// EZPlayerState的不相等判断
+///
+/// - Parameters:
+///   - lhs: 左值
+///   - rhs: 右值
+/// - Returns: 比较结果
+public func !=(lhs: LWPlayerState, rhs: LWPlayerState) -> Bool {
+    return !(lhs == rhs)
+}
+
+public class LWPlayerUtils {
+
+    /// system volume ui
+    public static let systemVolumeSlider : UISlider = {
+        let volumeView = MPVolumeView()
+        volumeView.showsVolumeSlider = true
+        volumeView.showsRouteButton = false
+        var returnSlider : UISlider!
+        for view in volumeView.subviews {
+            if let slider = view as? UISlider {
+                returnSlider = slider
+                break
+            }
+        }
+        return returnSlider
+    }()
+    
+    
+    /// fotmat time
+    ///
+    /// - Parameters:
+    ///   - position: video current position
+    ///   - duration: video duration
+    /// - Returns: formated time string
+    public static func formatTime( position: TimeInterval,duration:TimeInterval) -> String{
+        guard !position.isNaN && !duration.isNaN else{
+            return ""
+        }
+        let positionHours = (Int(position) / 3600) % 60
+        let positionMinutes = (Int(position) / 60) % 60
+        let positionSeconds = Int(position) % 60;
+        
+        let durationHours = (Int(duration) / 3600) % 60
+        let durationMinutes = (Int(duration) / 60) % 60
+        let durationSeconds = Int(duration) % 60
+        if(durationHours == 0){
+            return String(format: "%02d:%02d/%02d:%02d",positionMinutes,positionSeconds,durationMinutes,durationSeconds)
+        }
+        return String(format: "%02d:%02d:%02d/%02d:%02d:%02d",positionHours,positionMinutes,positionSeconds,durationHours,durationMinutes,durationSeconds)
+    }
+}

+ 36 - 0
JiaPeiManage/Sources/Custom/View/CollectionViewLayout/BilibiliCollectionViewLayout.swift

@@ -0,0 +1,36 @@
+//
+//  BilibiliCollectionViewLayout.swift
+//  SwiftBilibili
+//
+//  Created by 罗文 on 2021/7/18.
+//  Copyright © 2021年 罗文. All rights reserved.
+//  处理cell间距为0仍有间隙的问题
+
+import UIKit
+
+final class BilibiliCollectionViewLayout: UICollectionViewFlowLayout {
+    
+    var maxInteritemSpacing: CGFloat = 0
+    
+    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
+        
+        guard let superAttributes = super.layoutAttributesForElements(in: rect) else { return nil }
+        
+        let attributes = NSArray(array: superAttributes, copyItems: true) as! [UICollectionViewLayoutAttributes]
+        
+        if attributes.count < 2 { return attributes }
+        
+        for i in 1..<attributes.count {
+            let currentLayoutAttributes = attributes[i]
+            let prevLayoutAttributes = attributes[i-1]
+            let origin = prevLayoutAttributes.frame.maxX
+            if origin + maxInteritemSpacing + currentLayoutAttributes.frame.size.width <= self.collectionViewContentSize.width {
+                
+                var frame = currentLayoutAttributes.frame
+                frame.origin.x = origin + maxInteritemSpacing
+                currentLayoutAttributes.frame = frame
+            }
+        }
+        return attributes
+    }
+}

+ 15 - 0
JiaPeiManage/Sources/Custom/View/EmptyView/EmptyView.swift

@@ -0,0 +1,15 @@
+//
+//  EmptyView.swift
+//  SwiftBilibili
+//
+//  Created by 罗文 on 2021/2/6.
+//  Copyright © 2021年 罗文. All rights reserved.
+//
+
+import UIKit
+
+final class EmptyView: UIView,NibLoadable {
+
+
+
+}

+ 62 - 0
JiaPeiManage/Sources/Custom/View/EmptyView/EmptyView.xib

@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="13771" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
+    <device id="retina4_7" orientation="portrait">
+        <adaptation id="fullscreen"/>
+    </device>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13772"/>
+        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <objects>
+        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
+        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
+        <view contentMode="scaleToFill" id="M0l-TV-B9z" customClass="EmptyView" customModule="SwiftBilibili" customModuleProvider="target">
+            <rect key="frame" x="0.0" y="0.0" width="400" height="220"/>
+            <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+            <subviews>
+                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="sBg-X0-qmO">
+                    <rect key="frame" x="100" y="0.0" width="200" height="220"/>
+                    <subviews>
+                        <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="home_noData" translatesAutoresizingMaskIntoConstraints="NO" id="qNX-mI-6YY">
+                            <rect key="frame" x="0.0" y="0.0" width="200" height="180"/>
+                            <constraints>
+                                <constraint firstAttribute="height" constant="180" id="hBO-py-Jld"/>
+                            </constraints>
+                        </imageView>
+                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="什么都没有找到~" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="UuX-aB-hYx">
+                            <rect key="frame" x="0.0" y="180" width="200" height="40"/>
+                            <fontDescription key="fontDescription" type="system" pointSize="17"/>
+                            <nil key="textColor"/>
+                            <nil key="highlightedColor"/>
+                        </label>
+                    </subviews>
+                    <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                    <constraints>
+                        <constraint firstItem="qNX-mI-6YY" firstAttribute="leading" secondItem="sBg-X0-qmO" secondAttribute="leading" id="M5K-wK-MJp"/>
+                        <constraint firstAttribute="width" constant="200" id="OHs-6R-2MM"/>
+                        <constraint firstAttribute="bottom" secondItem="UuX-aB-hYx" secondAttribute="bottom" id="aqu-Zc-HaQ"/>
+                        <constraint firstAttribute="trailing" secondItem="qNX-mI-6YY" secondAttribute="trailing" id="haN-zT-ZsN"/>
+                        <constraint firstItem="qNX-mI-6YY" firstAttribute="top" secondItem="sBg-X0-qmO" secondAttribute="top" id="jpz-8L-BLj"/>
+                        <constraint firstItem="UuX-aB-hYx" firstAttribute="top" secondItem="qNX-mI-6YY" secondAttribute="bottom" id="k2y-qG-svY"/>
+                        <constraint firstItem="UuX-aB-hYx" firstAttribute="leading" secondItem="sBg-X0-qmO" secondAttribute="leading" id="mML-n1-RPN"/>
+                        <constraint firstAttribute="trailing" secondItem="UuX-aB-hYx" secondAttribute="trailing" id="msL-Mr-Mep"/>
+                    </constraints>
+                </view>
+            </subviews>
+            <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+            <constraints>
+                <constraint firstItem="sBg-X0-qmO" firstAttribute="centerX" secondItem="vC8-CP-LKV" secondAttribute="centerX" id="MeP-3T-ibO"/>
+                <constraint firstItem="sBg-X0-qmO" firstAttribute="top" secondItem="vC8-CP-LKV" secondAttribute="top" id="cOw-K6-e4j"/>
+                <constraint firstItem="vC8-CP-LKV" firstAttribute="bottom" secondItem="sBg-X0-qmO" secondAttribute="bottom" id="g5D-3I-OfQ"/>
+            </constraints>
+            <freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
+            <viewLayoutGuide key="safeArea" id="vC8-CP-LKV"/>
+            <point key="canvasLocation" x="87" y="-161"/>
+        </view>
+    </objects>
+    <resources>
+        <image name="home_noData" width="400" height="400"/>
+    </resources>
+</document>

+ 22 - 0
JiaPeiManage/Sources/Custom/View/GesConflictCollectionView/GesConflictCollectionView.swift

@@ -0,0 +1,22 @@
+//
+//  GesConflictCollectionView.swift
+//  SwiftBilibili
+//
+//  Created by 罗文 on 2021/3/4.
+//  Copyright © 2021年 罗文. All rights reserved.
+//
+
+import UIKit
+
+final class GesConflictCollectionView: UICollectionView,UIGestureRecognizerDelegate {
+
+    var allowOtherGes: Bool = false
+    
+    
+    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
+        
+        return allowOtherGes
+        
+    }
+
+}

+ 104 - 0
JiaPeiManage/Sources/Custom/View/ImageViewAnimation/NetAnimationView.swift

@@ -0,0 +1,104 @@
+//
+//  NetAnimationView.swift
+//  SwiftBilibili
+//
+//  Created by 罗文 on 2021/1/15.
+//  Copyright © 2021年 罗文. All rights reserved.
+//
+
+import UIKit
+
+enum AnimationType {
+    case loading
+    case failure
+}
+
+final class NetAnimationView: UIView {
+
+    fileprivate var type : AnimationType
+    
+    fileprivate let imageView = UIImageView().then{
+        $0.contentMode = .scaleToFill
+    }
+    
+    fileprivate let textLabel = UILabel().then{
+        $0.textColor = .db_darkGray
+        $0.font = NYFont.SysFont.sys_15
+        $0.textAlignment = .center
+    }
+    
+    init(animationType:AnimationType) {
+        
+        self.type = animationType
+        
+        super.init(frame: .zero)
+
+        self.isHidden = true
+        
+        addSubview(imageView)
+        addSubview(textLabel)
+        
+        configureSubView()
+    }
+    
+    required init?(coder aDecoder: NSCoder) {
+        fatalError("init(coder:) has not been implemented")
+    }
+    
+    fileprivate func configureSubView() {
+        
+        if type == .loading {
+           textLabel.text = "正在努力加载数据中..."
+           imageView.image = UIImage(named: "animation_loading_loading_1")
+           imageView.animationImages = [UIImage(named: "animation_loading_loading_1")!,UIImage(named: "animation_loading_loading_2")!]
+           imageView.animationDuration = 0.5
+           imageView.animationRepeatCount = 0
+        }
+        
+        if type == .failure {
+           textLabel.text = "似乎与互联网已经断开连接"
+           imageView.image = UIImage(named: "animation_loading_error_4")
+           imageView.animationImages = [UIImage(named: "animation_loading_error_1")!,
+                                        UIImage(named: "animation_loading_error_2")!,
+                                        UIImage(named: "animation_loading_error_3")!,
+                                        UIImage(named: "animation_loading_error_4")!]
+           imageView.animationDuration = 2.0
+           imageView.animationRepeatCount = 1
+        }
+        
+    }
+    
+    func startAnimation(animationType:AnimationType) {
+        
+        self.isHidden = false
+       
+        imageView.startAnimating()
+        
+    }
+    
+    func stopAnimation(animationType:AnimationType = .loading) {
+        self.isHidden = true
+        
+        imageView.stopAnimating()
+        
+    }
+    
+    override func layoutSubviews() {
+        super.layoutSubviews()
+        
+        imageView.snp.makeConstraints { (make) in
+            make.left.right.top.equalToSuperview()
+            make.height.equalTo(180)
+        }
+        
+        textLabel.snp.makeConstraints { (make) in
+            make.left.right.equalToSuperview()
+            make.top.equalTo(imageView.snp.bottom)
+            make.height.equalTo(20)
+        }
+    }
+    
+    
+    
+    
+}

+ 20 - 0
JiaPeiManage/Sources/Custom/View/PopOverView/Action.swift

@@ -0,0 +1,20 @@
+//
+//  Action.swift
+//  SwiftBilibili
+//
+//  Created by 罗文 on 2021/2/2.
+//  Copyright © 2021年 罗文. All rights reserved.
+//
+
+import UIKit
+
+struct Action<T> {
+
+    fileprivate(set) var data: T?
+    fileprivate(set) var handler: ((Action<T>) -> Void)?
+    
+    init(_ data: T?,handler: ((Action<T>) -> Void)?) {
+        self.data = data
+        self.handler = handler
+    }
+}

+ 609 - 0
JiaPeiManage/Sources/Custom/View/PopOverView/PopOverView.swift

@@ -0,0 +1,609 @@
+//
+//  PopOverView.swift
+//  SwiftBilibili
+//
+//  Created by 罗文 on 2021/1/24.
+//  Copyright © 2021年 罗文. All rights reserved.
+//
+
+import UIKit
+
+class Section<ActionDataType, SectionHeaderDataType> {
+    
+    open var data: SectionHeaderDataType? {
+        get { return _data?.data }
+        set { _data = RawData(data: newValue) }
+    }
+    open var actions = [Action<ActionDataType>]()
+    fileprivate var _data: RawData<SectionHeaderDataType>?
+    
+    public init() {}
+}
+
+enum CellSpec<CellType: UITableViewCell,CellDataType> {
+    case nibFile(nibName: String, bundle: Bundle?, height: ((CellDataType) -> CGFloat))
+    case cellClass(height:((CellDataType) -> CGFloat))
+    
+    var height: ((CellDataType) -> CGFloat) {
+        
+        switch self {
+        case .nibFile(_, _, let heightCallback):
+            return heightCallback
+        case .cellClass(let heightCallback):
+            return heightCallback
+        }
+    }
+}
+
+enum HeaderSpec<HeaderType: UIView,HeaderDataType> {
+    case nibFile(nibName: String, bundle: Bundle?, height: ((HeaderDataType) -> CGFloat))
+    case cellClass(height:((HeaderDataType) -> CGFloat))
+    
+    var height: ((HeaderDataType) -> CGFloat) {
+        switch self {
+        case .nibFile(_, _, let heightCallback):
+            return heightCallback
+        case .cellClass(let heightCallback):
+            return heightCallback
+        }
+    }
+}
+
+enum FooterSpec<FooterType: UIView,FooterDataType> {
+    case nibFile(nibName: String, bundle: Bundle?, height: ((FooterDataType) -> CGFloat))
+    case cellClass(height:((FooterDataType) -> CGFloat))
+    
+    var height: ((FooterDataType) -> CGFloat) {
+        switch self {
+        case .nibFile(_, _, let heightCallback):
+            return heightCallback
+        case .cellClass(let heightCallback):
+            return heightCallback
+        }
+    }
+}
+
+private enum ReusableViewIds: String {
+    case Cell = "Cell"
+    case SectionHeader = "SectionHeader"
+}
+
+final class RawData<T> {
+    var data: T!
+    
+    init?(data: T?) {
+        guard let data = data else { return nil }
+        self.data = data
+    }
+}
+
+class PopOverView<ActionViewType: UITableViewCell,ActionDataType,HeaderViewType:UIView,HeaderDataType,FooterViewType:UIView,FooterDataType,SectionHeaderViewType: UITableViewHeaderFooterView, SectionHeaderDataType> : UIView,UITableViewDataSource,UITableViewDelegate {
+    
+    private let screenHeight = UIScreen.main.bounds.height
+    private let screenWidth = UIScreen.main.bounds.width
+    private let keyWindow = UIApplication.shared.keyWindow
+    private var isUpward = true
+    
+    //MARK: - Private properties
+    fileprivate var _footerData: RawData<FooterDataType>?
+    fileprivate var _headerData: RawData<HeaderDataType>?
+    fileprivate var _actions = [Action<ActionDataType>]()
+    fileprivate var _sections = [Section<ActionDataType, SectionHeaderDataType>]()
+    
+    
+    //MARK - Public properties
+    var headerData: HeaderDataType? {
+        set { _headerData = RawData(data: newValue)}
+        get { return _headerData?.data}
+    }
+    
+    var footerData: FooterDataType? {
+        set { _footerData = RawData(data: newValue)}
+        get { return _footerData?.data}
+    }
+    
+    var settings: PopOverViewSettings = PopOverViewSettings.defaultSettings()
+    
+    var cellSpec: CellSpec<ActionViewType,ActionDataType>?
+    var sectionHeaderSpec: HeaderSpec<SectionHeaderViewType, SectionHeaderDataType>?
+    var headerSpec: HeaderSpec<HeaderViewType,HeaderDataType>?
+    var footerSpec: FooterSpec<FooterViewType,FooterDataType>?
+    var onConfigureCellForAction: ((ActionViewType,Action<ActionDataType>,IndexPath) -> ())?
+    var onConfigureHeader: ((HeaderViewType,HeaderDataType) -> ())?
+    var onConfigureSectionHeader: ((SectionHeaderViewType, SectionHeaderDataType) -> ())?
+    var onConfigureFooter: ((FooterViewType,FooterDataType) -> ())?
+
+    //MARK: - UI
+    private lazy var backgroundView: UIView = {
+        let backgroundView = UIView(frame: UIScreen.main.bounds)
+        backgroundView.autoresizingMask = [.flexibleHeight,.flexibleWidth]
+        backgroundView.backgroundColor = self.settings.overView.coverViewColor
+        if self.settings.behavior.hideOnTap {
+            let tapGes = UITapGestureRecognizer(target: self, action: #selector(PopOverView.tapGestureDidRecognize(_:)))
+            backgroundView.addGestureRecognizer(tapGes)
+        }
+        return backgroundView
+    }()
+    
+    lazy var tableView: UITableView = {
+        let tableView = UITableView(frame: .zero, style: .plain)
+        tableView.bounces = self.settings.behavior.bounces
+        tableView.autoresizingMask = [.flexibleHeight,.flexibleWidth]
+        tableView.backgroundColor = UIColor.clear
+        tableView.isScrollEnabled = self.settings.behavior.scrollEnable
+        tableView.showsVerticalScrollIndicator = false
+        tableView.estimatedRowHeight = 0
+        tableView.estimatedSectionFooterHeight = 0
+        tableView.estimatedSectionHeaderHeight = 0
+        tableView.separatorStyle = .none
+        tableView.dataSource = self
+        tableView.delegate = self
+        return tableView
+    }()
+    
+    //MARK: - Initializers
+    override init(frame: CGRect) {
+        super.init(frame: frame)
+        self.backgroundColor = settings.overView.backgroundColor
+        self.addSubview(tableView)
+    }
+    
+    required init?(coder aDecoder: NSCoder) {
+        super.init(coder: aDecoder)
+        self.backgroundColor = settings.overView.backgroundColor
+        self.addSubview(tableView)
+    }
+    
+    override func willMove(toSuperview newSuperview: UIView?) {
+        super.willMove(toSuperview: newSuperview)
+        
+        initializers()
+    }
+    
+    
+    private func initializers() {
+        
+        if let cellSpec = self.cellSpec {
+            switch cellSpec {
+            case let .nibFile(nibName, bundle, _):
+                tableView.register(UINib(nibName: nibName, bundle: bundle), forCellReuseIdentifier: ReusableViewIds.Cell.rawValue)
+            case .cellClass:
+                tableView.register(ActionViewType.self, forCellReuseIdentifier: ReusableViewIds.Cell.rawValue)
+            }
+        }
+        
+        if let headerSpec = headerSpec, let headerData = headerData {
+            
+            switch headerSpec {
+            case let .nibFile(nibName, bundle, _):
+                let bundle = bundle == nil ? Bundle.main : bundle
+                let headerView = bundle?.loadNibNamed(nibName, owner: nil, options: nil)?.first as? UIView
+                tableView.tableHeaderView = headerView
+                onConfigureHeader?(headerView as! HeaderViewType,headerData)
+            case .cellClass:
+                tableView.tableHeaderView = HeaderViewType()
+            }
+        }
+        
+        if let footerSpec = footerSpec, let footerData = footerData {
+            
+            switch footerSpec {
+            case let .nibFile(nibName, bundle, _):
+                let bundle = bundle == nil ? Bundle.main : bundle
+                let footView = bundle?.loadNibNamed(nibName, owner: nil, options: nil)?.first as? UIView
+                tableView.tableFooterView = footView
+                onConfigureFooter?(footView as! FooterViewType,footerData)
+            case .cellClass:
+                tableView.tableFooterView = FooterViewType()
+            }
+        }
+        
+        if let headerSpec = sectionHeaderSpec {
+            switch headerSpec {
+            case .cellClass:
+                tableView.register(SectionHeaderViewType.self, forHeaderFooterViewReuseIdentifier: ReusableViewIds.SectionHeader.rawValue)
+            case let .nibFile(nibName, bundle, _):
+                tableView.register(UINib(nibName: nibName, bundle: bundle), forHeaderFooterViewReuseIdentifier: ReusableViewIds.SectionHeader.rawValue)
+            }
+        }
+    }
+    
+    //MARK: - Public API
+    func addAction(_ action: Action<ActionDataType>) {
+        if let section = _sections.last {
+            section.actions.append(action)
+        }else{
+            let section = Section<ActionDataType, SectionHeaderDataType>()
+            addSection(section)
+            section.actions.append(action)
+        }
+    }
+    
+    @discardableResult
+    open func addSection(_ section: Section<ActionDataType, SectionHeaderDataType>) -> Section<ActionDataType, SectionHeaderDataType> {
+        _sections.append(section)
+        return section
+    }
+    
+    // MARK: - Helpers
+    
+    func sectionForIndex(_ index: Int) -> Section<ActionDataType, SectionHeaderDataType>? {
+        return _sections[index]
+    }
+    
+    func actionForIndexPath(_ indexPath: IndexPath) -> Action<ActionDataType>? {
+        return _sections[(indexPath as NSIndexPath).section].actions[(indexPath as NSIndexPath).item]
+    }
+    
+    func actionIndexPathFor(_ indexPath: IndexPath) -> IndexPath {
+        if hasHeader() {
+            return IndexPath(item: (indexPath as NSIndexPath).item, section: (indexPath as NSIndexPath).section - 1)
+        }
+        return indexPath
+    }
+    
+    private func actionSectionIndexFor(_ section: Int) -> Int {
+        return hasHeader() ? section - 1 : section
+    }
+    
+    override func layoutSubviews() {
+        super.layoutSubviews()
+        
+        tableView.frame = CGRect(x: 0, y: isUpward ? settings.arrowView.height : 0, width: self.bounds.width, height: self.bounds.height - settings.arrowView.height)
+    }
+    
+    
+    //MARK: - Event handlers
+    @objc func tapGestureDidRecognize(_ gesture: UITapGestureRecognizer) {
+        dismiss()
+    }
+    
+    //MARK: - Internal helpers
+    func hasHeader() -> Bool {
+        return headerData != nil && headerSpec != nil
+    }
+    
+    func hasFooter() -> Bool {
+        return footerData != nil && footerSpec != nil
+    }
+    
+    private func numberOfSections() -> Int {
+        return hasHeader() ? _sections.count + 1 : _sections.count
+    }
+    
+    func show(pointView: UIView,_ completion:(() -> ())? = nil) {
+       
+        guard let pointViewRect = pointView.superview?.convert(pointView.frame, to: keyWindow) else { return }
+        let pointViewUpLenth = pointViewRect.minY
+        let pointViewDownLength = screenHeight - pointViewRect.maxY
+        // 弹窗箭头指向的点
+        var toPoint = CGPoint(x: pointViewRect.midX, y: 0)
+        // 弹窗在 pointView 顶部
+        if pointViewUpLenth > pointViewDownLength {
+            
+            if pointViewUpLenth > screenHeight - settings.arrowView.igoreOffest {
+                
+                toPoint.y = pointViewUpLenth - settings.arrowView.upOffest - settings.arrowView.targetOffest
+            }else{
+                toPoint.y = pointViewUpLenth - settings.arrowView.targetOffest
+            }
+            
+            isUpward = false
+        }else{
+            toPoint.y = pointViewRect.maxY + settings.arrowView.targetOffest
+            isUpward = true
+        }
+        
+        show(toPoint: toPoint,completion:completion)
+    }
+    
+    func show(tapPoint:CGPoint,completion:(() -> ())? = nil) {
+        
+        isUpward = tapPoint.y <= screenHeight - tapPoint.y
+
+        show(toPoint: tapPoint,completion:completion)
+    }
+    
+    func dismiss(isNeedAnimation:Bool = true,_ completion: (() -> ())? = nil) {
+        
+        let duration = isNeedAnimation ? settings.animation.duration : 0
+        
+        UIView.animate(withDuration: duration, animations: {
+            
+            self.alpha = 0.0
+            self.backgroundView.alpha = 0.0
+            self.transform = CGAffineTransform(scaleX: self.settings.animation.scale.width, y: self.settings.animation.scale.height)
+        }) { (_) in
+            self.backgroundView.removeFromSuperview()
+            self.removeFromSuperview()
+            if completion != nil {
+                completion!()
+            }
+        }
+    }
+    
+    private func show(toPoint:CGPoint,completion:(() -> ())? = nil) {
+        
+        //参数
+        let edgeAlignment = settings.arrowView.edgeAlignment
+        let arrowWidth = settings.arrowView.width
+        let arrowHeight = settings.arrowView.height
+        let viewCornerRadius = settings.overView.viewCornerRadius
+        let arrowCornerRadius = settings.arrowView.arrowCornerRadius
+        let arrowBottomCornerRadius = settings.arrowView.arrowBottomCornerRadius
+        let horizontalMargin = settings.overView.horizontalMargin
+        let verticalMargin = settings.overView.verticalMargin
+        let viewWidth = settings.overView.viewWidth
+        
+        var toPoint = toPoint
+        //如果不需要箭头边缘对齐
+        if !edgeAlignment {
+           
+           let minHorizonalEdge = horizontalMargin + viewCornerRadius + arrowWidth/2
+           
+            if toPoint.x < minHorizonalEdge {
+                toPoint.x = minHorizonalEdge
+            }
+            
+            if screenWidth - toPoint.x < minHorizonalEdge {
+                toPoint.x = screenWidth - minHorizonalEdge
+            }
+        }
+        
+        backgroundView.alpha = 0
+        keyWindow!.addSubview(backgroundView)
+        
+        tableView.reloadData()
+        
+        let currentW = viewWidth
+        var currentH = tableView.contentSize.height
+        
+        if let headerSpec = headerSpec,let headerData = headerData {
+            currentH += headerSpec.height(headerData)
+        }
+        
+        if let footerSpec = footerSpec,let footerData = footerData {
+            currentH += footerSpec.height(footerData)
+        }
+        
+        currentH += arrowHeight
+        
+        // 限制最高高度, 免得选项太多时超出屏幕
+        let statusBarFrame = UIApplication.shared.statusBarFrame
+        let maxHeight = isUpward ? screenHeight - toPoint.y - verticalMargin : toPoint.y - statusBarFrame.height
+        if currentH > maxHeight {
+            currentH = maxHeight
+            tableView.isScrollEnabled = true
+        }
+        
+        var currentX = toPoint.x - currentW/2 + horizontalMargin
+        var currentY = toPoint.y
+        
+        var isLeft = false
+        var isRight = false
+    
+        if edgeAlignment {
+            isLeft = toPoint.x + currentW + horizontalMargin <= screenWidth
+            isRight = horizontalMargin + currentW <= toPoint.x
+        }else{
+            isLeft = toPoint.x <= currentW/2 + horizontalMargin
+            isRight = screenWidth - toPoint.x <= currentW/2 + horizontalMargin
+        }
+        // x: 窗口靠左
+        if isLeft {
+            currentX = edgeAlignment ? toPoint.x : horizontalMargin
+        }
+        // x: 窗口靠右
+        if isRight {
+            currentX = edgeAlignment ? toPoint.x - currentW : screenWidth - horizontalMargin - currentW
+        }
+        
+        if !isUpward {
+            currentY = toPoint.y - currentH
+        }
+        
+        self.frame = CGRect(x: currentX, y: currentY, width: currentW, height: currentH)
+        
+        let arrowPoint = CGPoint(x: toPoint.x - self.frame.minX, y: isUpward ? 0 : currentH)
+        let maskTop = isUpward ? arrowHeight : 0
+        let maskBottom = isUpward ? currentH : currentH - arrowHeight
+        let maskPath = UIBezierPath()
+
+        if edgeAlignment && isLeft && isUpward {
+            
+            maskPath.move(to: CGPoint(x: 0, y: 0))
+            
+        }else{
+            //左上圆角
+            maskPath.move(to: CGPoint(x: 0, y: viewCornerRadius + maskTop))
+            maskPath.addArc(withCenter: CGPoint(x: viewCornerRadius, y: viewCornerRadius + maskTop),
+                            radius: viewCornerRadius,
+                            startAngle: degreesToRadius(180),
+                            endAngle: degreesToRadius(270),
+                            clockwise: true)
+        }
+        // 箭头向上时的箭头位置
+        if isUpward {
+           let upX = edgeAlignment && (isLeft || isRight) ? arrowPoint.x : arrowPoint.x - arrowWidth/2
+           maskPath.addLine(to: CGPoint(x: upX, y: arrowHeight))
+            
+           if edgeAlignment && (isLeft || isRight) {
+            
+              maskPath.addLine(to: arrowPoint)
+              maskPath.addLine(to: CGPoint(x: arrowPoint.x + (isLeft ? arrowWidth/2 : -arrowWidth/2), y: arrowHeight))
+            
+           }else{
+              maskPath.addQuadCurve(to: CGPoint(x: arrowPoint.x - arrowCornerRadius, y: arrowCornerRadius),
+                                  controlPoint: CGPoint(x: arrowPoint.x - arrowWidth/2 + arrowBottomCornerRadius, y: arrowHeight))
+              maskPath.addQuadCurve(to: CGPoint(x: arrowPoint.x + arrowCornerRadius, y: arrowCornerRadius),
+                                  controlPoint: arrowPoint)
+            
+              maskPath.addQuadCurve(to: CGPoint(x: arrowPoint.x + arrowWidth/2, y: arrowHeight),
+                                  controlPoint: CGPoint(x: arrowPoint.x + arrowWidth/2 - arrowBottomCornerRadius, y: arrowHeight))
+           }
+        }
+        
+        if edgeAlignment && isRight && isUpward {
+            
+            maskPath.addLine(to: CGPoint(x: currentW, y: maskTop))
+        
+        }else{
+            //右上圆角
+            maskPath.addLine(to: CGPoint(x: currentW - viewCornerRadius, y: maskTop))
+            maskPath.addArc(withCenter: CGPoint(x: currentW - viewCornerRadius, y: viewCornerRadius + maskTop),
+                            radius: viewCornerRadius,
+                            startAngle: degreesToRadius(270),
+                            endAngle: degreesToRadius(0),
+                            clockwise: true)
+        }
+        
+        if edgeAlignment && isRight && !isUpward {
+            
+            maskPath.addLine(to: CGPoint(x: currentW, y: currentH - arrowHeight))
+            
+        }else{
+            //右下圆角
+            maskPath.addLine(to: CGPoint(x: currentW, y: maskBottom - viewCornerRadius))
+            maskPath.addArc(withCenter: CGPoint(x: currentW - viewCornerRadius, y: maskBottom - viewCornerRadius),
+                            radius: viewCornerRadius,
+                            startAngle: degreesToRadius(0),
+                            endAngle: degreesToRadius(90),
+                            clockwise: true)
+        }
+        // 箭头向下时的箭头位置
+        if !isUpward {
+           let downX = edgeAlignment && (isLeft || isRight) ? arrowPoint.x : arrowPoint.x + arrowWidth/2
+           maskPath.addLine(to: CGPoint(x: downX, y: currentH - arrowHeight))
+            
+           if edgeAlignment && (isLeft || isRight) {
+                
+              maskPath.addLine(to: arrowPoint)
+              maskPath.addLine(to: CGPoint(x: arrowPoint.x - (isLeft ? -arrowWidth/2 : arrowWidth/2), y: currentH - arrowHeight))
+                
+           }else{
+            
+              maskPath.addQuadCurve(to: CGPoint(x: arrowPoint.x + arrowCornerRadius, y: currentH - arrowCornerRadius),
+                                  controlPoint: CGPoint(x: arrowPoint.x + arrowWidth/2 - arrowBottomCornerRadius, y: currentH - arrowHeight))
+              maskPath.addQuadCurve(to: CGPoint(x: arrowPoint.x - arrowCornerRadius, y: currentH - arrowCornerRadius),
+                                  controlPoint: arrowPoint)
+            
+              maskPath.addQuadCurve(to: CGPoint(x: arrowPoint.x - arrowWidth/2, y: currentH - arrowHeight),
+                                  controlPoint: CGPoint(x: arrowPoint.x - arrowWidth/2 + arrowBottomCornerRadius, y: currentH - arrowHeight))
+           }
+        }
+        
+        if edgeAlignment && isLeft && !isUpward {
+            
+            maskPath.addLine(to: CGPoint(x: 0, y: currentH - arrowHeight))
+            
+        }else{
+            //左下圆角
+            maskPath.addLine(to: CGPoint(x: viewCornerRadius, y: maskBottom))
+            maskPath.addArc(withCenter: CGPoint(x:viewCornerRadius, y: maskBottom - viewCornerRadius),
+                            radius: viewCornerRadius,
+                            startAngle: degreesToRadius(90),
+                            endAngle: degreesToRadius(180),
+                            clockwise: true)
+        }
+        maskPath.close()
+        // 截取圆角和箭头
+        let maskLayer = CAShapeLayer()
+        maskLayer.frame = self.bounds
+        maskLayer.path = maskPath.cgPath
+        self.layer.mask = maskLayer
+        
+        keyWindow!.addSubview(self)
+        
+        //弹出动画
+        let oldFrame = self.frame
+        self.layer.anchorPoint = CGPoint(x: arrowPoint.x/currentW, y: isUpward ? 0 : 1)
+        self.frame = oldFrame
+        self.transform = CGAffineTransform(scaleX: settings.animation.scale.width, y: settings.animation.scale.height)
+        
+        UIView.animate(withDuration: settings.animation.duration, animations: {
+            
+            self.transform = CGAffineTransform.identity
+            self.backgroundView.alpha = 1
+            
+        }) { (_) in
+            
+            if let completion = completion {
+                completion()
+            }
+        }
+    }
+
+    private func degreesToRadius(_ angle: CGFloat) -> CGFloat {
+        return angle * CGFloat.pi / 180
+    }
+    
+    //MARK: - UITableViewDataSources
+    func numberOfSections(in tableView: UITableView) -> Int {
+        return numberOfSections()
+    }
+    
+    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
+        return _sections[section].actions.count
+    }
+    
+    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
+        
+        let action = actionForIndexPath(indexPath)
+        
+        let cell = tableView.dequeueReusableCell(withIdentifier: ReusableViewIds.Cell.rawValue, for: indexPath) as? ActionViewType
+        
+        self.onConfigureCellForAction?(cell!, action!, indexPath)
+        
+        return cell!
+    }
+    
+    func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
+        
+        if section == 0 && hasHeader() {
+            return nil
+        }else{
+            let reusableView = tableView.dequeueReusableHeaderFooterView(withIdentifier: ReusableViewIds.SectionHeader.rawValue) as? SectionHeaderViewType
+            onConfigureSectionHeader?(reusableView!, sectionForIndex(section)!.data!)
+            
+            return reusableView
+        }
+    }
+    
+    func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
+        
+        if section == 0 {
+            if let headerData = headerData, let headerSpec = headerSpec {
+                return headerSpec.height(headerData)
+            }else if let sectionHeaderSpec = sectionHeaderSpec, let section = sectionForIndex(actionSectionIndexFor(section)), let sectionData = section.data {
+                return sectionHeaderSpec.height(sectionData)
+            }
+        }else if let sectionHeaderSpec = sectionHeaderSpec, let section = sectionForIndex(actionSectionIndexFor(section)), let sectionData = section.data {
+              return  sectionHeaderSpec.height(sectionData)
+        }
+        return 0
+    }
+    
+    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
+        
+        let action = actionForIndexPath(indexPath)
+        
+        if let actionData = action?.data,
+           let cellSpec = self.cellSpec {
+            
+            return cellSpec.height(actionData)
+        }
+        return 0
+    }
+    
+    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
+        
+        let action = actionForIndexPath(indexPath)!
+    
+        self.dismiss(isNeedAnimation: settings.animation.tapShouldAnimated) {
+            action.handler?(action)
+        }
+    }
+}
+
+
+

+ 50 - 0
JiaPeiManage/Sources/Custom/View/PopOverView/PopOverViewCell.swift

@@ -0,0 +1,50 @@
+//
+//  PopOverViewCell.swift
+//  SwiftBilibili
+//
+//  Created by 罗文 on 2021/2/2.
+//  Copyright © 2021年 罗文. All rights reserved.
+//
+
+import UIKit
+
+class PopOverViewCell: UITableViewCell {
+    
+    @IBOutlet weak var actionImageView: UIImageView!
+    
+    @IBOutlet weak var actionTitleLabel: UILabel!
+    
+    @IBOutlet weak var separatorView: UIView!
+    
+    @IBOutlet weak var imageViewWidthConstraint: NSLayoutConstraint!
+    
+    @IBOutlet weak var titleLeftConstraint: NSLayoutConstraint!
+    var titleLeft: CGFloat = 0
+    
+    override func awakeFromNib() {
+        super.awakeFromNib()
+    
+        titleLeft = titleLeftConstraint.constant
+    
+        backgroundColor = .white
+        let backgroundView = UIView()
+        backgroundView.backgroundColor = UIColor(white: 0.9, alpha: 1.0)
+        selectedBackgroundView = backgroundView
+        
+    }
+
+    func reloadData(title:String?,image:UIImage?) {
+        
+        actionTitleLabel.text = title
+        actionImageView.image = image
+        
+        titleLeftConstraint.constant = image == nil ? 20 : titleLeft
+        
+        actionTitleLabel.textAlignment = image == nil ? .center : .left
+        
+        setNeedsLayout()
+    }
+
+    
+    
+}

+ 62 - 0
JiaPeiManage/Sources/Custom/View/PopOverView/PopOverViewCell.xib

@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="13771" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
+    <device id="retina4_7" orientation="portrait">
+        <adaptation id="fullscreen"/>
+    </device>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13772"/>
+        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <objects>
+        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
+        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
+        <view contentMode="scaleToFill" id="59d-pq-rUc" customClass="PopOverViewCell" customModule="SwiftBilibili" customModuleProvider="target">
+            <rect key="frame" x="0.0" y="0.0" width="510" height="122"/>
+            <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+            <subviews>
+                <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="RPk-P8-Z4n">
+                    <rect key="frame" x="20" y="46" width="30" height="30"/>
+                    <constraints>
+                        <constraint firstAttribute="width" constant="30" id="5EV-Z8-Unu"/>
+                        <constraint firstAttribute="height" constant="30" id="PDb-fV-vYD"/>
+                    </constraints>
+                </imageView>
+                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="使用小窗播放" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="KvS-6P-zvN">
+                    <rect key="frame" x="70" y="52" width="420" height="19.5"/>
+                    <fontDescription key="fontDescription" type="system" pointSize="16"/>
+                    <nil key="highlightedColor"/>
+                </label>
+                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="z1P-GO-1Ec">
+                    <rect key="frame" x="0.0" y="121.5" width="510" height="0.5"/>
+                    <color key="backgroundColor" red="0.82352941176470584" green="0.82352941176470584" blue="0.82352941176470584" alpha="1" colorSpace="calibratedRGB"/>
+                    <constraints>
+                        <constraint firstAttribute="height" constant="0.5" id="lXt-GQ-fM1"/>
+                    </constraints>
+                </view>
+            </subviews>
+            <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+            <constraints>
+                <constraint firstItem="mw0-Sf-kbi" firstAttribute="trailing" secondItem="KvS-6P-zvN" secondAttribute="trailing" constant="20" id="3lh-oR-zVk"/>
+                <constraint firstAttribute="bottom" secondItem="z1P-GO-1Ec" secondAttribute="bottom" id="47e-be-si9"/>
+                <constraint firstItem="RPk-P8-Z4n" firstAttribute="centerY" secondItem="mw0-Sf-kbi" secondAttribute="centerY" id="54i-xl-bR9"/>
+                <constraint firstItem="KvS-6P-zvN" firstAttribute="centerY" secondItem="mw0-Sf-kbi" secondAttribute="centerY" id="9mE-tA-Na3"/>
+                <constraint firstItem="z1P-GO-1Ec" firstAttribute="leading" secondItem="mw0-Sf-kbi" secondAttribute="leading" id="ADZ-gk-zHt"/>
+                <constraint firstItem="KvS-6P-zvN" firstAttribute="leading" secondItem="59d-pq-rUc" secondAttribute="leading" constant="70" id="Vok-Af-n3x"/>
+                <constraint firstItem="mw0-Sf-kbi" firstAttribute="trailing" secondItem="z1P-GO-1Ec" secondAttribute="trailing" id="a7Y-fJ-EOh"/>
+                <constraint firstItem="RPk-P8-Z4n" firstAttribute="leading" secondItem="mw0-Sf-kbi" secondAttribute="leading" constant="20" id="nul-HL-wtG"/>
+            </constraints>
+            <freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
+            <viewLayoutGuide key="safeArea" id="mw0-Sf-kbi"/>
+            <connections>
+                <outlet property="actionImageView" destination="RPk-P8-Z4n" id="bma-5l-LyA"/>
+                <outlet property="actionTitleLabel" destination="KvS-6P-zvN" id="ZP7-PA-UfV"/>
+                <outlet property="imageViewWidthConstraint" destination="5EV-Z8-Unu" id="4J8-mN-5fu"/>
+                <outlet property="separatorView" destination="z1P-GO-1Ec" id="527-Ju-O49"/>
+                <outlet property="titleLeftConstraint" destination="Vok-Af-n3x" id="iu3-za-wfI"/>
+            </connections>
+            <point key="canvasLocation" x="-19" y="42"/>
+        </view>
+    </objects>
+</document>

+ 77 - 0
JiaPeiManage/Sources/Custom/View/PopOverView/PopOverViewSettings.swift

@@ -0,0 +1,77 @@
+//
+//  PopOverViewSettings.swift
+//  SwiftBilibili
+//
+//  Created by 罗文 on 2021/1/24.
+//  Copyright © 2021年 罗文. All rights reserved.
+//
+
+import UIKit
+
+struct PopOverViewSettings {
+
+    struct Behavior {
+        
+        /** 点击是否隐藏  默认是true **/
+        var hideOnTap = true
+        /** 是否允许滚动 默认是false  **/
+        var scrollEnable = false
+        /** 是否具有弹性  默认是false **/
+        var bounces = false
+    }
+    
+    struct ArrowViewStyle {
+        /** 边缘是否对齐  默认是false **/
+        var edgeAlignment = false
+    
+        var height = CGFloat(15.0)
+        
+        var width = CGFloat(22.0)
+        /** 箭头距离目标的偏移  默认是5.0 **/
+        var targetOffest = CGFloat(5.0)
+        
+        var igoreOffest = CGFloat(44)
+        
+        var upOffest = CGFloat(170)
+        
+        var arrowCornerRadius = CGFloat(0)
+        
+        var arrowBottomCornerRadius = CGFloat(4.0)
+    }
+
+    struct OverViewStyle {
+        
+        var coverViewColor = UIColor.black.withAlphaComponent(0.05)
+        
+        var backgroundColor = UIColor.white
+        
+        var horizontalMargin = CGFloat(10.0)
+        
+        var verticalMargin = CGFloat(30.0)
+        
+        var viewCornerRadius = CGFloat(6.0)
+        
+        var viewWidth = CGFloat(150.0)
+    }
+    
+    struct AnimationStyle {
+        
+        var scale: CGSize = CGSize(width: 0.01, height: 0.01)
+        
+        var duration = TimeInterval(0.25)
+        
+        var tapShouldAnimated = false
+        
+    }
+    
+    var behavior = Behavior()
+    var arrowView = ArrowViewStyle()
+    var overView = OverViewStyle()
+    var animation = AnimationStyle()
+    
+    static func defaultSettings() -> PopOverViewSettings {
+        
+       return PopOverViewSettings()
+        
+    }
+}

+ 17 - 0
JiaPeiManage/Sources/Custom/View/SearchBarView/SearchBarView.swift

@@ -0,0 +1,17 @@
+//
+//  SearchBarView.swift
+//  SwiftBilibili
+//
+//  Created by 罗文 on 2021/3/1.
+//  Copyright © 2021年 罗文. All rights reserved.
+//
+
+import UIKit
+
+final class SearchBarView: UIView,NibLoadable {
+
+    
+    
+    
+
+}

+ 47 - 0
JiaPeiManage/Sources/Custom/View/SearchBarView/SearchBarView.xib

@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="13771" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
+    <device id="retina4_7" orientation="portrait">
+        <adaptation id="fullscreen"/>
+    </device>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13772"/>
+        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <objects>
+        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
+        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
+        <view contentMode="scaleToFill" id="w4a-Xz-Bef" customClass="SearchBarView" customModule="SwiftBilibili" customModuleProvider="target">
+            <rect key="frame" x="0.0" y="0.0" width="523" height="74"/>
+            <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+            <subviews>
+                <button opaque="NO" userInteractionEnabled="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="m5a-bt-tYE">
+                    <rect key="frame" x="10" y="29" width="503" height="16"/>
+                    <fontDescription key="fontDescription" type="system" pointSize="13"/>
+                    <inset key="titleEdgeInsets" minX="10" minY="0.0" maxX="0.0" maxY="0.0"/>
+                    <state key="normal" title="搜索房间或主播" image="home_search_live">
+                        <color key="titleColor" white="0.33333333329999998" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                    </state>
+                </button>
+            </subviews>
+            <color key="backgroundColor" red="0.90196078430000004" green="0.90196078430000004" blue="0.90196078430000004" alpha="1" colorSpace="calibratedRGB"/>
+            <constraints>
+                <constraint firstItem="oSi-6a-hLC" firstAttribute="trailing" secondItem="m5a-bt-tYE" secondAttribute="trailing" constant="10" id="Pt6-f0-RBQ"/>
+                <constraint firstItem="m5a-bt-tYE" firstAttribute="leading" secondItem="oSi-6a-hLC" secondAttribute="leading" constant="10" id="USb-C5-OBt"/>
+                <constraint firstItem="m5a-bt-tYE" firstAttribute="centerY" secondItem="oSi-6a-hLC" secondAttribute="centerY" id="wbQ-YH-k6T"/>
+            </constraints>
+            <freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
+            <viewLayoutGuide key="safeArea" id="oSi-6a-hLC"/>
+            <userDefinedRuntimeAttributes>
+                <userDefinedRuntimeAttribute type="number" keyPath="cornerRadius">
+                    <real key="value" value="6"/>
+                </userDefinedRuntimeAttribute>
+            </userDefinedRuntimeAttributes>
+            <point key="canvasLocation" x="36.5" y="-302"/>
+        </view>
+    </objects>
+    <resources>
+        <image name="home_search_live" width="16" height="16"/>
+    </resources>
+</document>

+ 26 - 0
JiaPeiManage/Sources/Custom/View/Toaster/NYSwToaster.swift

@@ -0,0 +1,26 @@
+//
+//  NYSwToaster.swift
+//  JSJP_Student_sw
+//
+//  Created by Ning.ge on 2023/5/30.
+//
+
+import UIKit
+
+import Toaster
+
+final class NYSwToaster {
+
+   static func show(_ text: String,
+                    _ delay: TimeInterval = 0,
+                    _ duration: TimeInterval = 2,
+                    bottomOffsetPortrait: CGFloat = kToastBottomMaxSpace) {
+    
+        ToastView.appearance().bottomOffsetPortrait = bottomOffsetPortrait
+    
+        if ToastCenter.default.currentToast == nil {
+            
+           Toast(text: text, delay: delay, duration: duration).show()
+        }
+    }
+}

+ 46 - 0
JiaPeiManage/Sources/Extensions/Array+SectionModel.swift

@@ -0,0 +1,46 @@
+//
+//  Array+SectionModel.swift
+//  SwiftBilibili
+//
+//  Created by 罗文 on 2021/1/13.
+//  Copyright © 2021年 罗文. All rights reserved.
+//
+
+import RxDataSources
+
+extension Array where Element: SectionModelType {
+    subscript(indexPath: IndexPath) -> Element.Item {
+        get {
+            return self[indexPath.section].items[indexPath.item]
+        }
+        mutating set {
+            self.update(section: indexPath.section) { items in
+                items[indexPath.item] = newValue
+            }
+        }
+    }
+    
+    mutating func insert(newElement: Element.Item, at indexPath: IndexPath) {
+        self.update(section: indexPath.section) { items in
+            items.insert(newElement, at: indexPath.item)
+        }
+    }
+    
+    @discardableResult
+    mutating func remove(at indexPath: IndexPath) -> Element.Item {
+        return self.update(section: indexPath.section) { items in
+            return items.remove(at: indexPath.item)
+        }
+    }
+    
+    mutating func replace(section: Int, items: [Element.Item]) {
+        self[section] = Element.init(original: self[section], items: items)
+    }
+    
+    private mutating func update<T>(section: Int, mutate: (inout [Element.Item]) -> T) -> T {
+        var items = self[section].items
+        let value = mutate(&items)
+        self[section] = Element.init(original: self[section], items: items)
+        return value
+    }
+}

+ 29 - 0
JiaPeiManage/Sources/Extensions/Data+Cache.swift

@@ -0,0 +1,29 @@
+//
+//  Data+Cache.swift
+//  SwiftBilibili
+//
+//  Created by 罗文 on 2021/1/22.
+//  Copyright © 2021年 罗文. All rights reserved.
+//
+
+import ObjectMapper
+import SwiftyJSON
+
+extension Data {
+    
+    func cacheObject<T: ImmutableMappable>(_ type: T.Type) -> T {
+        
+        let json = try! JSON(data: self)
+        let data = json[RESULT_DATA].dictionaryObject
+        return try! Mapper<T>().map(JSON: data!)
+    }
+    
+    func cacheArray<T: ImmutableMappable>(_ type: T.Type) -> [T] {
+    
+        let jsonArray = try! JSON(data: self).arrayObject
+        let data = try! JSONSerialization.data(withJSONObject: jsonArray!, options: JSONSerialization.WritingOptions.prettyPrinted)
+        let jsonString = String(data: data, encoding: String.Encoding.utf8)!
+        
+        return try! Mapper<T>().mapArray(JSONString: jsonString)
+    }
+}

+ 26 - 0
JiaPeiManage/Sources/Extensions/DefaultsKeys+Key.swift

@@ -0,0 +1,26 @@
+//
+//  DefaultsKeys+Key.swift
+//  SwiftBilibili
+//
+//  Created by 罗文 on 2021/1/16.
+//  Copyright © 2021年 罗文. All rights reserved.
+//
+
+import SwiftyUserDefaults
+
+extension DefaultsKeys {
+    
+    var openTimes: DefaultsKey<Int> { .init("openTimes", defaultValue: 0) }
+
+    var avIdx: DefaultsKey<String?> { .init("avIdx", defaultValue:"0") }
+    
+    var isLogin: DefaultsKey<Bool> { .init("isLogin", defaultValue:false) }
+    
+    var avater: DefaultsKey<Data> { .init("avater", defaultValue: Data()) }
+    
+    var userAccount:DefaultsKey<String?> { .init("userAccount", defaultValue:"") }
+    
+    var userPassword:DefaultsKey<String?> { .init("userPassword", defaultValue:"") }
+    
+    var currentEnvironment: DefaultsKey<NetEnvironment> { .init("currentEnvironment", defaultValue: .res) }
+}

+ 43 - 0
JiaPeiManage/Sources/Extensions/DispatchQueue+Extension.swift

@@ -0,0 +1,43 @@
+//
+//  DispatchQueue+Extension.swift
+//  SwiftBilibili
+//
+//  Created by 罗文 on 2021/1/15.
+//  Copyright © 2021年 罗文. All rights reserved.
+//
+
+import Foundation
+
+extension DispatchQueue {
+    
+    static var `default`: DispatchQueue { return DispatchQueue.global(qos: .`default`) }
+    static var userInteractive: DispatchQueue { return DispatchQueue.global(qos: .userInteractive) }
+    static var userInitiated: DispatchQueue { return DispatchQueue.global(qos: .userInitiated) }
+    static var utility: DispatchQueue { return DispatchQueue.global(qos: .utility) }
+    static var background: DispatchQueue { return DispatchQueue.global(qos: .background) }
+    
+    
+    class func delay(time:Double,action:@escaping ()->()) {
+        
+        let when = DispatchTime.now() + time
+        
+        DispatchQueue.main.asyncAfter(deadline: when) {
+            action()
+        }
+    }
+    
+    private static var _onceTracker = [String]()
+    class func once(_ token: String, block:()->Void) {
+        objc_sync_enter(self)
+        defer { objc_sync_exit(self) }
+        
+        if _onceTracker.contains(token) {
+            return
+        }
+        _onceTracker.append(token)
+        block()
+    }
+    
+
+    
+}

+ 15 - 0
JiaPeiManage/Sources/Extensions/NSAttributedString+BoundingRect.swift

@@ -0,0 +1,15 @@
+//
+//  NSAttributedString+BoundingRect.swift
+//  SwiftBilibili
+//
+//  Created by 罗文 on 2021/1/13.
+//  Copyright © 2021年 罗文. All rights reserved.
+//
+
+import YYText
+
+extension NSAttributedString {
+    
+    
+    
+}

+ 38 - 0
JiaPeiManage/Sources/Extensions/String+BoundingRect.swift

@@ -0,0 +1,38 @@
+//
+//  String+BoundingRect.swift
+//  SwiftBilibili
+//
+//  Created by 罗文 on 2021/1/13.
+//  Copyright © 2021年 罗文. All rights reserved.
+//
+
+import UIKit
+
+extension String {
+    
+    func boundingRect(with size: CGSize, attributes: [NSAttributedString.Key: Any]) -> CGRect {
+        let options: NSStringDrawingOptions = [.usesLineFragmentOrigin, .usesFontLeading]
+        let rect = self.boundingRect(with: size, options: options, attributes: attributes, context: nil)
+        return snap(rect)
+    }
+    
+    func size(thatFits size: CGSize, font: UIFont, maximumNumberOfLines: Int = 0) -> CGSize {
+        let attributes: [NSAttributedString.Key: Any] = [.font: font]
+        var size = self.boundingRect(with: size, attributes: attributes).size
+        if maximumNumberOfLines > 0 {
+            size.height = min(size.height, CGFloat(maximumNumberOfLines) * font.lineHeight)
+        }
+        return size
+    }
+    
+    func width(with font: UIFont, maximumNumberOfLines: Int = 0) -> CGFloat {
+        let size = CGSize(width: CGFloat.greatestFiniteMagnitude, height: CGFloat.greatestFiniteMagnitude)
+        return self.size(thatFits: size, font: font, maximumNumberOfLines: maximumNumberOfLines).width
+    }
+    
+    func height(thatFitsWidth width: CGFloat, font: UIFont, maximumNumberOfLines: Int = 0) -> CGFloat {
+        let size = CGSize(width: width, height: CGFloat.greatestFiniteMagnitude)
+        return self.size(thatFits: size, font: font, maximumNumberOfLines: maximumNumberOfLines).height
+    }
+    
+}

+ 184 - 0
JiaPeiManage/Sources/Extensions/UIBarButtonItem+FixSpace.swift

@@ -0,0 +1,184 @@
+//
+//  UIBarButtonItem+FixSpace.swift
+//  SwiftBilibili
+//
+//  Created by 罗文 on 2021/3/9.
+//  Copyright © 2021年 罗文. All rights reserved.
+//
+
+import Foundation
+import UIKit
+
+extension NSObject {
+    static func swizzleMethod(_ cls: AnyClass, originalSelector: Selector, swizzleSelector: Selector){
+        
+        let originalMethod = class_getInstanceMethod(cls, originalSelector)!
+        let swizzledMethod = class_getInstanceMethod(cls, swizzleSelector)!
+        let didAddMethod = class_addMethod(cls,
+                                           originalSelector,
+                                           method_getImplementation(swizzledMethod),
+                                           method_getTypeEncoding(swizzledMethod))
+        if didAddMethod {
+            class_replaceMethod(cls,
+                                swizzleSelector,
+                                method_getImplementation(originalMethod),
+                                method_getTypeEncoding(originalMethod))
+        } else {
+            method_exchangeImplementations(originalMethod,
+                                           swizzledMethod)
+        }
+    }
+}
+
+
+//extension UIApplication {
+//    private static let classSwizzedMethod: Void = {
+//        UINavigationController.sx_initialize
+//        if #available(iOS 11.0, *) {
+//            UINavigationBar.sx_initialize
+//        }
+//    }()
+//    
+//    open override var next: UIResponder? {
+//        UIApplication.classSwizzedMethod
+//        return super.next
+//    }
+//}
+var sx_defultFixSpace: CGFloat = 5
+var sx_disableFixSpace: Bool = false
+
+extension UINavigationController {
+    
+    private struct AssociatedKeys {
+        static var tempDisableFixSpace = "tempDisableFixSpace"
+        static var tempBehavor = "tempBehavor"
+    }
+    
+    static let sx_initialize: Void = {
+        DispatchQueue.once(UUID().uuidString) {
+            
+            swizzleMethod(UINavigationController.self,
+                          originalSelector: #selector(UINavigationController.viewDidLoad),
+                          swizzleSelector: #selector(UINavigationController.sx_viewDidLoad))
+            
+            swizzleMethod(UINavigationController.self,
+                          originalSelector: #selector(UINavigationController.viewWillAppear(_:)),
+                          swizzleSelector: #selector(UINavigationController.sx_viewWillAppear(_:)))
+            
+            swizzleMethod(UINavigationController.self,
+                          originalSelector: #selector(UINavigationController.viewWillDisappear(_:)),
+                          swizzleSelector: #selector(UINavigationController.sx_viewWillDisappear(_:)))
+            
+        }
+    }()
+    
+    private var tempDisableFixSpace: Bool {
+        get {
+            return objc_getAssociatedObject(self, &AssociatedKeys.tempDisableFixSpace) as? Bool ?? false
+        }
+        set {
+            objc_setAssociatedObject(self, &AssociatedKeys.tempDisableFixSpace, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
+        }
+    }
+    
+    @available(iOS 11.0, *)
+    private var tempBehavor: UIScrollView.ContentInsetAdjustmentBehavior {
+        get {
+            return objc_getAssociatedObject(self, &AssociatedKeys.tempBehavor) as? UIScrollView.ContentInsetAdjustmentBehavior ?? .automatic
+        }
+        set {
+            objc_setAssociatedObject(self, &AssociatedKeys.tempBehavor, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
+        }
+    }
+    
+    @objc private func sx_viewDidLoad() {
+        disableFixSpace(true, with: true)
+        sx_viewDidLoad()
+    }
+    
+    @objc private func sx_viewWillAppear(_ animated: Bool) {
+        disableFixSpace(true, with: false)
+        sx_viewWillAppear(animated)
+    }
+    
+    @objc private func sx_viewWillDisappear(_ animated: Bool) {
+        disableFixSpace(false, with: true)
+        sx_viewWillDisappear(animated)
+    }
+    
+    private func disableFixSpace(_ disable: Bool, with temp: Bool) {
+        if type(of: self) == UIImagePickerController.self {
+            if disable == true {
+                if temp { tempDisableFixSpace = sx_disableFixSpace }
+                sx_disableFixSpace = true
+                if #available(iOS 11.0, *) {
+                    tempBehavor = UIScrollView.appearance().contentInsetAdjustmentBehavior
+                    UIScrollView.appearance().contentInsetAdjustmentBehavior = .automatic
+                }
+            } else {
+                sx_disableFixSpace = tempDisableFixSpace
+                if #available(iOS 11.0, *) {
+                    UIScrollView.appearance().contentInsetAdjustmentBehavior = tempBehavor
+                }
+            }
+        }
+    }
+}
+@available(iOS 11.0, *)
+extension UINavigationBar {
+    
+    static let sx_initialize: Void = {
+        DispatchQueue.once(UUID().uuidString) {
+            swizzleMethod(UINavigationBar.self,
+                          originalSelector: #selector(UINavigationBar.layoutSubviews),
+                          swizzleSelector: #selector(UINavigationBar.sx_layoutSubviews))
+            
+        }
+    }()
+    
+    @objc func sx_layoutSubviews() {
+        sx_layoutSubviews()
+        
+        if sx_disableFixSpace == false {
+            layoutMargins = .zero
+            let space = sx_defultFixSpace
+            for view in subviews {
+                if NSStringFromClass(view.classForCoder).contains("ContentView") {
+                    view.layoutMargins = UIEdgeInsets(top: 0, left: space, bottom: 0, right: space)
+                }
+            }
+        }
+    }
+}
+
+extension UINavigationItem {
+    
+    private enum BarButtonItem: String {
+        case left = "_leftBarButtonItem"
+        case right = "_rightBarButtonItem"
+    }
+    
+    open override func setValue(_ value: Any?, forKey key: String) {
+        
+        if #available(iOS 11.0, *) {
+            super.setValue(value, forKey: key)
+        } else {
+            if sx_disableFixSpace == false && (key == BarButtonItem.left.rawValue || key == BarButtonItem.right.rawValue) {
+                guard let item = value as? UIBarButtonItem else {
+                    super.setValue(value, forKey: key)
+                    return
+                }
+                let space = UIBarButtonItem(barButtonSystemItem: .fixedSpace, target: nil, action: nil)
+                space.width = sx_defultFixSpace - 16
+                
+                if key == BarButtonItem.left.rawValue {
+                    leftBarButtonItems = [space, item]
+                } else {
+                    rightBarButtonItems = [space, item]
+                }
+            } else {
+                super.setValue(value, forKey: key)
+            }
+        }
+    }
+}

+ 60 - 0
JiaPeiManage/Sources/Extensions/UIBarButtonItem+Init.swift

@@ -0,0 +1,60 @@
+//
+//  UIBarButtonItem+Init.swift
+//  SwiftBilibili
+//
+//  Created by 罗文 on 2021/3/9.
+//  Copyright © 2021年 罗文. All rights reserved.
+//
+
+import Foundation
+
+import UIKit
+
+extension UIBarButtonItem {
+    convenience init(title: String?,
+                     titleColor: UIColor = .white,
+                     titleFont: UIFont = UIFont.systemFont(ofSize: 15),
+                     titleEdgeInsets: UIEdgeInsets = .zero,
+                     target: Any?,
+                     action: Selector?) {
+        
+        let button = UIButton(type: .custom)
+        button.setTitle(title, for: .normal)
+        button.setTitleColor(titleColor, for: .normal)
+        button.titleLabel?.font = titleFont
+        button.titleEdgeInsets = titleEdgeInsets
+        if action != nil {
+            button.addTarget(target, action: action!, for: .touchUpInside)
+        }
+        button.bounds = CGRect(x: 0, y: 0, width: 40, height: 40)
+//        button.sizeToFit()
+//        if button.bounds.width < 40 || button.bounds.height > 40 {
+//            let width = 40 / button.bounds.height * button.bounds.width;
+//            button.bounds = CGRect(x: 0, y: 0, width: width, height: 40)
+//        }
+        self.init(customView: button)
+    }
+    
+    convenience init(image: UIImage?,
+                     selectImage: UIImage? = nil,
+                     imageEdgeInsets: UIEdgeInsets = .zero,
+                     target: Any?,
+                     action: Selector?) {
+        
+        let button = UIButton(type: .custom)
+        button.setImage(image?.withRenderingMode(.alwaysOriginal), for: .normal)
+        button.setImage(selectImage?.withRenderingMode(.alwaysOriginal), for: .selected)
+        button.imageEdgeInsets = imageEdgeInsets
+        if action != nil {
+            button.addTarget(target, action: action!, for: .touchUpInside)
+        }
+        button.bounds = CGRect(x: 0, y: 0, width: 40, height: 40)
+//        if button.bounds.width < 40 || button.bounds.height > 40 {
+//            let width = 40 / button.bounds.height * button.bounds.width;
+//            button.bounds = CGRect(x: 0, y: 0, width: width, height: 40)
+//        }
+        
+        self.init(customView: button)
+    }
+    
+}

+ 56 - 0
JiaPeiManage/Sources/Extensions/UIButton+Kingfisher.swift

@@ -0,0 +1,56 @@
+//
+//  UIButton+Kingfisher.swift
+//  SwiftBilibili
+//
+//  Created by 罗文 on 2021/7/3.
+//  Copyright © 2021年 罗文. All rights reserved.
+//
+
+import Foundation
+import Kingfisher
+
+extension UIButton {
+    
+    @discardableResult
+    func setImage(
+        with resource: Resource?,
+        for state: UIControl.State,
+        placeholder: UIImage? = nil,
+        options: ImageOptions = [.transition(.fade(0.25))],
+        progress: ((Int64, Int64) -> Void)? = nil,
+        completion: ((ImageResult) -> Void)? = nil
+    ) -> DownloadTask? {
+        return self.kf.setImage(with: resource, for: state, placeholder: placeholder, options: options, progressBlock: progress, completionHandler: { result in
+            switch result {
+            case .success(let value):
+                print("Task done for: \(value.source.url?.absoluteString ?? "")")
+                completion?(.success(value.image))
+            case .failure(let error):
+                print("Job failed: \(error.localizedDescription)")
+                completion?(.failure(error))
+            }
+        })
+    }
+    
+    @discardableResult
+    func setBackgroundImage(
+        with resource: Resource?,
+        for state: UIControl.State,
+        placeholder: UIImage? = nil,
+        options: ImageOptions = [.transition(.fade(0.25))],
+        progress: ((Int64, Int64) -> Void)? = nil,
+        completion: ((ImageResult) -> Void)? = nil
+    ) -> DownloadTask? {
+        return self.kf.setBackgroundImage(with: resource, for: state, placeholder: placeholder, options: options, progressBlock: progress, completionHandler: { result in
+            switch result {
+            case .success(let value):
+                print("Task done for: \(value.source.url?.absoluteString ?? "")")
+                completion?(.success(value.image))
+            case .failure(let error):
+                print("Job failed: \(error.localizedDescription)")
+                completion?(.failure(error))
+            }
+        })
+    }
+    
+}

+ 27 - 0
JiaPeiManage/Sources/Extensions/UIColor+Bilibili.swift

@@ -0,0 +1,27 @@
+//
+//  UIColor+Bilibili.swift
+//  SwiftBilibili
+//
+//  Created by 罗文 on 2021/1/13.
+//  Copyright © 2021年 罗文. All rights reserved.
+//
+
+import UIKit
+
+extension UIColor {
+    
+    class var db_pink: UIColor { return UIColor(252,132,164) }
+    class var db_white: UIColor { return UIColor(255,255,255) }
+    class var db_gray: UIColor { return UIColor(244,244,244) }
+    class var db_lightGray: UIColor { return UIColor(210,210,210) }
+    class var db_darkGray: UIColor { return UIColor(150,150,150) }
+    class var db_black: UIColor { return UIColor(38,38,38) }
+    class var db_lightBlack: UIColor { return UIColor(63,63,63) }
+    class var db_orange: UIColor { return UIColor(231,157,105) }
+    
+    //新增
+    class var db_fontGray: UIColor { return UIColor(92, 96, 102) }
+    class var db_fontBlack: UIColor { return UIColor(10, 26, 51) }
+    class var db_sliderColor: UIColor { return UIColor(73, 142, 245) }
+    
+}

+ 62 - 0
JiaPeiManage/Sources/Extensions/UIColor+Hex.swift

@@ -0,0 +1,62 @@
+//
+//  UIColor+Hex.swift
+//  SwiftBilibili
+//
+//  Created by 罗文 on 2021/1/14.
+//  Copyright © 2021年 罗文. All rights reserved.
+//
+
+import UIKit
+
+extension UIColor {
+    
+    //在extension中给系统的类扩充构造函数,只能扩充`便利构造函数`
+    convenience init(_ r:CGFloat,_ g:CGFloat,_ b:CGFloat,_ alpha:CGFloat = 1.0) {
+        
+        self.init(red:r/255.0,green:g/255.0,blue:b/255.0,alpha:alpha)
+        
+    }
+    
+    convenience init?(_ hex : String, _ alpha : CGFloat = 1.0) {
+        
+        // 0xff0000
+        // 1.判断字符串的长度是否符合
+        guard hex.count >= 6 else {
+            return nil
+        }
+        
+        // 2.将字符串转成大写
+        var tempHex = hex.uppercased()
+        
+        // 3.判断开头: 0x/#/##
+        if tempHex.hasPrefix("0x") || tempHex.hasPrefix("##") {
+            tempHex = (tempHex as NSString).substring(from: 2)
+        }
+        if tempHex.hasPrefix("#") {
+            tempHex = (tempHex as NSString).substring(from: 1)
+        }
+        
+        // 4.分别取出RGB
+        // FF --> 255
+        var range = NSRange(location: 0, length: 2)
+        let rHex = (tempHex as NSString).substring(with: range)
+        range.location = 2
+        let gHex = (tempHex as NSString).substring(with: range)
+        range.location = 4
+        let bHex = (tempHex as NSString).substring(with: range)
+        
+        // 5.将十六进制转成数字 emoji表情
+        var r : UInt32 = 0, g : UInt32 = 0, b : UInt32 = 0
+        Scanner(string: rHex).scanHexInt32(&r)
+        Scanner(string: gHex).scanHexInt32(&g)
+        Scanner(string: bHex).scanHexInt32(&b)
+        
+        self.init(CGFloat(r), CGFloat(g), CGFloat(b))
+    }
+    
+    class func randomColor() -> UIColor {
+        return UIColor(CGFloat(arc4random_uniform(256)),CGFloat(arc4random_uniform(256)),CGFloat(arc4random_uniform(256)))
+    }
+    
+    
+}

+ 41 - 0
JiaPeiManage/Sources/Extensions/UIImage+Placeholder.swift

@@ -0,0 +1,41 @@
+//
+//  UIImage+Placeholder.swift
+//  SwiftBilibili
+//
+//  Created by 罗文 on 2021/1/19.
+//  Copyright © 2021年 罗文. All rights reserved.
+//
+
+import UIKit
+
+import SwiftyImage
+
+extension UIImage {
+    
+   class func placeholderImage(bgColor:UIColor = .db_gray,bgSize:CGSize) -> UIImage? {
+    
+       if bgSize == .zero {
+          return nil
+       }
+    
+       let bgImage = UIImage.size(bgSize).color(bgColor).image
+       let centerImage = NYImage.Home.default_img!
+       let placeholderImage = bgImage + centerImage
+       return placeholderImage
+   }
+   
+   class func resizeImage(image:UIImage?,newSize:CGSize) -> UIImage? {
+        
+        guard let image = image else {
+            return nil
+        }
+        
+        UIGraphicsBeginImageContext(newSize)
+        image.draw(in: CGRect(x: 0, y: 0, width: newSize.width, height: newSize.height))
+        let newImage = UIGraphicsGetImageFromCurrentImageContext()
+        UIGraphicsEndImageContext()
+        return newImage
+    }
+    
+    
+}

+ 77 - 0
JiaPeiManage/Sources/Extensions/UIImageView+Kingfisher.swift

@@ -0,0 +1,77 @@
+//
+//  UIImageView+Kingfisher.swift
+//  SwiftBilibili
+//
+//  Created by 罗文 on 2021/1/13.
+//  Copyright © 2021年 罗文. All rights reserved.
+//
+
+import Foundation
+
+import Kingfisher
+import RxCocoa
+import RxSwift
+
+typealias ImageOptions = KingfisherOptionsInfo
+
+enum ImageResult {
+    case success(UIImage)
+    case failure(Error)
+    
+    var image: UIImage? {
+        if case .success(let image) = self {
+            return image
+        } else {
+            return nil
+        }
+    }
+    
+    var error: Error? {
+        if case .failure(let error) = self {
+            return error
+        } else {
+            return nil
+        }
+    }
+}
+
+extension UIImageView {
+    @discardableResult
+    func setImage(
+        with resource: Resource?,
+        placeholder: UIImage? = nil,
+        options: ImageOptions = [.transition(.fade(0.25))],
+        progress: ((Int64, Int64) -> Void)? = nil,
+        completion: ((ImageResult) -> Void)? = nil
+    ) -> DownloadTask? {
+        var options = options
+        // GIF will only animates in the AnimatedImageView
+        if self is AnimatedImageView == false {
+            options.append(.onlyLoadFirstFrame)
+        }
+        return self.kf.setImage(
+            with: resource,
+            placeholder: placeholder,
+            options: options,
+            progressBlock: progress,
+            completionHandler: { result in
+                switch result {
+                case .success(let value):
+                    print("Task done for: \(value.source.url?.absoluteString ?? "")")
+                    completion?(.success(value.image))
+                case .failure(let error):
+                    print("Job failed: \(error.localizedDescription)")
+                    completion?(.failure(error))
+                }
+            }
+        )
+    }
+}
+
+extension Reactive where Base: UIImageView {
+    func image(placeholder: UIImage? = nil, options: ImageOptions = [.transition(.fade(0.25))]) -> Binder<Resource?> {
+        return Binder(self.base) { imageView, resource in
+            imageView.setImage(with: resource, placeholder: placeholder, options: options)
+        }
+    }
+}

+ 66 - 0
JiaPeiManage/Sources/Extensions/UIScrollView+Direction.swift

@@ -0,0 +1,66 @@
+//
+//  UIScrollView+Direction.swift
+//  SwiftBilibili
+//
+//  Created by 罗文 on 2021/6/21.
+//  Copyright © 2021年 罗文. All rights reserved.
+//
+
+import UIKit
+import RxSwift
+
+enum ScrollDirection {
+    case none
+    case down
+    case up
+}
+
+extension UIScrollView {
+    
+    private struct AssociatedKeys {
+        static var scrollDirection = "scrollDirection"
+        static var enableDirection = "enableDirection"
+    }
+    
+    
+    var scrollDirection: ScrollDirection {
+        get {
+            return objc_getAssociatedObject(self, &AssociatedKeys.scrollDirection) as? ScrollDirection ?? .none
+        }
+        set {
+            objc_setAssociatedObject(self, &AssociatedKeys.scrollDirection, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
+        }
+    }
+    
+    var enableDirection: Bool {
+        get {
+            return objc_getAssociatedObject(self, &AssociatedKeys.enableDirection) as? Bool ?? false
+        }
+        set {
+            objc_setAssociatedObject(self, &AssociatedKeys.enableDirection, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
+            
+            if enableDirection {
+               self.addObserver(self, forKeyPath: "contentOffest", options: [.old,.new], context: nil)
+            }
+        }
+        
+    }
+    
+    override open func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
+        
+        if keyPath == "contentOffest" {
+            let newOffest = change?[NSKeyValueChangeKey.newKey] as? CGPoint ?? .zero
+            let oldOffest = change?[NSKeyValueChangeKey.oldKey] as? CGPoint ?? .zero
+            
+            if newOffest.y > oldOffest.y {
+                self.scrollDirection = .up
+            }else if newOffest.y < oldOffest.y {
+                self.scrollDirection = .down
+            }
+        }
+        
+
+    }
+}
+
+

+ 30 - 0
JiaPeiManage/Sources/Extensions/UIScrollView+ScrollToBottom.swift

@@ -0,0 +1,30 @@
+//
+//  UIScrollView+ScrollToBottom.swift
+//  SwiftBilibili
+//
+//  Created by 罗文 on 2021/1/14.
+//  Copyright © 2021年 罗文. All rights reserved.
+//
+
+import UIKit
+
+extension UIScrollView {
+    
+    var isOverflowVertical: Bool {
+        return self.contentSize.height > self.frame.height && self.frame.height > 0
+    }
+    
+    func isReachedBottom(withTolerance tolerance: CGFloat = 0) -> Bool {
+        guard self.isOverflowVertical else { return false }
+        let contentOffsetBottom = self.contentOffset.y + self.frame.height
+        return contentOffsetBottom >= self.contentSize.height - tolerance
+    }
+    
+    func scrollToBottom(animated: Bool) {
+        guard self.isOverflowVertical else { return }
+        let targetY = self.contentSize.height + self.contentInset.bottom - self.frame.height
+        let targetOffset = CGPoint(x: 0, y: targetY)
+        self.setContentOffset(targetOffset, animated: true)
+    }
+    
+}

+ 60 - 0
JiaPeiManage/Sources/Extensions/UIView+CornerRadius.swift

@@ -0,0 +1,60 @@
+//
+//  UIView+CornerRadius.swift
+//  SwiftBilibili
+//
+//  Created by 罗文 on 2021/1/17.
+//  Copyright © 2021年 罗文. All rights reserved.
+//
+
+import UIKit
+
+extension UIView {
+    
+    @IBInspectable var cornerRadius: CGFloat {
+        get { return layer.cornerRadius }
+        set {
+            layer.cornerRadius = newValue
+            layer.masksToBounds = newValue > 0
+        }
+    }
+    @IBInspectable var borderWidth: CGFloat {
+        get { return layer.borderWidth }
+        set { layer.borderWidth = newValue }
+    }
+    @IBInspectable var borderColor: UIColor {
+        get { return UIColor(cgColor: layer.borderColor!) }
+        set { layer.borderColor = newValue.cgColor }
+    }
+    
+    func setShadow(shadowOpacity:Float = 0.1,
+                   shadowRadius:CGFloat = kCornerRadius,
+                   shadowOffset:CGSize = .zero,
+                   shadowColor:CGColor = UIColor.db_black.cgColor
+         ) {
+        
+        layer.masksToBounds = false
+        layer.contentsScale = UIScreen.main.scale
+        layer.shadowOpacity = shadowOpacity
+        layer.shadowRadius = shadowRadius
+        layer.shadowOffset = shadowOffset
+        layer.shadowColor = shadowColor
+        layer.shadowPath = UIBezierPath(roundedRect: self.bounds, cornerRadius: shadowRadius).cgPath
+        //设置缓存
+        layer.shouldRasterize = true
+        //设置抗锯齿边缘
+        layer.rasterizationScale = UIScreen.main.scale
+        
+    }
+    
+    func clipRectCorner(direction: UIRectCorner,cornerRadius: CGFloat) {
+        
+        let cornerSize = CGSize(width: cornerRadius, height: cornerRadius)
+        let maskPath = UIBezierPath(roundedRect: bounds, byRoundingCorners: direction, cornerRadii: cornerSize)
+        let maskLayer = CAShapeLayer()
+        maskLayer.frame = bounds
+        maskLayer.path = maskPath.cgPath
+        layer.mask = maskLayer
+        
+    }
+    
+}

+ 41 - 0
JiaPeiManage/Sources/Extensions/UIViewController+NetAnimation.swift

@@ -0,0 +1,41 @@
+//
+//  UIViewController+NetAnimation.swift
+//  SwiftBilibili
+//
+//  Created by 罗文 on 2021/9/4.
+//  Copyright © 2021年 罗文. All rights reserved.
+//
+
+import UIKit
+
+extension UIViewController {
+    
+    func showLoadingAnimation(superView: UIView) {
+        
+        if let existView = superView.subviews.filter({ $0.isKind(of: NetAnimationView.self) }).first as? NetAnimationView {
+            existView.removeFromSuperview()
+        }
+        
+        let animationView = NetAnimationView(animationType: .loading)
+        superView.addSubview(animationView)
+        superView.bringSubviewToFront(animationView)
+        
+        animationView.snp.makeConstraints { (make) in
+            make.width.equalTo(200)
+            make.height.equalTo(220)
+            make.centerX.equalToSuperview()
+            make.centerY.equalToSuperview()
+        }
+        
+        animationView.startAnimation(animationType:.loading)
+    }
+    
+    func hideLoadingAnimation(superView: UIView) {
+        
+        guard let animationView = superView.subviews.filter({ $0.isKind(of: NetAnimationView.self) }).first as? NetAnimationView else { return }
+        
+        animationView.stopAnimation(animationType:.loading)
+        animationView.removeFromSuperview()
+    }
+    
+}

+ 28 - 0
JiaPeiManage/Sources/Extensions/VTContentView+Gesture.swift

@@ -0,0 +1,28 @@
+//
+//  VTContentView+Gesture.swift
+//  LWBilibili
+//
+//  Created by 罗文 on 17/5/11.
+//  Copyright © 2017年 wenhua. All rights reserved.
+//
+
+import VTMagic
+
+//允许边缘侧滑返回
+extension VTContentView:UIGestureRecognizerDelegate {
+    
+    public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
+        
+        return gestureRecognizer is UIPanGestureRecognizer
+            && otherGestureRecognizer is UIScreenEdgePanGestureRecognizer
+        
+    }
+    
+    public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRequireFailureOf otherGestureRecognizer: UIGestureRecognizer) -> Bool {
+        
+        return gestureRecognizer is UIPanGestureRecognizer
+            && otherGestureRecognizer is UIScreenEdgePanGestureRecognizer
+    }
+    
+    
+}

+ 135 - 0
JiaPeiManage/Sources/Logging/Logger.swift

@@ -0,0 +1,135 @@
+//
+//  Logger.swift
+//  SwiftBilibili
+//
+//  Created by 罗文 on 2021/1/13.
+//  Copyright © 2021年 罗文. All rights reserved.
+//
+
+import CocoaLumberjack
+
+extension DDLogFlag {
+    public var level: String {
+        switch self {
+        case DDLogFlag.error: return "❤️ ERROR"
+        case DDLogFlag.warning: return "💛 WARNING"
+        case DDLogFlag.info: return "💙 INFO"
+        case DDLogFlag.debug: return "💚 DEBUG"
+        case DDLogFlag.verbose: return "💜 VERBOSE"
+        default: return "☠️ UNKNOWN"
+        }
+    }
+}
+
+private class LogFormatter: NSObject, DDLogFormatter {
+    
+    static let dateFormatter = DateFormatter().then {
+        $0.dateFormat = "yyyy-MM-dd HH:mm:ss.SSS"
+    }
+    
+    public func format(message logMessage: DDLogMessage) -> String? {
+        let timestamp = LogFormatter.dateFormatter.string(from: logMessage.timestamp)
+        let level = logMessage.flag.level
+        let filename = logMessage.fileName
+        let function = logMessage.function ?? ""
+        let line = logMessage.line
+        let message = logMessage.message.components(separatedBy: "\n").joined(separator: "\n    ")
+        return "\(timestamp) \(level) \(filename).\(function):\(line) - \(message)"
+    }
+    
+    private func formattedDate(from date: Date) -> String {
+        return LogFormatter.dateFormatter.string(from: date)
+    }
+    
+}
+
+/// A shared instance of `Logger`.
+let log = Logger()
+
+final class Logger {
+    
+    // MARK: Initialize
+    
+    init() {
+        setenv("XcodeColors", "YES", 0)
+        
+        // TTY = Xcode console
+        DDTTYLogger.sharedInstance?.do {
+            $0.logFormatter = LogFormatter()
+            $0.colorsEnabled = false /*true*/ // Note: doesn't work in Xcode 8
+            $0.setForegroundColor(DDMakeColor(30, 121, 214), backgroundColor: nil, for: .info)
+            $0.setForegroundColor(DDMakeColor(50, 143, 72), backgroundColor: nil, for: .debug)
+            DDLog.add($0)
+        }
+        
+        // File logger
+        DDFileLogger().do {
+            $0.rollingFrequency = TimeInterval(60 * 60 * 24)  // 24 hours
+            $0.logFileManager.maximumNumberOfLogFiles = 7
+            DDLog.add($0)
+        }
+    }
+    
+    
+    // MARK: Logging
+    
+    func error(
+        _ items: Any...,
+        file: StaticString = #file,
+        function: StaticString = #function,
+        line: UInt = #line
+    ) {
+        let message = self.message(from: items)
+        DDLogError(message, file: file, function: function, line: line)
+    }
+    
+    func warning(
+        _ items: Any...,
+        file: StaticString = #file,
+        function: StaticString = #function,
+        line: UInt = #line
+    ) {
+        let message = self.message(from: items)
+        DDLogWarn(message, file: file, function: function, line: line)
+    }
+    
+    func info(
+        _ items: Any...,
+        file: StaticString = #file,
+        function: StaticString = #function,
+        line: UInt = #line
+    ) {
+        let message = self.message(from: items)
+        DDLogInfo(message, file: file, function: function, line: line)
+    }
+    
+    func debug(
+        _ items: Any...,
+        file: StaticString = #file,
+        function: StaticString = #function,
+        line: UInt = #line
+    ) {
+        let message = self.message(from: items)
+        DDLogDebug(message, file: file, function: function, line: line)
+    }
+    
+    func verbose(
+        _ items: Any...,
+        file: StaticString = #file,
+        function: StaticString = #function,
+        line: UInt = #line
+    ) {
+        let message = self.message(from: items)
+        DDLogVerbose(message, file: file, function: function, line: line)
+    }
+    
+    
+    // MARK: Utils
+    
+    private func message(from items: [Any]) -> String {
+        return items
+            .map { String(describing: $0) }
+            .joined(separator: " ")
+    }
+    
+}

+ 150 - 0
JiaPeiManage/Sources/Main/Base/BaseCollectionViewController.swift

@@ -0,0 +1,150 @@
+//
+//  BaseCollectionViewController.swift
+//  JSJP_Student_sw
+//
+//  Created by Ning.ge on 2023/5/31.
+//
+
+import UIKit
+
+import RxSwift
+import EmptyKit
+
+class BaseCollectionViewController: BaseViewController,Refreshable {
+    
+    var isEmptyDisplay: Bool = true
+    
+    var allowListenScroll: Bool = false
+    
+    let collectionView: UICollectionView = UICollectionView(
+        frame: .zero,
+        collectionViewLayout: UICollectionViewFlowLayout()
+        ).then{
+            $0.backgroundColor = .db_gray
+            $0.alwaysBounceVertical = true
+            $0.showsVerticalScrollIndicator = false
+    }
+    
+    override func setupConstraints() {
+        collectionView.snp.makeConstraints { (make) in
+            make.edges.equalTo(0)
+        }
+    }
+    
+    override func viewDidLoad() {
+        super.viewDidLoad()
+
+        collectionView.ept.dataSource = self
+        collectionView.ept.delegate = self
+        
+        view.addSubview(collectionView)
+        
+        showLoadAnimation()
+        
+        registerNetErrorNotification()
+        
+    }
+    
+    //MARK: Public Method
+    func startRefresh() {
+       
+        self.collectionView.setContentOffset(.zero, animated: true)
+        DispatchQueue.delay(time: 0.3, action: {
+            self.collectionView.es.startPullToRefresh()
+        })
+    }
+    
+    func registerNetErrorNotification() {
+        
+        NotificationCenter.default.rx.notification(custom: .netError)
+            .subscribe(onNext: {[unowned self] (_) in
+                
+                self.stopRefresh()
+                
+                self.showNetErrorView()
+            })
+            .disposed(by: disposeBag)
+    }
+    
+    func showNetErrorView() {
+        
+        if totalItems() > 0 { return }
+        
+        self.showAnimationView(self.collectionView, animationType: .failure)
+    }
+    
+    func showLoadAnimation() {
+        self.showAnimationView(self.collectionView)
+    }
+    
+    func hideLoadAnimation() {
+        self.hideAnimationView(self.collectionView)
+    }
+    
+    func stopRefresh() {
+        
+        guard let isRefreshing = collectionView.header?.isRefreshing else { return }
+        
+        if isRefreshing {
+            collectionView.es.stopPullToRefresh()
+        }
+    }
+    
+    //MARK: Private Method
+    private func stopLoad() {
+        
+        guard let isLoading = collectionView.footer?.isRefreshing else { return }
+        
+        if isLoading {
+            collectionView.es.stopLoadingMore()
+        }
+    }
+}
+
+//MARK: Notification
+extension BaseCollectionViewController {
+    
+   func totalItems() -> Int {
+       
+       var totalItems: Int = 0
+        
+       let sectionCount = self.collectionView.numberOfSections
+
+       for i in 0 ..< sectionCount {
+        
+          let items = self.collectionView.numberOfItems(inSection: i)
+        
+          totalItems += items
+       }
+        
+       return totalItems
+    }
+}
+
+extension BaseCollectionViewController:EmptyDelegate,EmptyDataSource {
+    
+    func customViewForEmpty(in view: UIView) -> UIView? {
+
+        return EmptyView.loadFromNib()
+    }
+
+    func emptyShouldAllowScroll(in view: UIView) -> Bool {
+        return true
+    }
+    
+    func emptyShouldDisplay(in view: UIView) -> Bool {
+        return isEmptyDisplay
+    }
+}
+
+extension BaseCollectionViewController: UIScrollViewDelegate {
+    
+    func scrollViewDidScroll(_ scrollView: UIScrollView) {
+        
+//        guard self.collectionView.enableDirection,
+//              let homeParentVc = UIViewController.topMost?.parent as? HomeParentViewController
+//        else { return }
+        
+//        homeParentVc.scrollViewDidScroll(scrollView)
+    }
+}

+ 132 - 0
JiaPeiManage/Sources/Main/Base/BaseTableViewController.swift

@@ -0,0 +1,132 @@
+//
+//  BaseTableViewController.swift
+//  JSJP_Student_sw
+//
+//  Created by Ning.ge on 2023/6/1.
+//
+
+import UIKit
+
+import RxSwift
+import EmptyKit
+
+class BaseTableViewController: BaseViewController,Refreshable {
+    
+    var isEmptyDisplay: Bool = true
+    
+    let tableView: UITableView = UITableView().then{
+        $0.backgroundColor = .db_gray
+        $0.showsVerticalScrollIndicator = false
+        $0.tableFooterView = UIView()
+    }
+    
+    override func setupConstraints() {
+        tableView.snp.makeConstraints { (make) in
+            make.edges.equalTo(0)
+        }
+    }
+    
+    
+    override func viewDidLoad() {
+        super.viewDidLoad()
+        
+        tableView.ept.dataSource = self
+        tableView.ept.delegate = self
+        
+        view.addSubview(tableView)
+        
+        showLoadAnimation()
+        
+        registerNetErrorNotification()
+        
+    }
+    
+    //MARK: Public Method
+    func startRefresh() {
+        
+        self.tableView.setContentOffset(.zero, animated: true)
+        DispatchQueue.delay(time: 0.3, action: {
+            self.tableView.es.startPullToRefresh()
+        })
+    }
+    
+    func registerNetErrorNotification() {
+        
+        NotificationCenter.default.rx.notification(custom: .netError)
+            .subscribe(onNext: {[unowned self] (_) in
+                
+                self.stopRefresh()
+                
+                self.showNetErrorView()
+                
+            })
+            .disposed(by: disposeBag)
+    }
+    
+    
+    func showNetErrorView() {
+        
+        if totalItems() > 0 { return }
+        
+        self.showAnimationView(self.tableView, animationType: .failure)
+    }
+    
+    func showLoadAnimation() {
+        self.showAnimationView(self.tableView)
+    }
+    
+    func stopRefresh() {
+        
+        guard let isRefreshing = tableView.header?.isRefreshing else { return }
+        
+        if isRefreshing {
+            tableView.es.stopPullToRefresh()
+        }
+    }
+    
+    //MARK: Private Method
+    private func stopLoad() {
+        
+        guard let isLoading = tableView.footer?.isRefreshing else { return }
+        
+        if isLoading {
+            tableView.es.stopLoadingMore()
+        }
+    }
+}
+
+//MARK: Notification
+extension BaseTableViewController {
+    
+    func totalItems() -> Int {
+        
+        var totalItems: Int = 0
+        
+        let sectionCount = self.tableView.numberOfSections
+        
+        for i in 0 ..< sectionCount {
+            
+            let items = self.tableView.numberOfRows(inSection: i)
+            
+            totalItems += items
+        }
+        
+        return totalItems
+    }
+}
+
+extension BaseTableViewController:EmptyDelegate,EmptyDataSource {
+    
+    func customViewForEmpty(in view: UIView) -> UIView? {
+        
+        return EmptyView.loadFromNib()
+    }
+    
+    func emptyShouldAllowScroll(in view: UIView) -> Bool {
+        return true
+    }
+    
+    func emptyShouldDisplay(in view: UIView) -> Bool {
+        return isEmptyDisplay
+    }
+}

+ 102 - 0
JiaPeiManage/Sources/Main/Base/BaseViewController.swift

@@ -0,0 +1,102 @@
+//
+//  BaseViewController.swift
+//  JSJP_Student_sw
+//
+//  Created by Ning.ge on 2023/5/30.
+//
+
+import UIKit
+
+import RxSwift
+import RxCocoa
+import ReactorKit
+import RxDataSources
+
+class BaseViewController: UIViewController, NetAnimationLoadable {
+
+    // MARK: Properties
+    lazy private(set) var className: String = {
+        return type(of: self).description().components(separatedBy: ".").last ?? ""
+    }()
+    
+    /// There is a bug when trying to go back to previous view controller in a navigation controller
+    /// on iOS 11, a scroll view in the previous screen scrolls weirdly. In order to get this fixed,
+    /// we have to set the scrollView's `contentInsetAdjustmentBehavior` property to `.never` on
+    /// `viewWillAppear()` and set back to the original value on `viewDidAppear()`.
+    private var scrollViewOriginalContentInsetAdjustmentBehaviorRawValue: Int?
+
+    
+    // MARK: Initializing
+    init() {
+        super.init(nibName: nil, bundle: nil)
+    }
+    
+    init(nibName:String) {
+        super.init(nibName: nibName, bundle: nil)
+    }
+
+    required convenience init?(coder aDecoder: NSCoder) {
+        self.init()
+    }
+    
+    deinit {
+        log.verbose("DEINIT: \(self.className)")
+    }
+    
+    // MARK: Rx
+    var disposeBag = DisposeBag()
+    
+
+    // MARK: View Lifecycle
+    override func viewWillAppear(_ animated: Bool) {
+        super.viewWillAppear(animated)
+        
+        // fix iOS 11 scroll view bug
+        if #available(iOS 11, *) {
+            if let scrollView = self.view.subviews.first as? UIScrollView {
+                self.scrollViewOriginalContentInsetAdjustmentBehaviorRawValue =
+                    scrollView.contentInsetAdjustmentBehavior.rawValue
+                scrollView.contentInsetAdjustmentBehavior = .never
+            }
+        }
+    }
+    
+    override func viewDidAppear(_ animated: Bool) {
+        super.viewDidAppear(animated)
+        
+        // fix iOS 11 scroll view bug
+        if #available(iOS 11, *) {
+            if let scrollView = self.view.subviews.first as? UIScrollView,
+               let rawValue = self.scrollViewOriginalContentInsetAdjustmentBehaviorRawValue,
+               let behavior = UIScrollView.ContentInsetAdjustmentBehavior(rawValue: rawValue) {
+                scrollView.contentInsetAdjustmentBehavior = behavior
+            }
+        }
+    }
+    
+    // MARK: Layout Constraints
+    
+    private(set) var didSetupConstraints = false
+    
+    override func viewDidLoad() {
+        
+        self.view.setNeedsUpdateConstraints()
+        
+        self.edgesForExtendedLayout = []
+        
+        self.view.backgroundColor = .db_gray
+    }
+    
+    override func updateViewConstraints() {
+        if !self.didSetupConstraints {
+            self.setupConstraints()
+            self.didSetupConstraints = true
+        }
+        super.updateViewConstraints()
+    }
+    
+    func setupConstraints() {
+        // Override point
+    }
+    
+}

+ 41 - 0
JiaPeiManage/Sources/Main/MainNavigationController.swift

@@ -0,0 +1,41 @@
+//
+//  MainNavigationController.swift
+//  JSJP_Student_sw
+//
+//  Created by Ning.ge on 2023/5/30.
+//
+
+import UIKit
+
+import RxSwift
+
+final class MainNavigationController: UINavigationController {
+
+    override func viewDidLoad() {
+        super.viewDidLoad()
+        
+        self.interactivePopGestureRecognizer?.delegate = self
+        
+        self.navigationBar.setBackgroundImage(UIImage.size(CGSize(width: 1, height: 1)).color(UIColor.white).image, for: .default)
+        
+        self.navigationBar.titleTextAttributes = [NSAttributedString.Key.foregroundColor:UIColor.db_lightBlack]
+       
+    }
+    
+    override func pushViewController(_ viewController: UIViewController, animated: Bool){
+        if children.count > 0 {
+            viewController.hidesBottomBarWhenPushed = true
+            let backItem = UIBarButtonItem(image: NYImage.Home.back?.withRenderingMode(.alwaysOriginal), style: .plain, target: self, action: #selector(back))
+            viewController.navigationItem.leftBarButtonItems = [backItem]
+        }
+        super.pushViewController(viewController, animated: animated)
+    }
+    
+    @objc private func back() {
+        
+        self.popViewController(animated: true)
+        
+    }
+}
+
+//extension MainNavigationController: UIGestureRecognizerDelegate {}

+ 83 - 0
JiaPeiManage/Sources/Main/MainTabBarController.swift

@@ -0,0 +1,83 @@
+//
+//  MainTabBarController.swift
+//  JSJP_Student_sw
+//
+//  Created by Ning.ge on 2023/5/30.
+//
+
+import UIKit
+
+import ReactorKit
+import RxCocoa
+import RxSwift
+
+final class MainTabBarController: UITabBarController, View {
+
+    
+    // MARK: Constants
+    
+    fileprivate struct Metric {
+        static let tabBarHeight = 50.f
+    }
+    
+    // MARK: Properties
+    
+    var disposeBag = DisposeBag()
+
+//    init(
+//       reactor: MainTabBarViewReactor,
+//       homeParentViewController:HomeParentViewController,
+//       timekeepViewController:TimeKeepParentViewController,
+//       mineListViewController:MineParentViewController
+//     ) {
+//        defer { self.reactor = reactor }
+//        super.init(nibName: nil, bundle: nil)
+//        self.viewControllers = [homeParentViewController,
+//                                timekeepViewController,
+//                                mineListViewController]
+//            .map{ (viewController) -> UINavigationController in
+//                let navigationController = MainNavigationController(rootViewController: viewController)
+////                navigationController.tabBarItem.imageInsets.top = 5
+////                navigationController.tabBarItem.imageInsets.bottom = 5
+//                navigationController.tabBarItem.titlePositionAdjustment = UIOffset(horizontal: 0, vertical: -3)
+//                return navigationController
+//             }
+//        self.tabBar.barTintColor = .db_white
+//    }
+    
+    required init?(coder aDecoder: NSCoder) {
+        fatalError("init(coder:) has not been implemented")
+    }
+
+    // MARK: Configuring
+    
+    func bind(reactor: MainTabBarViewReactor) {
+        
+        self.rx.didSelect
+            .scan((nil, nil)) { state, viewController in
+                return (state.1, viewController)
+            }.subscribe(onNext: { (fromVc,toVc) in
+                
+//                if fromVc == toVc || fromVc == nil {
+//                    TogetherDataManager.refreshDataForTab(toVc, true)
+//                } else {
+//                    TogetherDataManager.refreshDataForTab(toVc, false)
+//                }
+//                if TogetherDataManager.homePageController(toVc) == nil {
+//                   TogetherDataManager.referenceDate = Date()
+//                }
+            }).disposed(by: disposeBag)
+    }
+    
+    
+    override func viewDidLayoutSubviews() {
+        super.viewDidLayoutSubviews()
+        if #available(iOS 11.0, *) {
+            self.tabBar.height = Metric.tabBarHeight + self.view.safeAreaInsets.bottom
+        } else {
+            self.tabBar.height = Metric.tabBarHeight
+        }
+        self.tabBar.bottom = self.view.height
+    }
+}
+

+ 17 - 0
JiaPeiManage/Sources/Main/MainTabBarViewReactor.swift

@@ -0,0 +1,17 @@
+//
+//  MainTabBarViewReactor.swift
+//  JSJP_Student_sw
+//
+//  Created by Ning.ge on 2023/5/30.
+//
+
+import ReactorKit
+
+final class MainTabBarViewReactor: Reactor {
+    
+    typealias Action = NoAction
+    
+    struct State {}
+    
+    let initialState: State = State()
+}

+ 23 - 0
JiaPeiManage/Sources/Main/NYArticleNavBar.swift

@@ -0,0 +1,23 @@
+//
+//  NYArticleNavBar.swift
+//  JSJP_Student_sw
+//
+//  Created by Ning.ge on 2023/6/1.
+//
+
+import UIKit
+
+final class NYArticleNavBar: UIView,NibLoadable {
+    
+    @IBOutlet weak var backButton: UIButton!
+    @IBOutlet weak var shareButton: UIButton!
+    @IBOutlet weak var applyButton: UIButton!
+    @IBOutlet weak var titleLabel: UILabel!
+    
+    @IBAction func pop(_ sender: UIButton) {
+        
+        
+    }
+    
+
+}

+ 70 - 0
JiaPeiManage/Sources/Main/NYArticleNavBar.xib

@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="21701" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
+    <device id="retina6_12" orientation="portrait" appearance="light"/>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21678"/>
+        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
+        <capability name="System colors in document resources" minToolsVersion="11.0"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <objects>
+        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
+        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
+        <view contentMode="scaleToFill" id="iN0-l3-epB" customClass="NYArticleNavBar">
+            <rect key="frame" x="0.0" y="0.0" width="636" height="210"/>
+            <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+            <subviews>
+                <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Hl4-2O-Meu">
+                    <rect key="frame" x="596" y="90" width="30" height="30"/>
+                </button>
+                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="IQO-dK-G2z">
+                    <rect key="frame" x="60" y="105" width="486" height="0.0"/>
+                    <fontDescription key="fontDescription" type="system" pointSize="15"/>
+                    <color key="textColor" red="0.2470588235" green="0.2470588235" blue="0.2470588235" alpha="1" colorSpace="calibratedRGB"/>
+                    <nil key="highlightedColor"/>
+                </label>
+                <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Ord-7s-vCm">
+                    <rect key="frame" x="20" y="90" width="30" height="30"/>
+                    <constraints>
+                        <constraint firstAttribute="width" constant="30" id="1p7-Z5-4nF"/>
+                        <constraint firstAttribute="height" constant="30" id="507-vM-fCl"/>
+                    </constraints>
+                </button>
+                <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="KDS-ig-Mpg">
+                    <rect key="frame" x="556" y="90" width="30" height="30"/>
+                </button>
+            </subviews>
+            <viewLayoutGuide key="safeArea" id="vUN-kp-3ea"/>
+            <color key="backgroundColor" systemColor="systemBackgroundColor"/>
+            <constraints>
+                <constraint firstAttribute="trailing" secondItem="IQO-dK-G2z" secondAttribute="trailing" constant="90" id="8BW-NO-M8E"/>
+                <constraint firstItem="KDS-ig-Mpg" firstAttribute="width" secondItem="Ord-7s-vCm" secondAttribute="width" id="8L5-6U-LXN"/>
+                <constraint firstItem="IQO-dK-G2z" firstAttribute="centerY" secondItem="Ord-7s-vCm" secondAttribute="centerY" id="8hc-WS-9EP"/>
+                <constraint firstItem="KDS-ig-Mpg" firstAttribute="leading" secondItem="IQO-dK-G2z" secondAttribute="trailing" constant="10" id="LV6-Fa-prV"/>
+                <constraint firstItem="Hl4-2O-Meu" firstAttribute="width" secondItem="Ord-7s-vCm" secondAttribute="width" id="Tce-4e-rQv"/>
+                <constraint firstItem="IQO-dK-G2z" firstAttribute="leading" secondItem="Ord-7s-vCm" secondAttribute="trailing" constant="10" id="hXX-if-tra"/>
+                <constraint firstItem="IQO-dK-G2z" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="60" id="i8Q-y3-PYz"/>
+                <constraint firstItem="Hl4-2O-Meu" firstAttribute="height" secondItem="Ord-7s-vCm" secondAttribute="height" id="kLc-ii-7ZF"/>
+                <constraint firstItem="Hl4-2O-Meu" firstAttribute="leading" secondItem="KDS-ig-Mpg" secondAttribute="trailing" constant="10" id="utB-zU-C0m"/>
+                <constraint firstItem="KDS-ig-Mpg" firstAttribute="height" secondItem="Ord-7s-vCm" secondAttribute="height" id="vri-RT-dpc"/>
+                <constraint firstItem="KDS-ig-Mpg" firstAttribute="centerY" secondItem="Ord-7s-vCm" secondAttribute="centerY" id="wvL-SS-46G"/>
+                <constraint firstItem="IQO-dK-G2z" firstAttribute="centerY" secondItem="iN0-l3-epB" secondAttribute="centerY" id="x4w-YW-ZE6"/>
+                <constraint firstItem="Hl4-2O-Meu" firstAttribute="centerY" secondItem="Ord-7s-vCm" secondAttribute="centerY" id="ytL-EQ-lTb"/>
+            </constraints>
+            <freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
+            <connections>
+                <outlet property="applyButton" destination="KDS-ig-Mpg" id="PSe-vy-VIC"/>
+                <outlet property="backButton" destination="Ord-7s-vCm" id="frF-sl-q9e"/>
+                <outlet property="shareButton" destination="Hl4-2O-Meu" id="zYI-og-oy0"/>
+                <outlet property="titleLabel" destination="IQO-dK-G2z" id="Pg7-w5-4aN"/>
+            </connections>
+            <point key="canvasLocation" x="42" y="-12"/>
+        </view>
+    </objects>
+    <resources>
+        <systemColor name="systemBackgroundColor">
+            <color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+        </systemColor>
+    </resources>
+</document>

+ 190 - 0
JiaPeiManage/Sources/Main/NYArticleViewController.swift

@@ -0,0 +1,190 @@
+//
+//  NYArticleViewController.swift
+//  JSJP_Student_sw
+//
+//  Created by Ning.ge on 2023/6/1.
+//
+
+import UIKit
+
+import WebKit
+
+final class NYArticleViewController: BaseViewController {
+
+    private var statusBar: UIStatusBarStyle = .default
+    private var link: String
+    private let maxOffest: CGFloat = isIphoneX ? 88 : 64
+    private let minOffest: CGFloat = isIphoneX ? 44 : 32
+    private lazy var webView: WKWebView = {
+        let webView = WKWebView(frame: .zero)
+        webView.scrollView.backgroundColor = UIColor.db_gray
+        return webView
+    }()
+    private lazy var navBar: NYArticleNavBar = {
+        let navBar = NYArticleNavBar.loadFromNib()
+        navBar.backgroundColor = UIColor.db_white
+        navBar.alpha = 0
+        navBar.backButton.setImage(NYImage.Home.back?.with(color: UIColor.db_pink), for: .normal)
+        navBar.shareButton.setImage(NYImage.Article.more?.with(color: UIColor.db_pink), for: .normal)
+        navBar.applyButton.setImage(NYImage.Home.editRcmd?.with(color: UIColor.db_pink), for: .normal)
+        
+        return navBar
+    }()
+    private lazy var backButton: UIButton = {
+        let backButton = UIButton(frame: .zero)
+        backButton.setImage(NYImage.Home.back, for: .normal)
+        return backButton
+    }()
+    private lazy var shareButton: UIButton = {
+        let shareButton = UIButton(frame: .zero)
+        shareButton.setImage(NYImage.Article.more?.with(color: UIColor.db_white), for: .normal)
+        return shareButton
+    }()
+    
+    
+    init(link:String) {
+        
+        self.link = link
+        super.init()
+        
+        log.info(link)
+    }
+    
+    required convenience init?(coder aDecoder: NSCoder) {
+        fatalError("init(coder:) has not been implemented")
+    }
+    
+    override func setupConstraints() {
+        
+        webView.snp.makeConstraints { (make) in
+           make.edges.equalToSuperview()
+        }
+        
+        navBar.snp.makeConstraints { (make) in
+            make.top.left.right.equalToSuperview()
+            make.height.equalTo(maxOffest)
+        }
+        
+        backButton.snp.makeConstraints { (make) in
+            make.width.height.equalTo(30)
+            make.left.equalTo(15)
+            make.centerY.equalTo(navBar.snp.centerY).offset(20)
+        }
+        shareButton.snp.makeConstraints { (make) in
+            make.width.height.equalTo(30)
+            make.right.equalTo(-15)
+            make.centerY.equalTo(navBar.snp.centerY).offset(20)
+        }
+    }
+    
+    override var preferredStatusBarStyle: UIStatusBarStyle {
+        return statusBar
+    }
+    
+    override func viewWillAppear(_ animated: Bool) {
+        super.viewWillAppear(animated)
+        self.navigationController?.setNavigationBarHidden(true, animated: true)
+        
+        statusBar = .default
+        self.setNeedsStatusBarAppearanceUpdate()
+    }
+    
+    override func viewWillDisappear(_ animated: Bool) {
+        super.viewWillDisappear(animated)
+        
+        statusBar = .lightContent
+        self.setNeedsStatusBarAppearanceUpdate()
+    }
+    
+    override func viewDidLoad() {
+        super.viewDidLoad()
+        
+        self.view.addSubview(webView)
+        self.view.addSubview(navBar)
+        self.view.addSubview(backButton)
+        self.view.addSubview(shareButton)
+        
+        webView.navigationDelegate = self
+        webView.allowsBackForwardNavigationGestures = true
+        webView.scrollView.delegate = self
+        
+        if #available(iOS 11, *) {
+            webView.scrollView.contentInsetAdjustmentBehavior = .never
+        } else {
+            self.automaticallyAdjustsScrollViewInsets = false
+        }
+        
+        if let linkURL = URL(string: link) {
+            webView.load(URLRequest(url: linkURL))
+        }
+        
+        self.clickEvent()
+        
+    }
+    
+    private func loadProgress() {
+
+        webView.rx.observe(Double.self, "estimatedProgress")
+            .filterNil()
+            .subscribe(onNext: { (_) in
+
+            })
+            .disposed(by: disposeBag)
+    }
+    
+    private func clickEvent() {
+        
+        navBar.backButton.rx.tap
+            .subscribe(onNext: {[unowned self] (_) in
+            
+              self.navigationController?.popViewController(animated: true)
+          })
+            .disposed(by: disposeBag)
+        
+        backButton.rx.tap
+            .subscribe(onNext: {[unowned self] (_) in
+                
+                self.navigationController?.popViewController(animated: true)
+            })
+            .disposed(by: disposeBag)
+    }
+}
+
+extension NYArticleViewController: WKNavigationDelegate {
+    
+    func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
+
+        webView.evaluateJavaScript("document.getElementsByClassName(\"top-holder\")[0].hidden = true;", completionHandler: nil)
+    }
+    
+    func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
+    }
+    
+    func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
+        
+        if navigationAction.targetFrame == nil {
+            webView.load(navigationAction.request)
+        }
+        decisionHandler(.allow)
+    }
+}
+
+extension NYArticleViewController: UIScrollViewDelegate {
+    
+    func scrollViewDidScroll(_ scrollView: UIScrollView) {
+        
+        let offestY = scrollView.contentOffset.y
+        if offestY > 0 {
+            backButton.alpha = 0
+            shareButton.alpha = 0
+            let alpha = (offestY+minOffest)/maxOffest
+            navBar.alpha = alpha
+            navBar.titleLabel.text = webView.title
+            
+        } else {
+            backButton.alpha = 1
+            shareButton.alpha = 1
+            navBar.alpha = 0
+        }
+    }
+}

+ 143 - 0
JiaPeiManage/Sources/Main/NYWebViewController.swift

@@ -0,0 +1,143 @@
+//
+//  NYWebViewController.swift
+//  JSJP_Student_sw
+//
+//  Created by Ning.ge on 2023/6/1.
+//
+
+import UIKit
+
+import WebKit
+import RxSwift
+
+final class NYWebViewController: BaseViewController {
+
+    private var link: String
+
+    private lazy var webView: WKWebView = {
+        let webView = WKWebView(frame: .zero)
+        return webView
+    }()
+    
+    init(link:String) {
+        
+        self.link = link
+        super.init()
+    }
+    
+    required init?(coder aDecoder: NSCoder) {
+        fatalError("init(coder:) has not been implemented")
+    }
+    
+    override func setupConstraints() {
+        
+        webView.snp.makeConstraints { (make) in
+           make.edges.equalToSuperview()
+        }
+        
+    }
+    
+    override func viewWillAppear(_ animated: Bool) {
+        super.viewWillAppear(animated)
+        self.navigationController?.setNavigationBarHidden(false, animated: true)
+    }
+    
+    override func viewDidLoad() {
+        super.viewDidLoad()
+    
+        view.addSubview(webView)
+        
+        setupWebView()
+        
+    }
+
+    private func setupWebView() {
+    
+        webView.navigationDelegate = self
+        webView.allowsBackForwardNavigationGestures = true
+        
+        if let linkURL = URL(string: link) {
+            webView.load(URLRequest(url: linkURL))
+           
+        }
+    }
+    
+    private func adjustScrollViewTop() {
+        if #available(iOS 11, *) {
+            webView.scrollView.contentInsetAdjustmentBehavior = .never
+        }else{
+            self.automaticallyAdjustsScrollViewInsets = false
+        }
+    }
+    
+
+    
+}
+
+extension NYWebViewController: WKNavigationDelegate {
+    
+    func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
+
+        self.setLeftItems()
+        self.setRightItems()
+        title = webView.title
+            //webView.evaluateJavaScript("document.getElementsByClassName(\"top-holder\")[0].hidden = true;", completionHandler: nil)
+    }
+    
+    func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
+        NYSwToaster.show("被禁止访问了???")
+    }
+    
+    func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
+        
+        if navigationAction.targetFrame == nil {
+            webView.load(navigationAction.request)
+        }
+        decisionHandler(.allow)
+    }
+
+}
+
+//MARK: - setBarButtonItem
+extension NYWebViewController {
+    
+    private func setLeftItems() {
+        let backItem = UIBarButtonItem(image: NYImage.Home.back?.withRenderingMode(.alwaysOriginal), style: .plain, target: self, action: #selector(back))
+        
+        let closeButton = UIButton(type: .custom)
+        closeButton.setTitle("关闭", for: .normal)
+        closeButton.setTitleColor(UIColor.db_white, for: .normal)
+        closeButton.titleLabel?.font = NYFont.SysFont.sys_16
+        closeButton.rx.tap.subscribe(onNext: {[unowned self] _ in
+            self.close()
+        }).disposed(by: disposeBag)
+        let closeItem = UIBarButtonItem(customView: closeButton)
+        
+        self.navigationItem.leftBarButtonItems = [backItem,closeItem]
+    }
+    
+    private func setRightItems() {
+       
+       let moreImage = NYImage.Home.more?.with(color: UIColor.db_white)
+        
+        let moreItem = UIBarButtonItem(image:moreImage?.withRenderingMode(.alwaysOriginal), style: .plain, target: self, action: #selector(more))
+       self.navigationItem.rightBarButtonItem = moreItem
+    }
+    
+    @objc private func back() {
+        
+        if webView.canGoBack {
+            webView.goBack()
+        }else{
+            self.navigationController?.popViewController(animated: true)
+        }
+    }
+    
+    private func close() {
+        self.navigationController?.popViewController(animated: true)
+    }
+    
+    @objc private func more() {
+       
+    }
+}

+ 286 - 0
JiaPeiManage/Sources/Models/UserInfoModel.swift

@@ -0,0 +1,286 @@
+//
+//  UserModel.swift
+//  JSJP_Student_sw
+//
+//  Created by Ning.ge on 2023/6/5.
+//
+
+import ObjectMapper
+import SwiftyUserDefaults
+
+// 用户登录的渠道
+enum UserLoginChannelType : Int{
+    case Default = -1       ///    未登录
+    case Phone = 0          /// 手机号登录
+    case QQ                 /// qq登录
+    case Email              /// 邮箱登录
+    case ChatID             /// 微信号登录
+    case AutoLogin          /// 自动登录
+}
+
+struct ParamsItem :ImmutableMappable{
+    
+    var ID:String = ""
+    var VALUE:String = ""
+    var REMARK:String = ""
+    var KEY:String = ""
+    var NAME:String = ""
+    
+    init(map: Map) throws {
+        ID = try map.value("ID")
+        VALUE = try map.value("VALUE")
+        REMARK = try map.value("REMARK")
+        KEY = try map.value("KEY")
+        NAME = try map.value("NAME")
+    }
+}
+
+
+struct SchInfo :ImmutableMappable{
+    
+    var address:String = ""
+    var bussinessLicenseNum:String = ""
+    var cityName:String = ""
+    var contactor:String  = ""
+    var count:String  = ""
+    var countryId:String  = ""
+    var countryName:String  = ""
+    var desc:String  = ""
+    var distance:String  = ""
+    var experienceRange:String  = ""
+    var headimg:String  = ""
+    var _id:String  = ""
+    var jxbh:String  = ""
+    var key:String  = ""
+    var latitude:String  = ""
+    var legalRepresentative:String  = ""
+    var legalTel:String  = ""
+    var licenseDeadline:String  = ""
+    var licenseNum:String  = ""
+    var longitude:String  = ""
+    var macId:String  = ""
+    var name:String  = ""
+    var price:String  = ""
+    var provinceId:String  = ""
+    var provinceName:String  = ""
+    var regDate:String  = ""
+    var route:String  = ""
+    var score:String  = ""
+    var serviceStar:String  = ""
+    var shortName:String  = ""
+    var siteStar:String  = ""
+    var star:String  = ""
+    var telePhone:String  = ""
+    var tollStar:String  = ""
+    var trainLevel:String  = ""
+    var unit:String  = ""
+    var vip:String  = ""
+    var watch:String  = ""
+    
+    init(map: Map) throws {
+        address = try map.value("address")
+        bussinessLicenseNum = try map.value("bussinessLicenseNum")
+        cityName = try map.value("cityName")
+        contactor = try map.value("contactor")
+        count = try map.value("count")
+        countryId = try map.value("countryId")
+        countryName = try map.value("countryName")
+        desc = try map.value("desc")
+        distance = try map.value("distance")
+        experienceRange = try map.value("experienceRange")
+        headimg = try map.value("headimg")
+        _id = try map.value("_id")
+        jxbh = try map.value("jxbh")
+        key = try map.value("key")
+        latitude = try map.value("latitude")
+        legalRepresentative = try map.value("legalRepresentative")
+        legalTel = try map.value("legalTel")
+        licenseDeadline = try map.value("licenseDeadline")
+        licenseNum = try map.value("licenseNum")
+        longitude = try map.value("longitude")
+        macId = try map.value("macId")
+        name = try map.value("name")
+        price = try map.value("price")
+        provinceId = try map.value("provinceId")
+        provinceName = try map.value("provinceName")
+        regDate = try map.value("regDate")
+        route = try map.value("count")
+        score = try map.value("count")
+        serviceStar = try map.value("count")
+        shortName = try map.value("count")
+        siteStar = try map.value("count")
+        star = try map.value("count")
+        telePhone = try map.value("count")
+        tollStar = try map.value("count")
+        trainLevel = try map.value("count")
+        unit = try map.value("count")
+        vip = try map.value("count")
+        watch = try map.value("count")
+        
+
+    }
+}
+
+
+struct UserInfo :ImmutableMappable{
+    /// 登录渠道
+    var channel:UserLoginChannelType?
+
+    var address:String = ""
+    var appType:String = ""
+    var birthday:String = ""
+    var carType:String  = ""
+    var city:String  = ""
+    var cityName:String  = ""
+    var country:String  = ""
+    var countryName:String  = ""
+    var crDate:String  = ""
+    var eduStatus:String  = ""
+    var email:String  = ""
+    var expireTime:String  = ""
+    var _id:String  = ""
+    var imei:String  = ""
+    var isycbd:String  = ""
+    var loginCode:String  = ""
+    var mnStatus:String  = ""
+    var mnqFaceCount:String  = ""
+    var mnqFacePorcess:String  = ""
+    var mnqThreeOpen:String  = ""
+    var mnqTwoOpen:String  = ""
+    var nickName:String  = ""
+    var outId:String  = ""
+    var params:[ParamsItem]?
+    var payPwd:String  = ""
+    var photo:String  = ""
+    var pxjd:String  = ""
+    var pxkm:String  = ""
+    var qzgx:String  = ""
+    var schInfo:SchInfo?
+    var school:String  = ""
+    var schoolName:String  = ""
+    var sex:String  = ""
+    var sfzb:String  = ""
+    var status:String  = ""
+    var stuDevNum:String  = ""
+    var stuNum:String  = ""
+    var systemVersion:String  = ""
+    var telphone:String  = ""
+    var userName:String  = ""
+    var verifyLevel:String  = ""
+    var version:String  = ""
+    var ycbdFaceCount:String  = ""
+    var busitype:String  = ""
+    var isOpenScan:String  = ""
+    var qzVideo:String  = ""
+    var sjjlbh:String  = ""
+    var a_c:String  = ""
+    
+    
+    var isLogin: Bool {
+        set {
+           Defaults.isLogin = newValue
+        }
+        get {
+           return Defaults.isLogin
+        }
+    }
+    
+    var openTimes: Int {
+        set {
+            Defaults.openTimes = newValue
+        }
+        get {
+            return Defaults.openTimes
+        }
+    }
+    
+    var avater: UIImage? {
+        set {
+            if newValue != nil {
+                Defaults.avater = newValue!.pngData() ?? Data()
+            }
+        }
+        get {
+            return UIImage(data: Defaults.avater)
+        }
+    }
+    
+    
+    var userAccount:String? {
+        set {
+            if newValue != nil{
+                Defaults.userAccount = newValue! ?? ""
+            }
+        }
+        get {
+            return Defaults.userAccount
+        }
+    }
+    
+    var password:String? {
+        set {
+            if newValue != nil{
+                Defaults.userPassword = newValue! ?? ""
+            }
+        }
+        get {
+            return Defaults.userPassword
+        }
+    }
+    
+    init(){}
+    init(map: Map) throws {
+
+        
+        channel = try map.value("channel")
+        address = try map.value("address")
+        appType = try map.value("appType")
+        birthday = try map.value("birthday")
+        carType = try map.value("carType")
+        city = try map.value("city")
+        cityName = try map.value("cityName")
+        country = try map.value("country")
+        countryName = try map.value("countryName")
+        crDate = try map.value("crDate")
+        eduStatus = try map.value("eduStatus")
+        email = try map.value("email")
+        expireTime = try map.value("expireTime")
+        _id = try map.value("_id")
+        imei = try map.value("imei")
+        isycbd = try map.value("isycbd")
+        loginCode = try map.value("loginCode")
+        mnStatus = try map.value("mnStatus")
+        mnqFaceCount = try map.value("mnqFaceCount")
+        mnqFacePorcess = try map.value("mnqFacePorcess")
+        mnqThreeOpen = try map.value("mnqThreeOpen")
+        mnqTwoOpen = try map.value("mnqTwoOpen")
+        nickName = try map.value("nickName")
+        outId = try map.value("outId")
+        params = try map.value("params")
+        payPwd = try map.value("payPwd")
+        photo = try map.value("photo")
+        pxjd = try map.value("pxjd")
+        pxkm = try map.value("pxkm")
+        qzgx = try map.value("qzgx")
+        schInfo = try map.value("schInfo")
+        school = try map.value("school")
+        schoolName = try map.value("schoolName")
+        sex = try map.value("sex")
+        sfzb = try map.value("sfzb")
+        status = try map.value("status")
+        stuDevNum = try map.value("stuDevNum")
+        stuNum = try map.value("stuNum")
+        systemVersion = try map.value("systemVersion")
+        telphone = try map.value("telphone")
+        userName = try map.value("userName")
+        verifyLevel = try map.value("verifyLevel")
+        version = try map.value("version")
+        ycbdFaceCount = try map.value("ycbdFaceCount")
+        busitype = try map.value("busitype")
+        isOpenScan = try map.value("isOpenScan")
+        qzVideo = try map.value("qzVideo")
+        sjjlbh = try map.value("sjjlbh")
+        a_c = try map.value("a_c")
+
+    }
+}

+ 17 - 0
JiaPeiManage/Sources/Modulars/Login/Controllers/LoginViewController.swift

@@ -0,0 +1,17 @@
+//
+//  LoginViewController.swift
+//  JiaPeiManage
+//
+//  Created by Ning.ge on 2023/6/12.
+//
+
+import UIKit
+import RxSwift
+import RxCocoa
+import SwiftyUserDefaults
+
+final class LoginViewController: BaseViewController {
+    
+    
+    
+}

+ 31 - 0
JiaPeiManage/Sources/Modulars/Login/Controllers/LoginViewController.xib

@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="21701" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
+    <device id="retina6_12" orientation="portrait" appearance="light"/>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21678"/>
+        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
+        <capability name="System colors in document resources" minToolsVersion="11.0"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <objects>
+        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="LoginViewController" customModule="JiaPeiManage" customModuleProvider="target">
+            <connections>
+                <outlet property="view" destination="iN0-l3-epB" id="vZz-m1-1GF"/>
+            </connections>
+        </placeholder>
+        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
+        <view contentMode="scaleToFill" id="iN0-l3-epB">
+            <rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
+            <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+            <viewLayoutGuide key="safeArea" id="vUN-kp-3ea"/>
+            <color key="backgroundColor" systemColor="systemBackgroundColor"/>
+            <point key="canvasLocation" x="33" y="-12"/>
+        </view>
+    </objects>
+    <resources>
+        <systemColor name="systemBackgroundColor">
+            <color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+        </systemColor>
+    </resources>
+</document>

+ 99 - 0
JiaPeiManage/Sources/Modulars/Splash/SplashModel.swift

@@ -0,0 +1,99 @@
+//
+//  SplashModel.swift
+//  JSJP_Student_sw
+//
+//  Created by Ning.ge on 2023/5/30.
+//
+
+import ObjectMapper
+
+struct SplashModel:ImmutableMappable {
+    
+    var max_time: Int
+    var min_interval: Int
+    var pull_interval: Int
+    var list:[ListModel]
+    var show:[ShowModel]?
+    
+    init(map: Map) throws {
+        max_time = try map.value("max_time")
+        min_interval = try map.value("min_interval")
+        pull_interval = try map.value("pull_interval")
+        list = try map.value("list")
+        show = try? map.value("show")
+    }
+    
+}
+
+struct ListModel:ImmutableMappable {
+    
+    var id: Int
+    var type: Int
+    var card_type: Int
+    var duration: Int
+    var begin_time: Int
+    var end_time: Int
+    var thumb: String
+    var hash: String
+    var logo_url: String
+    var logo_hash: String
+    var skip: Int
+    var uri: String
+    var uri_title: String
+    var source: Int
+    var ad_cb: String
+    var resource_id: Int
+    var request_id: String
+    var client_ip: String
+    var is_ad: Bool
+    var is_ad_loc: Bool
+    var extra:ExtraModel
+    
+    init(map: Map) throws {
+        id = try map.value("id")
+        type = try map.value("type")
+        card_type = try map.value("card_type")
+        duration = try map.value("duration")
+        begin_time = try map.value("begin_time")
+        end_time = try map.value("end_time")
+        thumb = try map.value("thumb")
+        hash = try map.value("hash")
+        logo_url = try map.value("logo_url")
+        logo_hash = try map.value("logo_hash")
+        skip = try map.value("skip")
+        uri = try map.value("uri")
+        uri_title = try map.value("uri_title")
+        source = try map.value("source")
+        ad_cb = try map.value("ad_cb")
+        resource_id = try map.value("resource_id")
+        request_id = try map.value("request_id")
+        client_ip = try map.value("client_ip")
+        is_ad = try map.value("is_ad")
+        is_ad_loc = try map.value("is_ad_loc")
+        extra = try map.value("extra")
+    }
+    
+}
+
+struct ExtraModel:ImmutableMappable {
+    
+    var use_ad_web_v2: Bool
+    //show_urls: []
+    //click_urls: [""]
+    //download_whitelist: []
+    init(map: Map) throws {
+        use_ad_web_v2 = try map.value("use_ad_web_v2")
+    }
+}
+
+struct ShowModel:ImmutableMappable {
+    var id: Int
+    var stime: Int
+    var etime: Int
+    
+    init(map: Map) throws {
+        id = try map.value("id")
+        stime = try map.value("stime")
+        etime = try map.value("etime")
+    }
+}

+ 64 - 0
JiaPeiManage/Sources/Modulars/Splash/SplashViewController.swift

@@ -0,0 +1,64 @@
+//
+//  SplashViewController.swift
+//  JSJP_Student_sw
+//
+//  Created by Ning.ge on 2023/5/30.
+//
+
+import UIKit
+
+final class SplashViewController: BaseViewController {
+
+    private let presentMainScreen: () -> Void
+    
+    private let backgroundImageView = UIImageView().then{
+        $0.image = NYImage.Launch.background
+    }
+    
+    private let splashImageView = UIImageView().then{
+        $0.image = NYImage.Launch.splash
+    }
+
+    override func setupConstraints() {
+        backgroundImageView.snp.makeConstraints { (make) in
+            make.edges.equalToSuperview()
+        }
+        
+        splashImageView.snp.makeConstraints { (make) in
+            make.width.equalTo(kScreenWidth - 60)
+            make.height.equalTo(kScreenHeight * 0.4)
+            make.centerX.equalToSuperview()
+            make.centerY.equalToSuperview()
+        }
+        
+    }
+    
+    init(presentMainScreen: @escaping () -> Void) {
+        
+        self.presentMainScreen = presentMainScreen
+        super.init()
+    }
+    
+    required convenience init?(coder aDecoder: NSCoder) {
+        fatalError("init(coder:) has not been implemented")
+    }
+    
+    override var prefersStatusBarHidden: Bool {
+        return true
+    }
+    override func viewDidLoad() {
+        super.viewDidLoad()
+        
+        view.addSubview(backgroundImageView)
+        backgroundImageView.addSubview(splashImageView)
+    }
+    
+    override func viewDidAppear(_ animated: Bool) {
+        super.viewDidAppear(animated)
+        
+        DispatchQueue.delay(time: 1.0, action: {[weak self] in
+            self?.presentMainScreen()
+        })
+    }
+    
+}

+ 38 - 0
JiaPeiManage/Sources/NYAppCongfigure.swift

@@ -0,0 +1,38 @@
+//
+//  NYAppCongfigure.swift
+//  JSJP_Student_sw
+//
+//  Created by ningye on 2023/5/22.
+//
+
+import UIKit
+
+let kScreenWidth = UIScreen.main.bounds.width
+let kScreenHeight = UIScreen.main.bounds.height
+let kScreenRatio = kScreenHeight/750.f //以iPhone7为标准
+
+let kToastBottomMaxSpace = 560.f * kScreenRatio
+let kToastBottomCenterSpace = 420.f * kScreenRatio
+let kBannerHeight = 120.f
+let kNormalItemHeight = 180.f
+let kAdItemHeight = 170.f
+let kLiveItemHeight = 150.f
+let kShadowImageHeight = 50.f
+let kTipCellHeight = 30.f
+let kCornerRadius = 6.f
+let kCollectionItemPadding = 15.f
+let kDislikeViewMaxWidth = 330.f
+let kDislikeViewMinWidth = 180.f
+let kDislikeCellHeight = 60.f
+let kDislikeFooterHeight = 150.f
+let kLivePartitionRefreshRotationTime: Double = 0.5
+let kTimeoutIntervalForRequest: TimeInterval = 10
+let kRecommendRecallDislikeMinute = 2
+
+let isIphoneX = kScreenWidth >= 375.f && kScreenHeight >= 812.f
+
+let bannerModuleId = 1
+let regionModuleId = 2
+let rcmdModuleId = 3
+let hourRankModuleId = 4
+let attentionModuleId = 13

+ 53 - 0
JiaPeiManage/Sources/Networking/NetEnvironment.swift

@@ -0,0 +1,53 @@
+//
+//  NetEnvironment.swift
+//  SwiftBilibili
+//
+//  Created by 罗文 on 2021/6/11.
+//  Copyright © 2021年 罗文. All rights reserved.
+//
+
+import Foundation
+import SwiftyUserDefaults
+
+enum NetEnvironment: String, DefaultsSerializable {
+    case dev = "测试服"  //测试环境
+    case res = "正式服"  //线上
+    
+//    public static var _defaults: DefaultsCodableBridge<Self> { return DefaultsCodableBridge() }
+//    public static var _defaultsArray: DefaultsCodableBridge<[Self]> { return DefaultsCodableBridge() }
+}
+
+
+enum HttpRequest {
+    case app
+    case api
+    case bangumi
+    case live
+    case http
+    case another
+}
+
+extension HttpRequest {
+    
+    var path: String {
+        
+        let environment = Defaults.currentEnvironment
+
+        switch self {
+        case .app:
+            return environment == .res ? "http://app.bilibili.com" : "app"
+        case .api:
+            return environment == .res ? "http://api.bilibili.com" : "api"
+        case .bangumi:
+            return environment == .res ? "http://bangumi.bilibili.com" : "bangumi"
+        case .live:
+            return environment == .res ? "http://api.live.bilibili.com" : "live"
+        case .http:
+            return environment == .res ? "http://fj.jppt.com.cn/" : "http://192.168.8.87:8080/xm"
+        case .another:
+            return environment == .res ? "https://zzjs.zzxcx.net" : "http://192.168.8.87:8080"
+        }
+    }
+}
+
+

+ 102 - 0
JiaPeiManage/Sources/Networking/Networking.swift

@@ -0,0 +1,102 @@
+//
+//  Networking.swift
+//  JSJP_Student_sw
+//
+//  Created by Ning.ge on 2023/5/30.
+//
+
+import Moya
+import RxSwift
+import Alamofire
+
+//typealias HomeNetworking = Networking<HomeAPI>
+typealias LoginNetworking = Networking<LoginAPI>
+
+final class Networking<Target: TargetType>: MoyaProvider<Target> {
+    
+    init(plugins: [PluginType] = [LoadingPlugin()]) {
+        
+        let sessionManager: Session = {
+            let configuration = URLSessionConfiguration.default
+            configuration.httpAdditionalHeaders = HTTPHeaders.default.dictionary
+            configuration.timeoutIntervalForRequest = kTimeoutIntervalForRequest
+            return Session(configuration: configuration, startRequestsImmediately: false)
+        }()
+        
+        super.init(endpointClosure:Networking.endpointMapping, session:sessionManager, plugins:plugins)
+     }
+    
+    func request(
+        _ target: Target,
+        _ isCache: Bool = false,
+        file: StaticString = #file,
+        function: StaticString = #function,
+        line: UInt = #line
+    ) -> Single<Response> {
+        let requestString = "\(target.method) \(target.path)"
+        
+        return self.rx.request(target)
+            .filterSuccessfulStatusCodes()
+            .do(onSuccess: { (value) in
+                let message = "SUCCESS: \(requestString) (\(value.statusCode))"
+                log.debug(message, file: file, function: function, line: line)
+                
+            }, onError: {(error) in
+                
+                NotificationCenter.post(customNotification: .netError)
+                
+                if let response = (error as? MoyaError)?.response {
+                    if let jsonObject = try? response.mapJSON(failsOnEmptyData: false) {
+                        let message = "FAILURE: \(requestString) (\(response.statusCode))\n\(jsonObject)"
+                        log.warning(message, file: file, function: function, line: line)
+                    } else if let rawString = String(data: response.data, encoding: .utf8) {
+                        let message = "FAILURE: \(requestString) (\(response.statusCode))\n\(rawString)"
+                        log.warning(message, file: file, function: function, line: line)
+                    } else {
+                        let message = "FAILURE: \(requestString) (\(response.statusCode))"
+                        log.warning(message, file: file, function: function, line: line)
+                    }
+                } else {
+                    let message = "FAILURE: \(requestString)\n\(error)"
+                    log.warning(message, file: file, function: function, line: line)
+                }
+            }, onSubscribed: {
+                let message = "REQUEST: \(requestString)"
+                log.debug(message, file: file, function: function, line: line)
+            })
+    }
+    
+    private static func endpointMapping<Target: TargetType>(target: Target) -> Endpoint {
+    
+        var param: [String:Any] = [:]
+        switch target.task {
+        case let .requestParameters(parameters, _):
+            param = parameters
+        default:break
+        }
+    
+        var url = "\(target.baseURL)\(target.path)?"
+        
+        if target.method == .get {
+            
+            let s = param.map { (key,value) -> String in
+                return "\(key)=\(value)&"
+            }
+            
+            for p in s {
+                url += p
+            }
+            
+            url.remove(at: String.Index(utf16Offset: url.count - 1, in: url))
+            
+            log.info("请求链接:\(url) \n 请求方法:\(target.method)")
+            
+        } else {
+            log.info("请求链接:\(url) \n 参数:\(param) \n 请求方法:\(target.method)")
+        }
+    
+        return MoyaProvider.defaultEndpointMapping(for: target)
+    }
+    
+}
+

+ 34 - 0
JiaPeiManage/Sources/Networking/Plugins/LoadingPlugin.swift

@@ -0,0 +1,34 @@
+//
+//  LoadingPlugin.swift
+//  SwiftBilibili
+//
+//  Created by 罗文 on 2021/1/15.
+//  Copyright © 2021年 罗文. All rights reserved.
+//
+
+import Moya
+//import Result
+
+class LoadingPlugin : PluginType {
+    
+    func willSend(_ request: RequestType, target: TargetType) {
+//        if target is HomeAPI {
+//            let api = target as! HomeAPI
+//            if api.isShowLoading {
+//                guard let curController = UIViewController.topMost else { return }
+//                curController.showLoadingAnimation(superView: curController.view)
+//            }
+//        }
+    }
+    
+    func didReceive(_ result: Result<Response, MoyaError>, target: TargetType) {
+//        if target is HomeAPI {
+//            let api = target as! HomeAPI
+//            if api.isShowLoading {
+//                guard let curController = UIViewController.topMost else { return }
+//                curController.hideLoadingAnimation(superView: curController.view)
+//            }
+//        }
+    }
+
+}

+ 74 - 0
JiaPeiManage/Sources/Networking/RequestError.swift

@@ -0,0 +1,74 @@
+//
+//  RequestError.swift
+//  SwiftBilibili
+//
+//  Created by 罗文 on 2021/1/16.
+//  Copyright © 2021年 罗文. All rights reserved.
+//
+
+import UIKit
+
+import RxSwift
+
+///定义返回的JSON数据字段
+let RESULT_CODE = "code"//状态码
+let RESULT_MESSAGE = "msg"//错误消息提示
+let RESULT_DATA = "data"//数据包
+let RESULT_RESULT = "result"//数据包
+let RESULT_BODY = "body"//数据包
+let RESULT_OTHER = "other"//数据包
+
+enum RequestError: Swift.Error {
+    case noCodeKey
+    case noDataKey
+    case wrongData
+    case sysError(statusCode: String?, errorMsg: String?)
+}
+
+extension RequestError {
+    
+    var errorDescription: String {
+        
+        switch self {
+        case .noCodeKey,.noDataKey,.wrongData:
+            return "解析数据错误"
+        case .sysError(_, let errorMsg):
+            return errorMsg ?? "无错误信息"
+        }
+    }
+}
+
+
+enum StatusCode: Int {
+    case success = 0
+    case unknow
+}
+
+extension Single {
+    
+    func handleError(_ element: Element? = nil,
+                     showText:String = "似乎与互联网断开连接",
+                     bottomOffset:CGFloat = kToastBottomCenterSpace
+        ) -> Single<Element> {
+        
+        if let element = element {
+            
+            return self.asObservable().catchErrorJustReturn(element).asSingle()
+           
+        }else {
+            return self.asObservable().do(onError: { (error) in
+                if error is RequestError {
+//                    BilibiliToaster.show((error as! RequestError).errorDescription)
+                }else{
+//                    BilibiliToaster.show(showText,bottomOffsetPortrait:bottomOffset)
+                }
+            }).asSingle()
+            
+        }
+    }
+}
+
+
+
+
+

+ 67 - 0
JiaPeiManage/Sources/Rx/Moya+Rx.swift

@@ -0,0 +1,67 @@
+//
+//  Moya+Rx.swift
+//  SwiftBilibili
+//
+//  Created by 罗文 on 2021/1/13.
+//  Copyright © 2021年 罗文. All rights reserved.
+//
+
+import Moya
+import RxSwift
+import SwiftyJSON
+import ObjectMapper
+
+
+extension PrimitiveSequence where Trait == SingleTrait, Element == Moya.Response {
+    
+    func map<T: ImmutableMappable>(_ type: T.Type) -> PrimitiveSequence<Trait, T> {
+        return self
+            .map { (response) -> T in
+                let json = try JSON(data: response.data)
+                guard let code = json[RESULT_CODE].int else { throw RequestError.noCodeKey }
+                if code != StatusCode.success.rawValue { throw RequestError.sysError(statusCode:"\(code)" , errorMsg: json[RESULT_MESSAGE].string) }
+                if let data = json[RESULT_DATA].dictionaryObject {
+                    return try Mapper<T>().map(JSON: data)
+                } else if let data = json[RESULT_RESULT].dictionaryObject {
+                    return try Mapper<T>().map(JSON: data)
+                }
+                
+                throw RequestError.noDataKey
+            }.do(onSuccess: { _ in
+                
+            }, onError: { error in
+                if error is MapError {
+                    log.error(error)
+                }
+            })
+    }
+    
+    func map<T: ImmutableMappable>(_ type: T.Type) -> PrimitiveSequence<Trait, [T]> {
+        return self
+            .map { response -> [T] in
+                let json = try JSON(data: response.data)
+                guard let code = json[RESULT_CODE].int else { throw RequestError.noCodeKey }
+                if code != StatusCode.success.rawValue { throw RequestError.sysError(statusCode:"\(code)" , errorMsg: json[RESULT_MESSAGE].string) }
+                var jsonArray: [Any] = []
+                if let array = json[RESULT_DATA].arrayObject {
+                    jsonArray = array
+                } else if let array = json[RESULT_RESULT].arrayObject {
+                    jsonArray = array
+                } else {
+                    throw RequestError.noDataKey
+                }
+                guard let data = try? JSONSerialization.data(withJSONObject: jsonArray, options: JSONSerialization.WritingOptions.prettyPrinted),
+                      let jsonString = String(data: data, encoding: String.Encoding.utf8)
+                else { throw RequestError.wrongData }
+                
+                return try Mapper<T>().mapArray(JSONString: jsonString)
+                
+            }.do(onSuccess: { _ in
+                
+            }, onError: { error in
+                if error is MapError {
+                    log.error(error)
+                }
+            })
+    }
+}

+ 46 - 0
JiaPeiManage/Sources/Rx/NotificationCenter+Rx.swift

@@ -0,0 +1,46 @@
+//
+//  NotificationCenter+Rx.swift
+//  SwiftBilibili
+//
+//  Created by 罗文 on 2021/1/21.
+//  Copyright © 2021年 罗文. All rights reserved.
+//
+
+import RxSwift
+
+enum BiliNotification: String {
+    
+    case netError
+    case stopRotate
+    case startRequest
+    case endRequest
+    
+    var stringValue: String {
+        
+        return "Bilibili" + rawValue
+    }
+    
+    var notificationName: NSNotification.Name {
+        
+        return NSNotification.Name(stringValue)
+    }
+}
+
+extension NotificationCenter {
+    
+   static func post(customNotification name: BiliNotification,object: Any? = nil) {
+        
+        NotificationCenter.default.post(name: name.notificationName, object: object)
+    }
+    
+}
+
+extension Reactive where Base: NotificationCenter {
+    
+    func notification(custom name: BiliNotification,object: AnyObject? = nil) -> Observable<Notification>
+    {
+        
+        return notification(name.notificationName, object: object)
+        
+    }
+}

+ 23 - 0
JiaPeiManage/Sources/Rx/PullToRefresh+Rx.swift

@@ -0,0 +1,23 @@
+//
+//  PullToRefresh+Rx.swift
+//  SwiftBilibili
+//
+//  Created by 罗文 on 2021/1/20.
+//  Copyright © 2021年 罗文. All rights reserved.
+//
+import RxCocoa
+import RxSwift
+import ESPullToRefresh
+
+extension Reactive where Base: ESRefreshHeaderView {
+    
+    var isRefreshing: Binder<Bool> {
+        return Binder(self.base) { headerView, refresh in
+            if refresh {
+               headerView.startRefreshing()
+            } else {
+               headerView.stopRefreshing()
+            }
+        }
+    }
+}

+ 20 - 0
JiaPeiManage/Sources/Rx/UICollectionView+Rx.swift

@@ -0,0 +1,20 @@
+//
+//  UICollectionView+Rx.swift
+//  SwiftBilibili
+//
+//  Created by 罗文 on 2021/1/20.
+//  Copyright © 2021年 罗文. All rights reserved.
+//
+
+import RxCocoa
+import RxDataSources
+import RxSwift
+
+extension Reactive where Base: UICollectionView {
+    func itemSelected<S>(dataSource: CollectionViewSectionedDataSource<S>) -> ControlEvent<S.Item> {
+        let source = self.itemSelected.map { indexPath in
+            dataSource[indexPath]
+        }
+        return ControlEvent(events: source)
+    }
+}

+ 11 - 0
JiaPeiManage/Sources/Rx/UICollectionViewFlexLayout+Rx.swift

@@ -0,0 +1,11 @@
+//
+//  UICollectionViewFlexLayout+Rx.swift
+//  SwiftBilibili
+//
+//  Created by 罗文 on 2021/1/22.
+//  Copyright © 2021年 罗文. All rights reserved.
+//
+
+import RxCocoa
+
+

+ 21 - 0
JiaPeiManage/Sources/Rx/UILabel+Rx.swift

@@ -0,0 +1,21 @@
+//
+//  UILabel+Rx.swift
+//  SwiftBilibili
+//
+//  Created by 罗文 on 2021/3/6.
+//  Copyright © 2021年 罗文. All rights reserved.
+//
+
+import UIKit
+
+import RxCocoa
+import RxSwift
+
+extension Reactive where Base: UILabel {
+    
+    var textColor: Binder<UIColor?> {
+        return Binder(self.base) { view,color in
+            view.textColor = color ?? .clear
+        }
+    }
+}

+ 24 - 0
JiaPeiManage/Sources/Rx/UIScrollView+Rx.swift

@@ -0,0 +1,24 @@
+//
+//  UIScrollView+Rx.swift
+//  SwiftBilibili
+//
+//  Created by 罗文 on 2021/1/13.
+//  Copyright © 2021年 罗文. All rights reserved.
+//
+
+import RxCocoa
+import RxSwift
+
+extension Reactive where Base: UIScrollView {
+    
+    var isReachedBottom: ControlEvent<Void> {
+        let source = self.contentOffset
+            .filter { [weak base = self.base] offset in
+                guard let base = base else { return false }
+                return base.isReachedBottom(withTolerance: base.frame.height * 0.7)
+            }
+            .map { _ in Void() }
+        return ControlEvent(events: source)
+    }
+}
+

+ 32 - 0
JiaPeiManage/Sources/Rx/UIView+Rx.swift

@@ -0,0 +1,32 @@
+//
+//  UIView+Rx.swift
+//  SwiftBilibili
+//
+//  Created by 罗文 on 2021/2/1.
+//  Copyright © 2021年 罗文. All rights reserved.
+//
+
+import UIKit
+
+import RxCocoa
+import RxSwift
+
+extension Reactive where Base: UIView {
+    var setNeedsLayout: Binder<Void> {
+        return Binder(self.base) { view, _ in
+            view.setNeedsLayout()
+        }
+    }
+    
+    var backgroundColor: Binder<UIColor?> {
+        return Binder(self.base) { view,color in
+            view.backgroundColor = color
+        }
+    }
+    
+    var borderColor: Binder<UIColor?> {
+        return Binder(self.base) { view,color in
+            view.borderColor = color ?? .clear
+        }
+    }
+}

+ 118 - 0
JiaPeiManage/Sources/Services/LoginAPI.swift

@@ -0,0 +1,118 @@
+//
+//  LoginAPI.swift
+//  JiaPeiManage
+//
+//  Created by Ning.ge on 2023/6/12.
+//
+import Moya
+import SwiftyUserDefaults
+
+enum LoginAPI {
+    
+    //登录
+    case loginUser(user_name:String, user_password:String)
+    //修改密码
+    case updateUser(user_name:String, user_password:String,code:String)
+    
+}
+
+extension LoginAPI: TargetType {
+    
+    var baseURL: URL {
+        switch self {
+        case .loginUser,.updateUser:
+            return URL(string: HttpRequest.http.path)!
+        }
+    }
+    
+    var path: String {
+        switch self {
+        case .loginUser(_,_):
+            return "appservice/student/stuLogin" //basePath(path: "appservice/student/stuLogin")
+        case .updateUser(_,_,_):
+            return "appservice/student/update"
+       
+        }
+    }
+    
+    var method: Moya.Method {
+        switch self {
+        case .loginUser,.updateUser:
+            return .post
+        default:
+            return .get
+        }
+    }
+    
+    var sampleData: Data {
+        return Data()
+    }
+    
+    var task: Moya.Task {
+        switch self {
+        case .loginUser(_,_):
+            if let parameters = parameters {
+//                return .requestCompositeParameters(bodyParameters: parameters, bodyEncoding: URLEncoding.httpBody, urlParameters: sign(with: urlParameters!)!)
+                return .requestParameters(parameters: parameters, encoding: URLEncoding.default)
+            }
+            return .requestPlain
+        case .updateUser(_,_,_):
+            if let parameters = parameters {
+                return .requestParameters(parameters: parameters, encoding: URLEncoding.default)
+            }
+            return .requestPlain
+        }
+    }
+    
+    var headers: [String: String]? {
+        switch self {
+        case .loginUser:
+            var urlParameters = urlParameters!
+            let sgin = "" // signStr(with: urlParameters)
+            urlParameters["sign"] = sgin
+            // 遍历字典的键值对及索引
+            var hs:[String: String] = [:]
+            for (index, (key, value)) in urlParameters.enumerated() {
+                // 如果 value 为空则跳过
+                let vv = value as? String
+                if vv!.count == 0 { continue }
+                hs[key] = vv
+            }
+            return hs
+        default:
+            return ["Content-Type":"application/json"]
+        }
+    }
+    
+    var parameters: [String : Any]? {
+        switch self {
+        case let .loginUser(user_name, user_password):
+            
+            return ["loginCode": user_name,
+                    "password": "",
+                    "version": NYMacros.appVersion ?? "1.1.0",
+                    "appType": "2",
+                    "systemVersion": String(format: "iOS %.1f", NYMacros.iOSVersion),//手机操作系统版本
+                    "photo": "",
+                    "sex": "",
+                    "nickName": "",
+                    "openId": "",
+                    "loginType": "1",]
+        case let .updateUser(user_name, user_password, code):
+            return ["username": user_name, "password": user_password,"code":code];
+        default:
+            return nil
+        }
+    }
+    
+    var urlParameters: [String: Any]? {
+        var parameters = parameters
+//        if let extendsParameters = ["":""] {
+//            parameters = parameters?.merging(extendsParameters) { $1 } //组合
+//        }
+        return parameters
+    }
+    
+    
+    
+}

+ 34 - 0
JiaPeiManage/Sources/Services/LoginService.swift

@@ -0,0 +1,34 @@
+//
+//  LoginService.swift
+//  JiaPeiManage
+//
+//  Created by Ning.ge on 2023/6/12.
+//
+
+import RxSwift
+import RxCocoa
+import SwiftyJSON
+
+protocol LoginServiceType {
+
+    func loginRequest(user_name: String, user_password: String) -> Single<UserInfo>
+    
+}
+
+final class LoginService: LoginServiceType {
+    
+    private let networking : LoginNetworking
+    
+    init(networking: LoginNetworking) {
+        self.networking = networking
+    }
+    
+    func loginRequest(user_name: String, user_password: String) -> RxSwift.Single<UserInfo> {
+        
+        let api = LoginAPI.loginUser(user_name: user_name, user_password: user_password)
+        return networking.request(api).map(UserInfo.self)
+    }
+    
+ 
+    
+}

+ 23 - 0
JiaPeiManage/Sources/Utils/LocalManager.swift

@@ -0,0 +1,23 @@
+//
+//  LocalManager.swift
+//  SwiftBilibili
+//
+//  Created by 罗文 on 2021/3/23.
+//  Copyright © 2021年 罗文. All rights reserved.
+//
+
+import UIKit
+import SwiftyUserDefaults
+
+class LocalManager {
+    
+    
+    static var userInfo: UserInfo = UserInfo()
+    
+    
+    class func clearUserInfo() {
+        
+        
+    }
+    
+}

+ 77 - 0
JiaPeiManage/Sources/Utils/NYSwRouter.swift

@@ -0,0 +1,77 @@
+//
+//  NYSwRouter.swift
+//  JSJP_Student_sw
+//
+//  Created by Ning.ge on 2023/5/30.
+//
+
+import UIKit
+
+import URLNavigator
+
+
+enum NYSwPushType {
+    case recommend_rank
+    case recommend_player
+    case live_room
+    case live_all
+    case drama_recommend
+}
+
+
+enum NYSwOpenType: String {
+    case area = "http://live.bilibili.com/app/area"
+    case common = "http://live.bilibili.com/app/mytag/"
+    case attention = "http://live.bilibili.com/app/myfollow/"
+    case all = "http://live.bilibili.com/app/all-live/"
+    
+    case login = "Bilibili://app/login"
+}
+
+extension NYSwPushType {
+
+    var path:String {
+        switch self {
+        case .recommend_rank:
+            return "Bilibili://recommend/rank"
+        case .recommend_player:
+            return "Bilibili://recommend/player"
+        case .live_all:
+            return "Bilibili://live/recommend"
+        case .live_room:
+            return "Bilibili://live/room"
+        case .drama_recommend:
+            return "Bilibili://drama/recommend"
+
+        }
+    }
+}
+
+class NYSwRouter {
+    
+    @discardableResult
+    class func push(_ type:NYSwPushType, context: Any? = nil) -> UIViewController? {
+       
+       return navigator.push(type.path, context: context)
+    }
+    
+    @discardableResult
+    class func push(_ url:String) -> UIViewController? {
+        
+        return navigator.push(url)
+    }
+    
+    @discardableResult
+    class func open(_ url:String) -> Bool? {
+        
+        guard let header = url.components(separatedBy: "?").first,
+              let _ = NYSwOpenType(rawValue: header)
+        else {
+            NYSwToaster.show("需要跳转的路径未找到,请先注册!")
+            return nil
+        }
+        
+        return navigator.open(url)
+    }
+}
+

+ 27 - 0
JiaPeiManage/Sources/Utils/Snap.swift

@@ -0,0 +1,27 @@
+//
+//  Snap.swift
+//  SwiftBilibili
+//
+//  Created by 罗文 on 2021/1/13.
+//  Copyright © 2021年 罗文. All rights reserved.
+//
+
+import UIKit
+
+/// Ceil to snap pixel
+func snap(_ x: CGFloat) -> CGFloat {
+    let scale = UIScreen.main.scale
+    return ceil(x * scale) / scale
+}
+
+func snap(_ point: CGPoint) -> CGPoint {
+    return CGPoint(x: snap(point.x), y: snap(point.y))
+}
+
+func snap(_ size: CGSize) -> CGSize {
+    return CGSize(width: snap(size.width), height: snap(size.height))
+}
+
+func snap(_ rect: CGRect) -> CGRect {
+    return CGRect(origin: snap(rect.origin), size: snap(rect.size))
+}

+ 123 - 0
JiaPeiManage/Sources/Utils/URLNavigationMap.swift

@@ -0,0 +1,123 @@
+//
+//  URLNavigationMap.swift
+//  SwiftBilibili
+//
+//  Created by 罗文 on 2021/1/16.
+//  Copyright © 2021年 罗文. All rights reserved.
+//
+
+import UIKit
+import URLNavigator
+
+final class URLNavigationMap {
+    
+    static func initialize(navigator:NavigatorType) {
+        
+        navigator.register(NYSwPushType.recommend_rank.path) { (url, values, context) -> UIViewController? in
+            
+            guard let context = context as? [String:Bool],
+                  let _ = context["isFromRcmd"]
+            else { return nil }
+            
+            let rankParentVc = UIViewController()
+            
+//            RankParentViewController()
+            
+            return rankParentVc
+        }
+        
+        navigator.register(NYSwPushType.live_room.path) { (url, values, context) -> UIViewController? in
+            let roomViewController = UIViewController()
+            
+            //LiveRoomViewController()
+            return roomViewController
+        }
+        
+        navigator.register(NYSwPushType.live_all.path) { (url, values, context) -> UIViewController? in
+            let rcmdParentVc = UIViewController()
+            
+            //LiveAllParentViewController(service:HomeService(networking: HomeNetworking()))
+            return rcmdParentVc
+        }
+        
+        navigator.register(NYSwPushType.recommend_player.path) { (url, values, context) -> UIViewController? in
+            let testVc = UIViewController()
+//            TestViewController()
+            return testVc
+        }
+        
+        navigator.register(NYSwPushType.drama_recommend.path) { (url, values, context) -> UIViewController? in
+            
+            guard let context = context as? [String:Bool],
+                  let isRcmd = context["isRcmd"]
+            else { return nil }
+
+            let rcmdVc = UIViewController()
+//            DramaRcmdViewController(isRcmd: isRcmd)
+            
+            return rcmdVc
+        }
+        
+        navigator.register("http://<path:_>",self.webViewControllerFactory)
+        navigator.register("https://<path:_>",self.webViewControllerFactory)
+        navigator.handle(NYSwOpenType.area.rawValue, self.area(navigator: navigator))
+        navigator.handle(NYSwOpenType.all.rawValue, self.all(navigator: navigator))
+        navigator.handle(NYSwOpenType.login.rawValue, self.login(navigator: navigator))
+    }
+    
+    private static func webViewControllerFactory(
+        url: URLConvertible,
+        values: [String: Any],
+        context: Any?
+        ) -> UIViewController? {
+        
+        let link = url.urlStringValue
+        
+        if link.contains("read") {
+            return NYArticleViewController(link: link)
+        } else {
+            return NYWebViewController(link: url.urlStringValue)
+        }
+    }
+    
+    private static func area(navigator: NavigatorType) -> URLOpenHandlerFactory {
+        return { url, values, context in
+
+            let parent_area_id = url.queryParameters["parent_area_id"]!
+            let parent_area_name = url.queryParameters["parent_area_name"]!
+            let area_id = url.queryParameters["area_id"]!
+            let area_name = url.queryParameters["area_name"]!
+            
+            let partitionController = UIViewController()
+//            LivePartitionViewController(parent_area_id: parent_area_id,
+//                                                                  parent_area_name: parent_area_name,
+//                                                                  area_id: area_id,
+//                                                                  area_name: area_name)
+            navigator.push(partitionController)
+            return true
+        }
+    }
+    
+    private static func all(navigator: NavigatorType) -> URLOpenHandlerFactory {
+        return { url, values, context in
+            
+            let allParentVc = UIViewController()
+            
+//            LiveAllParentViewController(service: HomeService(networking: HomeNetworking()))
+            navigator.push(allParentVc)
+            return true
+        }
+    }
+    
+    private static func login(navigator: NavigatorType) -> URLOpenHandlerFactory {
+        return { url, values, context in
+            
+            let loginController = UIViewController()
+//            LoginViewController()
+            navigator.present(loginController, wrap: MainNavigationController.self)
+            return true
+        }
+    }
+    
+}
+

+ 1 - 1
Podfile

@@ -53,7 +53,7 @@ target 'JiaPeiManage' do
   pod 'ESPullToRefresh'
   pod 'ReachabilitySwift'
   pod 'Then'
-  pod 'URLNavigator'
+  pod 'URLNavigator', '~> 2.2.0'
   pod 'SwiftyColor'
   pod 'SwiftyImage'
   pod 'SwiftTimer'

+ 4 - 4
Podfile.lock

@@ -363,7 +363,7 @@ PODS:
   - SwiftyUserDefaults (5.3.0)
   - Then (3.0.0)
   - Toaster (2.3.0)
-  - URLNavigator (2.4.1)
+  - URLNavigator (2.2.0)
   - VTMagic (1.2.4):
     - VTMagic/Core (= 1.2.4)
   - VTMagic/Core (1.2.4)
@@ -406,7 +406,7 @@ DEPENDENCIES:
   - SwiftyUserDefaults
   - Then
   - Toaster
-  - URLNavigator
+  - URLNavigator (~> 2.2.0)
   - VTMagic (from `https://github.com/tianzhuo112/VTMagic.git`)
   - YYText
 
@@ -500,11 +500,11 @@ SPEC CHECKSUMS:
   SwiftyUserDefaults: 63f80248cf5bfb3458825d9a78f2eb7e1293a040
   Then: 844265ae87834bbe1147d91d5d41a404da2ec27d
   Toaster: c3473963c78e8cabbf6ea6f11ad0fdaae6f54987
-  URLNavigator: 9e277a422a5c131a3e37970c2558e0e866cc22bd
+  URLNavigator: 0bffc3efdeb2d97f7b5eec7ac82d290f4a099431
   VTMagic: b49e5f456dbcbfd9a3588ba92417233a105bc193
   WeakMapTable: 05c694ce8439a7a9ebabb56187287a63c57673d6
   YYText: 5c461d709e24d55a182d1441c41dc639a18a4849
 
-PODFILE CHECKSUM: 7b7dde0f5c301eea45d0e3346c7c705487e138e6
+PODFILE CHECKSUM: 7768ce7bf9fffa380f9690528cf4dad8f017e979
 
 COCOAPODS: 1.12.1

+ 4 - 4
Pods/Manifest.lock

@@ -363,7 +363,7 @@ PODS:
   - SwiftyUserDefaults (5.3.0)
   - Then (3.0.0)
   - Toaster (2.3.0)
-  - URLNavigator (2.4.1)
+  - URLNavigator (2.2.0)
   - VTMagic (1.2.4):
     - VTMagic/Core (= 1.2.4)
   - VTMagic/Core (1.2.4)
@@ -406,7 +406,7 @@ DEPENDENCIES:
   - SwiftyUserDefaults
   - Then
   - Toaster
-  - URLNavigator
+  - URLNavigator (~> 2.2.0)
   - VTMagic (from `https://github.com/tianzhuo112/VTMagic.git`)
   - YYText
 
@@ -500,11 +500,11 @@ SPEC CHECKSUMS:
   SwiftyUserDefaults: 63f80248cf5bfb3458825d9a78f2eb7e1293a040
   Then: 844265ae87834bbe1147d91d5d41a404da2ec27d
   Toaster: c3473963c78e8cabbf6ea6f11ad0fdaae6f54987
-  URLNavigator: 9e277a422a5c131a3e37970c2558e0e866cc22bd
+  URLNavigator: 0bffc3efdeb2d97f7b5eec7ac82d290f4a099431
   VTMagic: b49e5f456dbcbfd9a3588ba92417233a105bc193
   WeakMapTable: 05c694ce8439a7a9ebabb56187287a63c57673d6
   YYText: 5c461d709e24d55a182d1441c41dc639a18a4849
 
-PODFILE CHECKSUM: 7b7dde0f5c301eea45d0e3346c7c705487e138e6
+PODFILE CHECKSUM: 7768ce7bf9fffa380f9690528cf4dad8f017e979
 
 COCOAPODS: 1.12.1

+ 156 - 156
Pods/Pods.xcodeproj/project.pbxproj

@@ -163,6 +163,7 @@
 		1B34557401699C70474B5122D19803FE /* QMUITips.m in Sources */ = {isa = PBXBuildFile; fileRef = 1678FDB6A0D282D3BF26F98ADE675A24 /* QMUITips.m */; settings = {COMPILER_FLAGS = "-w -Xanalyzer -analyzer-disable-all-checks"; }; };
 		1B4D1BDCBAA50F2F0E2DD4A04C889449 /* VTContentView.m in Sources */ = {isa = PBXBuildFile; fileRef = EED17A7719633A5ABFDE22A64BA40CE1 /* VTContentView.m */; settings = {COMPILER_FLAGS = "-w -Xanalyzer -analyzer-disable-all-checks"; }; };
 		1B4F0BAB44FB6A9290FF67ECE06A1E60 /* Reactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F5BEE1568C0A6C67A7FA2168EA36186 /* Reactor.swift */; };
+		1B5F478892CFF682720BF3336F1A7575 /* NavigatorDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41DA50CAD1386AC15411BDB20C75B849 /* NavigatorDelegate.swift */; };
 		1B822B673B30918B714541EBC63E5D6F /* Driver+Subscription.swift in Sources */ = {isa = PBXBuildFile; fileRef = F849E28CE0D64681C67AF6CC8B0567FE /* Driver+Subscription.swift */; };
 		1BC68CA6F748EE43E17297AB398112EE /* AutoCurry.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA3BE029553DA6CA0C7EEC8BD5DBB175 /* AutoCurry.swift */; };
 		1BF5C253CD1A2D5830DED9BDA22032B1 /* UITableViewHeaderFooterView+QMUI.m in Sources */ = {isa = PBXBuildFile; fileRef = CE99DCA936AC12CEA2420BDCF4141293 /* UITableViewHeaderFooterView+QMUI.m */; settings = {COMPILER_FLAGS = "-w -Xanalyzer -analyzer-disable-all-checks"; }; };
@@ -224,6 +225,7 @@
 		243D0CC47C2D7AA840A9E9028B319394 /* DefaultsBridges.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7D95047150C624CCF0ABD9C9E477CF5F /* DefaultsBridges.swift */; };
 		243D7CFE1D56ED80ACB2B3E71B4CB603 /* AuthenticationChallengeResponsable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 905B7C5301A761AD5C22831CA6D824DC /* AuthenticationChallengeResponsable.swift */; };
 		244F4A02905D8B8672286B4BCC5B7480 /* Toaster-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = D1AC35B8A7F1D0ADA2A2684DD45D289B /* Toaster-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; };
+		24AD273272A49A0C36649ADA5F54A492 /* URLPathComponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD79B0F4034471F19AFE25543213F83A /* URLPathComponent.swift */; };
 		24BF916B3481F1D8E84F91E1B3295448 /* RxSearchControllerDelegateProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD610237DE73DF49BADADAB93E9534F7 /* RxSearchControllerDelegateProxy.swift */; };
 		2503DB826D123C40B7BBACD44C9FB295 /* RLMQueryUtil.mm in Sources */ = {isa = PBXBuildFile; fileRef = 844C8988C52FB411840FB9AE82312123 /* RLMQueryUtil.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"10.39.1\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC -w -Xanalyzer -analyzer-disable-all-checks"; }; };
 		25562B09D11D479D2887F3B5A4397BFA /* UIViewController+VTMagic.m in Sources */ = {isa = PBXBuildFile; fileRef = 8637AEC59FCE463805D842C95405288E /* UIViewController+VTMagic.m */; settings = {COMPILER_FLAGS = "-w -Xanalyzer -analyzer-disable-all-checks"; }; };
@@ -238,7 +240,6 @@
 		27D9F773AB09ACEA1463B46594D36905 /* RLMSyncSubscription_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 6135D893675A768DA5E505883CCEA1B6 /* RLMSyncSubscription_Private.h */; };
 		27E5B32572C93220F6DCF56FC8203E40 /* QMUIEasings.h in Headers */ = {isa = PBXBuildFile; fileRef = 876DC19D559703DD767403A9E348B5EC /* QMUIEasings.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		280349E8E0AB963B5C2A9BD76F237482 /* RLMDictionary.mm in Sources */ = {isa = PBXBuildFile; fileRef = EAB3C7CE6639A9BC3B4B7E1FD14A1CF5 /* RLMDictionary.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"10.39.1\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC -w -Xanalyzer -analyzer-disable-all-checks"; }; };
-		2827ECA7A7232E3278EB22CF36E9C9B1 /* Navigator.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3F4A202ED73579A91143995DB5F9A2E /* Navigator.swift */; };
 		282FCD4EA8B05830E9108690B8CD336B /* Session.swift in Sources */ = {isa = PBXBuildFile; fileRef = 166396A286BA21A2046CC924A22F3A45 /* Session.swift */; };
 		2907943C25D184F1F2522CCB62E3A34D /* DelaySubscription.swift in Sources */ = {isa = PBXBuildFile; fileRef = D623708E1536699353690B5865BD2E64 /* DelaySubscription.swift */; };
 		2992ACF4432F037B1A01338AAF514A32 /* VTContentView.h in Headers */ = {isa = PBXBuildFile; fileRef = BD21246B91438A552C88870743B96A66 /* VTContentView.h */; settings = {ATTRIBUTES = (Public, ); }; };
@@ -281,13 +282,13 @@
 		2EF2B2F6D906D893E5F6A43751D688C3 /* ESPullToRefresh-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 44F91C1826ECFF959782E0214B553785 /* ESPullToRefresh-dummy.m */; };
 		2F1C81FFAF2C041C202077FCEA779544 /* Reactive.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76C6F96692A40F828DDC25C890308D79 /* Reactive.swift */; };
 		2F9125FBA55F593BF81F07C1EEDF3406 /* Timeout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3669C3A9D950265BA93733F03ADACE1A /* Timeout.swift */; };
-		3014E01D888D6B31280E0FEE7CA29A85 /* UIViewController+TopMostViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A04A9AB9AA9EA59F9FA021E69CA5AEA7 /* UIViewController+TopMostViewController.swift */; };
 		3016A88B34A06EA21603FBED38426F00 /* QMUIConsoleToolbar.m in Sources */ = {isa = PBXBuildFile; fileRef = BF6E6A165ECFCA2887354A9262E5147A /* QMUIConsoleToolbar.m */; settings = {COMPILER_FLAGS = "-w -Xanalyzer -analyzer-disable-all-checks"; }; };
 		305F646B9C8926440A69812E0394D77C /* RLMArray.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8D0A38D21D87E9D4E593E86FBEAC67E5 /* RLMArray.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"10.39.1\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC -w -Xanalyzer -analyzer-disable-all-checks"; }; };
 		30895AA596D8099D30400E1764910EAE /* QMUISegmentedControl.m in Sources */ = {isa = PBXBuildFile; fileRef = 1E14947EE9C7A8F3289E25112D6930A6 /* QMUISegmentedControl.m */; settings = {COMPILER_FLAGS = "-w -Xanalyzer -analyzer-disable-all-checks"; }; };
 		3091F1A82E0FD6ABA766EBC91D4E5DE6 /* Then.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDCB8344C4C93F91D288B7E489EE0C3D /* Then.swift */; };
 		30CD689EDBC5A7C878C1145F03E84990 /* UIFont+QMUI.m in Sources */ = {isa = PBXBuildFile; fileRef = C5B65B3D7F4235528CEB505B5E236B2F /* UIFont+QMUI.m */; settings = {COMPILER_FLAGS = "-w -Xanalyzer -analyzer-disable-all-checks"; }; };
 		30E0D73CBD41C228A52EA92ACE9BA014 /* AccessTokenPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE5B7C1F7996B965BB77B4BEDE0CBE35 /* AccessTokenPlugin.swift */; };
+		316AA4C2C204FE29F00D8996C2171ECD /* UIViewController+TopMostViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BBF41284CADBE7BD04147D8537C0D4A /* UIViewController+TopMostViewController.swift */; };
 		31760C5454140309E156F5AC84BBD5CE /* UIInterface+QMUI.h in Headers */ = {isa = PBXBuildFile; fileRef = DCCB5F93C023B71FEE6B6D1040864C15 /* UIInterface+QMUI.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		31884986042116DD80147FAFA9EF9482 /* ConcurrentMainScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B4CC11E5DA5B2B882C561A1CC4CFCFD /* ConcurrentMainScheduler.swift */; };
 		318AFF729D2DE955120F0BDEC6A58BD6 /* RLMAPIKeyAuth.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = 534F6EBF8863D9B435DD56E5656B5A4C /* RLMAPIKeyAuth.h */; };
@@ -297,7 +298,6 @@
 		320C0D7A88AA812EFB20016BA1F5D7AE /* AFError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7489B80C9767809D731B54FE3082121A /* AFError.swift */; };
 		325CCDB295FFA016638075B5D2CEDD5C /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A28FFA5AD40AF2BE31120F5E1935C751 /* Foundation.framework */; };
 		3263F8B7FAA1214FAD93F69AFD95D227 /* CGRect+ManualLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = A541378F41B0CB170EA3123BED7D62B5 /* CGRect+ManualLayout.swift */; };
-		327167585B084C3F360FF7498CB8DADA /* URLPatchComponentMatchResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8FA305CA426AF1A5E57E87FE34946AE /* URLPatchComponentMatchResult.swift */; };
 		32F0993C8D1EF5B2FB81B8C8BAFE4287 /* QMUIMoreOperationController.h in Headers */ = {isa = PBXBuildFile; fileRef = B96A47777EB992CEFCA4FD4BE93D4250 /* QMUIMoreOperationController.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		33012BA293EA9A6F4437312B1A70F164 /* QMUICellSizeKeyCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 27D2BEC5A1622F08595F818CA566FD47 /* QMUICellSizeKeyCache.m */; settings = {COMPILER_FLAGS = "-w -Xanalyzer -analyzer-disable-all-checks"; }; };
 		33B4399678645C669E21AA7D05E234D5 /* NSMethodSignature+QMUI.h in Headers */ = {isa = PBXBuildFile; fileRef = 935DC622119660BC77B1C3E2F9FD775D /* NSMethodSignature+QMUI.h */; settings = {ATTRIBUTES = (Public, ); }; };
@@ -479,6 +479,7 @@
 		4EF23AA478718E60B16137E683E6A684 /* DDAssert.swift in Sources */ = {isa = PBXBuildFile; fileRef = 844605981C823DFE1577F765D492047E /* DDAssert.swift */; };
 		4F37E521D341C47CE73DDCF21BA95A52 /* KingfisherError.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1FF521E6DB7ECF8B1174F56B5F02D2F /* KingfisherError.swift */; };
 		4F44D9BDEC9ECD73D244BB46EF3EF7E8 /* QMUIImagePickerPreviewViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = C6967E8D9F33F9D79253316D3B885420 /* QMUIImagePickerPreviewViewController.m */; settings = {COMPILER_FLAGS = "-w -Xanalyzer -analyzer-disable-all-checks"; }; };
+		4FAE9CC98EEA936D705D84DE9B9F57B1 /* Navigator.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA54C532883855DDF17DE8CA61ADDC01 /* Navigator.swift */; };
 		4FBB681F30753274DB4B90BB49B20D78 /* ToastView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF5B236AE78D54A3A7B01D5905778729 /* ToastView.swift */; };
 		4FDFBF298AB3B93C0E72DF8B8F432AD4 /* UIColor+QMUITheme.m in Sources */ = {isa = PBXBuildFile; fileRef = D549DEE9D9A49465C36529C9C035EA88 /* UIColor+QMUITheme.m */; settings = {COMPILER_FLAGS = "-w -Xanalyzer -analyzer-disable-all-checks"; }; };
 		506128E1CC424E40E2691546D9547549 /* Placeholder.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED78EDF51E069729CA0A1018DEBF6670 /* Placeholder.swift */; };
@@ -643,7 +644,6 @@
 		6A1BFBEEEFA402CDC912388329D454D3 /* OptionalType.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE99E9DB5CF4B416458C4C92CD888A52 /* OptionalType.swift */; };
 		6A224C9DFE4413925E5DE08450F7D530 /* Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE7E034796DECB62B898B7D5028AEC6A /* Rx.swift */; };
 		6A225A5FAE97408EDD9E3972C2E47263 /* QMUIRuntime.m in Sources */ = {isa = PBXBuildFile; fileRef = 078799435D4E7BEA80AC18F56FBD99BF /* QMUIRuntime.m */; settings = {COMPILER_FLAGS = "-w -Xanalyzer -analyzer-disable-all-checks"; }; };
-		6A6E33CF6687DB2A90875127C21A46A6 /* URLMatchResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = D299F45ACDCCBEEAFD09338C80EF6C87 /* URLMatchResult.swift */; };
 		6A79F58E73D6169FECF4606D198736F5 /* QMUIStaticTableViewCellDataSource.h in Headers */ = {isa = PBXBuildFile; fileRef = D9303D914289AD5088C81B10761F6B49 /* QMUIStaticTableViewCellDataSource.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		6AC1B881BB319C89AD023A02CDC8FC3D /* ConstraintLayoutGuide+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = BEA3FD717617A6D10BE194F5845CCDBF /* ConstraintLayoutGuide+Extensions.swift */; };
 		6AE773CDC43873AD079E95360A32A284 /* RLMSwiftObject.h in Headers */ = {isa = PBXBuildFile; fileRef = 3CA5BF3D622A29D348DC98F4AA90D4A9 /* RLMSwiftObject.h */; };
@@ -759,6 +759,7 @@
 		7C2EB40AF37796856068A37DAABAFA63 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AC554520320CB7BC43C805398E0AB728 /* QuartzCore.framework */; };
 		7C42320EC510AF127E5CC6A35A5FF853 /* RLMUpdateResult.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = 833C7D0A771F25D6324879D275E56ADE /* RLMUpdateResult.h */; };
 		7C7418FF01DD7BB909719682B634A8A5 /* SessionDataTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = B706F0CFF99A1DB9DBFF184846578CA5 /* SessionDataTask.swift */; };
+		7C93AF1CF51A2E8777669AE3371058FE /* URLMatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 923148F68079972EE539058BA881D9FC /* URLMatcher.swift */; };
 		7CEAC0F052AAFD79BEC81BF85E40C111 /* RLMAsyncTask.h in Headers */ = {isa = PBXBuildFile; fileRef = 383FFE5773FE4AF718DCAF051982986C /* RLMAsyncTask.h */; };
 		7D19CF17AE75DC75F23000BD1874D057 /* UITableViewHeaderFooterView+QMUI.h in Headers */ = {isa = PBXBuildFile; fileRef = 51C4B0618DF936707559C9FAA13AA346 /* UITableViewHeaderFooterView+QMUI.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		7D38B68C319C02301E356A685E7A46E2 /* QMUIWindowSizeMonitor.h in Headers */ = {isa = PBXBuildFile; fileRef = DD529F9BB99A4ECC4345D35463A1A2C1 /* QMUIWindowSizeMonitor.h */; settings = {ATTRIBUTES = (Public, ); }; };
@@ -791,7 +792,6 @@
 		812DE624EF12F8735C6AA3F521034C72 /* RLMLogger.h in Headers */ = {isa = PBXBuildFile; fileRef = 89582C6448361A8F2AA04C2DA92CBD09 /* RLMLogger.h */; };
 		816FFBC3C531AD18548A1CEBA86FE15E /* DDASLLogCapture.m in Sources */ = {isa = PBXBuildFile; fileRef = BAF1DD19E95FA3C05992F6647EAE9B4C /* DDASLLogCapture.m */; settings = {COMPILER_FLAGS = "-w -Xanalyzer -analyzer-disable-all-checks"; }; };
 		81C3A0068920FC702FE1488DC104992E /* RLMSchema.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = D6FC3E95F9D8F4224E7122FF581E1ED1 /* RLMSchema.h */; };
-		81D122D57B02C7A604B3C7121DA7C824 /* URLNavigator-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 968AA24EEF69EFF1DFE38934E0C5F263 /* URLNavigator-dummy.m */; };
 		81E6A0FF7F286008345ECFA3A2103FA8 /* Alamofire.swift in Sources */ = {isa = PBXBuildFile; fileRef = 215B8ED167EADC145EDE3FCF091FB529 /* Alamofire.swift */; };
 		82302C9954BF33CB68E2979136C44112 /* NSError+RLMSync.m in Sources */ = {isa = PBXBuildFile; fileRef = 71E6A7A1F00B25A0B1E82D8B5A38C1BF /* NSError+RLMSync.m */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"10.39.1\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC -w -Xanalyzer -analyzer-disable-all-checks"; }; };
 		8243A89941D2CE99A29D442991D2B0FA /* UIPageControl+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30F45C5A27B8A053E3F756354788F68D /* UIPageControl+Rx.swift */; };
@@ -942,7 +942,6 @@
 		99596FF7907C4E67FB33140A55EF2B44 /* QMUILogger.h in Headers */ = {isa = PBXBuildFile; fileRef = 9EB50C87B80E24F61D5DCEED773B959D /* QMUILogger.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		997C7E79114E06634C4CA39472E26547 /* _RXKVOObserver.h in Headers */ = {isa = PBXBuildFile; fileRef = 311C77003977E548AF31502D60772F88 /* _RXKVOObserver.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		99A8C7C0E3F60E131720268314124D1A /* UITextView+QMUI.m in Sources */ = {isa = PBXBuildFile; fileRef = C9B9171BB5F81003B705D1EB05A35D1F /* UITextView+QMUI.m */; settings = {COMPILER_FLAGS = "-w -Xanalyzer -analyzer-disable-all-checks"; }; };
-		99BF176C1A7487E6EED2BDDE37FF9B71 /* NavigatorDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A2E3E9AF476D5584320485DC67E60125 /* NavigatorDelegate.swift */; };
 		99E953154DCE72E3FF4503EB4AD3A95A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A28FFA5AD40AF2BE31120F5E1935C751 /* Foundation.framework */; };
 		99FB2910921988A9BCC5533A1EA70E59 /* Region.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3036AA1F4F9507A023DA99EE46311D1A /* Region.swift */; };
 		9AB4BCD57C2D680794BF8B9488DACC98 /* Amb.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA69B519088BBD0B5EFCB2538B082749 /* Amb.swift */; };
@@ -987,7 +986,6 @@
 		A41D0B56E181E5A9E484BF48E2AAF28D /* RLMUtil.mm in Sources */ = {isa = PBXBuildFile; fileRef = 622BA47B2DC3F3615A0FA93F6F8FB9F7 /* RLMUtil.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"10.39.1\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC -w -Xanalyzer -analyzer-disable-all-checks"; }; };
 		A42632AC8B549690E7DB885EE9B3A920 /* Map.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F1AB3699E693CD886C7646D0EF64450 /* Map.swift */; };
 		A523488C60BF961290D0B1ABB9ADACEC /* RequestCompression.swift in Sources */ = {isa = PBXBuildFile; fileRef = E38A9DF1D279A3A15F1F2C836E9FB376 /* RequestCompression.swift */; };
-		A5576CF93240C7B914A6213C81C40B59 /* URLPathComponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD2C3C103EB330C7BF4ED890EE6E4738 /* URLPathComponent.swift */; };
 		A5A8183A83EB65F11F671B7DA53DE8FB /* Moya-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 5B57F1BC41E2166A9A437FBBA6809BFB /* Moya-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		A5A8BF973BFE9C9304372A26C9F2E35B /* ConstraintMakerExtendable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5612F6019A6DF87721AAA0861820C56 /* ConstraintMakerExtendable.swift */; };
 		A603720309585FFB893BB20288253282 /* RLMEvent.mm in Sources */ = {isa = PBXBuildFile; fileRef = 38BD0C783B67BD9A7874E4375BA7EEE1 /* RLMEvent.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"10.39.1\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC -w -Xanalyzer -analyzer-disable-all-checks"; }; };
@@ -1105,7 +1103,6 @@
 		B698E9397CBA97AB80F37307AE25839A /* Repeat.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9351C94D36947B26D928EBFA6EDB2B9F /* Repeat.swift */; };
 		B6B6C7CF4924C91832C2A2580D5AA395 /* DDLoggerNames.h in Headers */ = {isa = PBXBuildFile; fileRef = 79E2A93DD8CD86A176761FA114668A44 /* DDLoggerNames.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		B6C4D2A63C9C55DEFF78BA3E8F6F4A6E /* Throttle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52FE8D4C1147097CEE939FA9601A028E /* Throttle.swift */; };
-		B71E0501C4EAA254B21BAD23474F15D6 /* NavigatorProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC2635374E3A7D8BD350F08DBD347B19 /* NavigatorProtocol.swift */; };
 		B752F7C4BECB65894B1F49421049CE5F /* ConstraintView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 254C683BBDAF736006297ABA426ADA5A /* ConstraintView.swift */; };
 		B784C1E8FB583A4AA328D89038D2DC5B /* TimePeriod.swift in Sources */ = {isa = PBXBuildFile; fileRef = C10A83DDF575C44AC10E7F526188D537 /* TimePeriod.swift */; };
 		B785E35AE212C2CB4C4CD0D84C49B90D /* AsymmetricObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0BD89FEE43F0E76825FC7D9064AD0007 /* AsymmetricObject.swift */; };
@@ -1148,7 +1145,6 @@
 		BE48AB075A8AAE538081A358AF9CB2BD /* Completable.swift in Sources */ = {isa = PBXBuildFile; fileRef = A96E70391544E08E09D50A2B1A394D56 /* Completable.swift */; };
 		BE8BA3D15E5BD128A6ED04BD29C7F93D /* UIColor+VTMagic.m in Sources */ = {isa = PBXBuildFile; fileRef = E924267684C2C8CAB3795E9B29B04530 /* UIColor+VTMagic.m */; settings = {COMPILER_FLAGS = "-w -Xanalyzer -analyzer-disable-all-checks"; }; };
 		BEC6482FD624649AC67E8F349DFF9BD9 /* HistoricalScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49AD0747BB5507811900DBBC97289B85 /* HistoricalScheduler.swift */; };
-		BF19F8647E65E59F741A1F65051076D1 /* URLConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDE652123293AFAD18C4950CC16BA40F /* URLConvertible.swift */; };
 		BF27822ABD5BE2686F4EF57D562D85ED /* QMUICellHeightCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 3C9BDF13DF279E707D275AF7F49A6254 /* QMUICellHeightCache.m */; settings = {COMPILER_FLAGS = "-w -Xanalyzer -analyzer-disable-all-checks"; }; };
 		BF5352E6A21E27DB879C2189155F17EA /* BasicTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = F84C7F24961D55A65AFC17D8D711AFA1 /* BasicTypes.swift */; };
 		BF5B3ED4967EE008A5FDDDEAFE07A84E /* QMUIDialogViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 5D2F768346BE7E5BA1D05AD34D1509B8 /* QMUIDialogViewController.h */; settings = {ATTRIBUTES = (Public, ); }; };
@@ -1169,9 +1165,9 @@
 		C1211757D7840CAB716E96AC8A58D011 /* First.swift in Sources */ = {isa = PBXBuildFile; fileRef = F527B2882FF4467B29F839CB1851668D /* First.swift */; };
 		C14EAF94DFF6C99436052FDA9333FB2F /* ImmutableMappable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13D54A8200C2866ECAABF2A742CA13CB /* ImmutableMappable.swift */; };
 		C16F9108587C4D012FBB50DBD8AA5E77 /* RLMEmailPasswordAuth.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = D4D996E64D0C3891E550580712C3A25F /* RLMEmailPasswordAuth.h */; };
+		C18E4CAC68EF64A35DB0E7DFB9D630B6 /* URLConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = D866E978858454960A2D3A0760CDFB53 /* URLConvertible.swift */; };
 		C1999693D93F4DDC7F886C0BDA1C2264 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A28FFA5AD40AF2BE31120F5E1935C751 /* Foundation.framework */; };
 		C1A3E274E465EFBD757C7C34641B11FB /* UIStepper+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2AF1C3C1D12F5168749AC5ED5776B63 /* UIStepper+Rx.swift */; };
-		C1AADA499E97A0DFE5F52D0227C75145 /* UIViewControllerType.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB92B6D2744FD7C885892836E38D64CF /* UIViewControllerType.swift */; };
 		C21CC70908ACBEE99F98EEE082AF23F3 /* RLMDecimal128.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = 1FEC811039A8AE44D905C262DE2A7FEF /* RLMDecimal128.h */; };
 		C22A0BDBA4358C1379BA7C77C74A3C17 /* FSPageViewTransformer.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6D88D79DD138C6A0335D12C3A2D6EA9 /* FSPageViewTransformer.swift */; };
 		C299C153CCAA101F736736B259631C44 /* YYTextParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 9108A559A937C7B496B3405620A2BE8C /* YYTextParser.m */; settings = {COMPILER_FLAGS = "-w -Xanalyzer -analyzer-disable-all-checks"; }; };
@@ -1186,6 +1182,7 @@
 		C4764D63846DD8AAB0663C08AEE1A001 /* CAAnimation+QMUI.h in Headers */ = {isa = PBXBuildFile; fileRef = 930C9F18270FF7A49B091B8A7AED9D50 /* CAAnimation+QMUI.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		C5272B92B0702E2E256471855B91FB32 /* FloatingPointType+IdentifiableType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EDA9BA9BB6F610C19158564718E56C7 /* FloatingPointType+IdentifiableType.swift */; };
 		C52E0B64E8606502454142E3110C35B0 /* YYTextAsyncLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = 2BD9D640176BD35A5C48A27D71FD4EC7 /* YYTextAsyncLayer.h */; settings = {ATTRIBUTES = (Public, ); }; };
+		C54967210D76333CA09233FE499819C5 /* URLPatchComponentMatchResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6256D969FDE419DCC9EF9A45BBC48D5D /* URLPatchComponentMatchResult.swift */; };
 		C56E369A606D242BDF9021A75E1F1CBE /* UINavigationController+QMUI.m in Sources */ = {isa = PBXBuildFile; fileRef = E29483AA23C6B6667A9C15C5D02A096A /* UINavigationController+QMUI.m */; settings = {COMPILER_FLAGS = "-w -Xanalyzer -analyzer-disable-all-checks"; }; };
 		C6069D49189DCAF8EEC12D1937492D0A /* UITextView+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33D33B6FBAEC96D500CFF7B6A020273B /* UITextView+Rx.swift */; };
 		C60EDD3D619F2C0741E5E911F2AD2BBA /* QMUILog+QMUIConsole.m in Sources */ = {isa = PBXBuildFile; fileRef = C03838346EEDAE6B1892E38440214885 /* QMUILog+QMUIConsole.m */; settings = {COMPILER_FLAGS = "-w -Xanalyzer -analyzer-disable-all-checks"; }; };
@@ -1329,13 +1326,14 @@
 		DEFB7AE04C60ADDF2A50E1E8B07ADDD7 /* GroupedObservable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E21A9F78255F5E4BA13C7986C1EAA6E /* GroupedObservable.swift */; };
 		DF24CE81C8CA841B807B63E1FED38259 /* TransformOf.swift in Sources */ = {isa = PBXBuildFile; fileRef = 843EBF379A4068F07BB72EA4FD323B14 /* TransformOf.swift */; };
 		DF4563832C19B8582C810BF502A5CA29 /* KF.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF43B191C6E29C191497C726A177586F /* KF.swift */; };
+		DF605A22A5D371C66ED79532CDEBA647 /* NavigatorType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C464F8951D64A073119BD14D3504D27 /* NavigatorType.swift */; };
 		DF710146795F6937F2762B2F46BCE91A /* RLMMongoClient.mm in Sources */ = {isa = PBXBuildFile; fileRef = B57CFFE409B7B220A6409BDD0303A4BC /* RLMMongoClient.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"10.39.1\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC -w -Xanalyzer -analyzer-disable-all-checks"; }; };
 		DFAF8F19734481548698DFE068259186 /* NSView+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEF58A822FB0F4B5D516460A16A43013 /* NSView+Rx.swift */; };
 		DFC53CFC1A987B5F5EFCE02602ECCF00 /* UITabBar+QMUI.m in Sources */ = {isa = PBXBuildFile; fileRef = AB8246C290607B12BBEBE17090A01A10 /* UITabBar+QMUI.m */; settings = {COMPILER_FLAGS = "-w -Xanalyzer -analyzer-disable-all-checks"; }; };
 		DFCCEF555AE33802FF7ADD796A9F67FE /* MarginLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FA05868A9C86FC2C918AE4C8CB36D2B /* MarginLabel.swift */; };
 		DFCDE4638265B4CCD494ECA5D560DBEE /* Indicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 99FFE209DA54D143321297AD3ADBFF70 /* Indicator.swift */; };
 		DFD57F7A2E58FBC8E95978E9D5F0B685 /* QMUIImagePickerCollectionViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = E42BA1C61E659049502CDE2DAA06FF02 /* QMUIImagePickerCollectionViewCell.m */; settings = {COMPILER_FLAGS = "-w -Xanalyzer -analyzer-disable-all-checks"; }; };
-		DFF374030016461A00801F75A7824329 /* URLNavigator-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 3AD25D0D847D7EA8C7A6304E590FB750 /* URLNavigator-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; };
+		DFF374030016461A00801F75A7824329 /* URLNavigator-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = F23CE929250FDE9C283C761F359F4EDA /* URLNavigator-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		E0324E2E5B2CAAE629FF0B2FE1C0A3CF /* Lock.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2EC9C140CF9CF513FB0A402D4093D05 /* Lock.swift */; };
 		E04DEC6829F64E872569DF8748D45ABA /* SmartAssign.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8BB075C9BB9ADA14E3FAAF9DA7C9AEF8 /* SmartAssign.swift */; };
 		E068C4789C3392280D36005B2223CAD7 /* PropertyWrappers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 299FD3A96D6681C80BEF84880346B4AA /* PropertyWrappers.swift */; };
@@ -1343,6 +1341,7 @@
 		E0E4FB924E770A7F6066360BF9F21D59 /* DisposeBag.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78AAB6357BF0A4A7B388AC370440CEB5 /* DisposeBag.swift */; };
 		E0F232498D163274CB115E52A96803BC /* NSParagraphStyle+YYText.h in Headers */ = {isa = PBXBuildFile; fileRef = 672B52211C1E4A707F6D80F3CA8E8805 /* NSParagraphStyle+YYText.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		E0F676F594C65B182748EFC109BF842D /* RLMSyncManager.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = 2039A785FDB2C14CA60643F89E2EEC81 /* RLMSyncManager.h */; };
+		E146CB3E6720ED2C5A7A961BF8A1A984 /* URLMatchResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF71C51A566FC52D6EF912C2AC882271 /* URLMatchResult.swift */; };
 		E1BBD9E2DB653AA330A75F7939DE9B7E /* ObserverBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67E125F92092E06D38CE2367B8698411 /* ObserverBase.swift */; };
 		E1CE0EAFB582108D827ADED69A33BAB1 /* CombineLatest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97906246FF38EAE940404ED5B64F362A /* CombineLatest.swift */; };
 		E1D20E684B02FC29B356455D3387468C /* UIControl+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACDFD746E6C908AC11D73DA6A778496F /* UIControl+Rx.swift */; };
@@ -1429,6 +1428,7 @@
 		F0B0D05D7CA84787ABE4A0F75332772A /* QMUILog+QMUIConsole.h in Headers */ = {isa = PBXBuildFile; fileRef = 63BEEB55EA2D6E35E5BDC5B0203C8AC6 /* QMUILog+QMUIConsole.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		F0F41AE3DF92351D568A632DFFE94BDC /* RLMRealm+Sync.mm in Sources */ = {isa = PBXBuildFile; fileRef = EE33138750AE59F6BF3BBF3D536696DD /* RLMRealm+Sync.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"10.39.1\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC -w -Xanalyzer -analyzer-disable-all-checks"; }; };
 		F10A7A9E32700F3F8830519A37035264 /* CocoaLumberjack-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = A746C2AB56452A0845129B37E17ADBA8 /* CocoaLumberjack-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; };
+		F10D12BBC36177350D3C6685C005FDC4 /* UIViewControllerType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 485A5C1D60D6769796318F1C4996B8F1 /* UIViewControllerType.swift */; };
 		F11F7A6AA8D8E5F0619142C445D5C3AC /* RxCollectionViewSectionedAnimatedDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5529CB8E33215AEB3DDD6E084C004B42 /* RxCollectionViewSectionedAnimatedDataSource.swift */; };
 		F147CA50AABD9BFF90293236C1E7E0ED /* MobileCoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D1A8607ABFEBC3F1E628352E218A6BCE /* MobileCoreServices.framework */; };
 		F14F44BB6B9074E59B496F2EC9B08BB4 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A28FFA5AD40AF2BE31120F5E1935C751 /* Foundation.framework */; };
@@ -1453,6 +1453,7 @@
 		F3CACF5A7112D95EBC663C0B1E57E77F /* SwiftyImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8848688DF91BEB70979CD5BB2A2A1475 /* SwiftyImage.swift */; };
 		F4610C06DBF2174D4B20830E6F9E7618 /* RLMPlatform.h in Headers */ = {isa = PBXBuildFile; fileRef = 4EEBA0AD4034FF2D2FD696DA41611ACA /* RLMPlatform.h */; };
 		F4A00ACC9E63E309AFA129D546C86F04 /* CocoaLumberjack-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 0F6BBEE71810C4C985EAC40DF68D815A /* CocoaLumberjack-dummy.m */; };
+		F4A46693AC3A15C53B6FCA6C5F8F7521 /* URLNavigator-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = DC6F1D41B3BF242CBD49D28B7F79F895 /* URLNavigator-dummy.m */; };
 		F4C0DA8AC3BD8315E1C6FBEAE93C59CA /* QMUIKeyboardManager.h in Headers */ = {isa = PBXBuildFile; fileRef = BEC320316DF58208CDB500BAB37E281A /* QMUIKeyboardManager.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		F4C8E57A06E24E18AAF3B2C80ACE35E2 /* UIApplication+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 107A33FDDFEB42DA1ED0D6B9DA1010F8 /* UIApplication+Rx.swift */; };
 		F4DD0C5221AF830DC87E871FA52C20B3 /* RxOptionalError.swift in Sources */ = {isa = PBXBuildFile; fileRef = B76764EE31894EB7AB261F0565A245A2 /* RxOptionalError.swift */; };
@@ -1481,7 +1482,6 @@
 		F8DF281CBE878222C862D2ED3ED4466C /* QMUIAsset.h in Headers */ = {isa = PBXBuildFile; fileRef = C841FA0D40E543AF74FC4BB4BFDD808E /* QMUIAsset.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		F9537B023E24AC4A724E301F7E372491 /* KFImageProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71A6191711E02F25047FEB21E665CCF0 /* KFImageProtocol.swift */; };
 		F953AA9104BFE0C2DAD639EA60104A75 /* ConstraintView+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA72FA9C9967EE4EB33D1E027673DE08 /* ConstraintView+Extensions.swift */; };
-		F97BA357BCC4CB6B9E2E70F4A8CA8435 /* URLMatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 015A11982F54456EE8C2A792F265A5DA /* URLMatcher.swift */; };
 		F999CFEC9CADE77F5EF855F3AF2C69B8 /* RLMObjectBase_Dynamic.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = D29E713F0B8489F3689979BE7562F13C /* RLMObjectBase_Dynamic.h */; };
 		F99DA2744B5986FF29E3448991F82C10 /* QMUIStaticTableViewCellData.h in Headers */ = {isa = PBXBuildFile; fileRef = F9382C57C3870C8E142072B2987AD600 /* QMUIStaticTableViewCellData.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		F9A02C58490047BB3A9797070C329791 /* SwiftyUserDefaults-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 6AAB240792CF479DB3BD6173610FB3D8 /* SwiftyUserDefaults-dummy.m */; };
@@ -2083,7 +2083,6 @@
 		00B75A9A06D8CFE7B18ABCD8675D53DE /* ReachabilitySwift.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = ReachabilitySwift.modulemap; sourceTree = "<group>"; };
 		00EC37A6F5019C04175B37337EA6DA9A /* Images.xcassets */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = QMUIKit/QMUIResources/Images.xcassets; sourceTree = "<group>"; };
 		013B1D960C4B292B1631F6179F83DB73 /* DefaultsSerializable.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = DefaultsSerializable.swift; path = Sources/DefaultsSerializable.swift; sourceTree = "<group>"; };
-		015A11982F54456EE8C2A792F265A5DA /* URLMatcher.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = URLMatcher.swift; path = Sources/URLMatcher/URLMatcher.swift; sourceTree = "<group>"; };
 		015F12FEE89B9FDC1EE51AC45097F84C /* YYTextSelectionView.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = YYTextSelectionView.m; path = YYText/Component/YYTextSelectionView.m; sourceTree = "<group>"; };
 		0175506663A82BAEA6AB51A7F138E2F4 /* Event.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Event.swift; path = RxSwift/Event.swift; sourceTree = "<group>"; };
 		0178574833F541525A933CF3374CC809 /* NSParagraphStyle+YYText.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSParagraphStyle+YYText.m"; path = "YYText/Utility/NSParagraphStyle+YYText.m"; sourceTree = "<group>"; };
@@ -2286,7 +2285,6 @@
 		21AFC3DCD178F91939C508BB4AD1A33E /* UITapGestureRecognizer+RxGesture.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "UITapGestureRecognizer+RxGesture.swift"; path = "Pod/Classes/iOS/UITapGestureRecognizer+RxGesture.swift"; sourceTree = "<group>"; };
 		21B03CA622E690725A6626C088E1D09F /* ReachabilitySwift */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = ReachabilitySwift; path = Reachability.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		224CE8E6573772A1D45ED4DD44393079 /* UIColor+QMUI.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIColor+QMUI.h"; path = "QMUIKit/UIKitExtensions/UIColor+QMUI.h"; sourceTree = "<group>"; };
-		22834AD078AD0E5AC27717A0389DBE78 /* URLNavigator.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = URLNavigator.debug.xcconfig; sourceTree = "<group>"; };
 		22B6CA95660E35B0D15C26585AD0D3C7 /* Dollar */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = Dollar; path = Dollar.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		22C4E200936B399477C1DD0E6EDFB012 /* NSButton+Kingfisher.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "NSButton+Kingfisher.swift"; path = "Sources/Extensions/NSButton+Kingfisher.swift"; sourceTree = "<group>"; };
 		2336FF9D7D3E79643D075D1192B8B6F9 /* ViewTransition.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ViewTransition.swift; path = Sources/RxDataSources/ViewTransition.swift; sourceTree = "<group>"; };
@@ -2436,13 +2434,12 @@
 		3A7E9634CFF37B55C9CD44079BD15B8B /* RequestTaskMap.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RequestTaskMap.swift; path = Source/RequestTaskMap.swift; sourceTree = "<group>"; };
 		3ABEC84774E4E916F929AB5055BA6D53 /* QMUICellHeightCache.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = QMUICellHeightCache.h; path = QMUIKit/QMUIComponents/QMUICellHeightCache.h; sourceTree = "<group>"; };
 		3AC2BC786BCAAA01D67F45CF0F6AA431 /* YYText.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = YYText.debug.xcconfig; sourceTree = "<group>"; };
-		3AD25D0D847D7EA8C7A6304E590FB750 /* URLNavigator-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "URLNavigator-umbrella.h"; sourceTree = "<group>"; };
 		3BA3DA47C7E2A19BA09DC2C5FFFB7878 /* RLMSyncSubscription.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMSyncSubscription.h; path = include/RLMSyncSubscription.h; sourceTree = "<group>"; };
+		3BBF41284CADBE7BD04147D8537C0D4A /* UIViewController+TopMostViewController.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "UIViewController+TopMostViewController.swift"; path = "Sources/URLNavigator/UIViewController+TopMostViewController.swift"; sourceTree = "<group>"; };
 		3BE5CA2FA42F9BB3C0AC449A86C07FCC /* SwiftyColor.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = SwiftyColor.modulemap; sourceTree = "<group>"; };
 		3BE7379DAB819EEB419FFF72A4E218D7 /* MultipartFormData.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = MultipartFormData.swift; path = Sources/Moya/MultipartFormData.swift; sourceTree = "<group>"; };
 		3BFD8B54546614735F07873ED56CC25C /* ReachabilitySwift-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "ReachabilitySwift-prefix.pch"; sourceTree = "<group>"; };
 		3C2C3FDB2743EECCFE77D0DB24720E05 /* UIVisualEffectView+QMUI.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIVisualEffectView+QMUI.m"; path = "QMUIKit/UIKitExtensions/UIVisualEffectView+QMUI.m"; sourceTree = "<group>"; };
-		3C36366803EDE007B754CEB6B07D3DBE /* URLNavigator-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "URLNavigator-Info.plist"; sourceTree = "<group>"; };
 		3C9BDF13DF279E707D275AF7F49A6254 /* QMUICellHeightCache.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = QMUICellHeightCache.m; path = QMUIKit/QMUIComponents/QMUICellHeightCache.m; sourceTree = "<group>"; };
 		3CA5BF3D622A29D348DC98F4AA90D4A9 /* RLMSwiftObject.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMSwiftObject.h; path = include/RLMSwiftObject.h; sourceTree = "<group>"; };
 		3D144DD124A1B4DB20DBF2B1A2149F54 /* ControlEvent+Signal.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "ControlEvent+Signal.swift"; path = "RxCocoa/Traits/Signal/ControlEvent+Signal.swift"; sourceTree = "<group>"; };
@@ -2471,6 +2468,7 @@
 		4168BA1D0C93687E6BF38DD75416D223 /* Then.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = Then.debug.xcconfig; sourceTree = "<group>"; };
 		4173C63C9C2260C59FD9E0AD257A1200 /* URLNavigator */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = URLNavigator; path = URLNavigator.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		4185C1E37625EE61A7DF089D1310FD13 /* Concurrency.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Concurrency.swift; path = Source/Concurrency.swift; sourceTree = "<group>"; };
+		41DA50CAD1386AC15411BDB20C75B849 /* NavigatorDelegate.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NavigatorDelegate.swift; path = Sources/URLNavigator/NavigatorDelegate.swift; sourceTree = "<group>"; };
 		41E3A0CBD8A28CC94F18749516B328FA /* RLMEmbeddedObject.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMEmbeddedObject.h; path = include/RLMEmbeddedObject.h; sourceTree = "<group>"; };
 		41FE55FB4A96373B5DCC0F196331F4C6 /* UISegmentedControl+Rx.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "UISegmentedControl+Rx.swift"; path = "RxCocoa/iOS/UISegmentedControl+Rx.swift"; sourceTree = "<group>"; };
 		420BCDF726959153DC5D1A98799CA720 /* RLMArray_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMArray_Private.h; path = include/RLMArray_Private.h; sourceTree = "<group>"; };
@@ -2521,6 +2519,7 @@
 		47FECB040B0B5CDF7DCC6E4B07B6679C /* RLMSchema.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMSchema.mm; path = Realm/RLMSchema.mm; sourceTree = "<group>"; };
 		4802E191AA37513E230181EFDD275853 /* QMUIEmptyView.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = QMUIEmptyView.h; path = QMUIKit/QMUIComponents/QMUIEmptyView.h; sourceTree = "<group>"; };
 		48171625957B85475380B3312424E4AD /* MultipartUpload.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = MultipartUpload.swift; path = Source/MultipartUpload.swift; sourceTree = "<group>"; };
+		485A5C1D60D6769796318F1C4996B8F1 /* UIViewControllerType.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = UIViewControllerType.swift; path = Sources/URLNavigator/UIViewControllerType.swift; sourceTree = "<group>"; };
 		4897DFA6863CD188A4034CB7B734B887 /* CALayer+QMUI.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "CALayer+QMUI.h"; path = "QMUIKit/UIKitExtensions/CALayer+QMUI.h"; sourceTree = "<group>"; };
 		48998F505A8AD9CBA01CDD4D025AAFC4 /* Deprecated.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Deprecated.swift; path = RxSwift/Deprecated.swift; sourceTree = "<group>"; };
 		48DD377AEB1371F017E84A00E181AA79 /* UINavigationItem+QMUI.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UINavigationItem+QMUI.m"; path = "QMUIKit/UIKitExtensions/UINavigationItem+QMUI.m"; sourceTree = "<group>"; };
@@ -2586,6 +2585,7 @@
 		531819B300AAA91C751AE2CC3CBD1220 /* CGFloatLiteral-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "CGFloatLiteral-dummy.m"; sourceTree = "<group>"; };
 		532D8EB3C76DCEF165851E24DABDE69F /* UIImage+QMUI.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIImage+QMUI.m"; path = "QMUIKit/UIKitExtensions/UIImage+QMUI.m"; sourceTree = "<group>"; };
 		534F6EBF8863D9B435DD56E5656B5A4C /* RLMAPIKeyAuth.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMAPIKeyAuth.h; path = include/RLMAPIKeyAuth.h; sourceTree = "<group>"; };
+		5381019763296D1153BA2EFFF956B239 /* URLNavigator-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "URLNavigator-prefix.pch"; sourceTree = "<group>"; };
 		53A78111E9B708293D7B19E153A6014A /* ConstraintMaker.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ConstraintMaker.swift; path = Sources/ConstraintMaker.swift; sourceTree = "<group>"; };
 		543534767CCC53C7EC62BBFE1E5FF5CF /* RLMObservation.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMObservation.mm; path = Realm/RLMObservation.mm; sourceTree = "<group>"; };
 		547212B1AD3FA9F472D35B9B9868875F /* DelegateProxy.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = DelegateProxy.swift; path = RxCocoa/Common/DelegateProxy.swift; sourceTree = "<group>"; };
@@ -2603,6 +2603,7 @@
 		57277CA03C116B12CE5DB4FB6A8C02F1 /* ConstraintLayoutGuide.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ConstraintLayoutGuide.swift; path = Sources/ConstraintLayoutGuide.swift; sourceTree = "<group>"; };
 		575F524DF315659F5D548A648B11CA19 /* Errors.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Errors.swift; path = RxSwift/Errors.swift; sourceTree = "<group>"; };
 		5767F50756F20F6EC92844160003E2B4 /* UITabBar+QMUIBarProtocol.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UITabBar+QMUIBarProtocol.h"; path = "QMUIKit/UIKitExtensions/QMUIBarProtocol/UITabBar+QMUIBarProtocol.h"; sourceTree = "<group>"; };
+		576963704B65C5D0D742512AD9291635 /* URLNavigator-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "URLNavigator-Info.plist"; sourceTree = "<group>"; };
 		577505BA6885ECACC388B679F00D8C75 /* AutoBind.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AutoBind.swift; path = Sources/AutoBind.swift; sourceTree = "<group>"; };
 		578A415D64A97794DD922A7833950B93 /* SwiftyImage-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "SwiftyImage-umbrella.h"; sourceTree = "<group>"; };
 		5791E855F35648175862DDF2E28A87CF /* IdentifiableValue.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = IdentifiableValue.swift; path = Sources/Differentiator/IdentifiableValue.swift; sourceTree = "<group>"; };
@@ -2677,6 +2678,7 @@
 		622BA47B2DC3F3615A0FA93F6F8FB9F7 /* RLMUtil.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMUtil.mm; path = Realm/RLMUtil.mm; sourceTree = "<group>"; };
 		6245885F60E99407E5AB6ACB5F9A351B /* ToArray.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ToArray.swift; path = RxSwift/Observables/ToArray.swift; sourceTree = "<group>"; };
 		62479F298853E66852989E482FAB49F2 /* RLMProperty.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMProperty.h; path = include/RLMProperty.h; sourceTree = "<group>"; };
+		6256D969FDE419DCC9EF9A45BBC48D5D /* URLPatchComponentMatchResult.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = URLPatchComponentMatchResult.swift; path = Sources/URLMatcher/URLPatchComponentMatchResult.swift; sourceTree = "<group>"; };
 		628216763B03AF1251603D00214BE1BD /* SectionReactor.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = SectionReactor.release.xcconfig; sourceTree = "<group>"; };
 		62982E4923F8E3B505E76D8936B6BAF9 /* RecursiveLock.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RecursiveLock.swift; path = Platform/RecursiveLock.swift; sourceTree = "<group>"; };
 		62B37B9BC9A94D8996F001DA03174A7C /* NSButton+Rx.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "NSButton+Rx.swift"; path = "RxCocoa/macOS/NSButton+Rx.swift"; sourceTree = "<group>"; };
@@ -2828,6 +2830,7 @@
 		7C00D6CF6E33C509B6BC44425C93C9A3 /* QMUISearchBar.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = QMUISearchBar.h; path = QMUIKit/QMUIComponents/QMUISearchBar.h; sourceTree = "<group>"; };
 		7C1FBAFF8810622A18E059D36C7E156C /* Then-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Then-Info.plist"; sourceTree = "<group>"; };
 		7C2B6E1767C2CDCBDAF3A9106A3C167B /* SectionReactor.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SectionReactor.swift; path = Sources/SectionReactor/SectionReactor.swift; sourceTree = "<group>"; };
+		7C464F8951D64A073119BD14D3504D27 /* NavigatorType.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NavigatorType.swift; path = Sources/URLNavigator/NavigatorType.swift; sourceTree = "<group>"; };
 		7C4ADED80C61A5651ADBA8D7B27517C9 /* Do.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Do.swift; path = RxSwift/Observables/Do.swift; sourceTree = "<group>"; };
 		7C6F90C9F4EEA41C0B8328D66A0F5D6B /* UIImage+QMUITheme.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIImage+QMUITheme.m"; path = "QMUIKit/QMUIComponents/QMUITheme/UIImage+QMUITheme.m"; sourceTree = "<group>"; };
 		7C77602CB04A5E4F14456B42760674A7 /* RLMScheduler.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMScheduler.h; path = include/RLMScheduler.h; sourceTree = "<group>"; };
@@ -2843,7 +2846,6 @@
 		7E82B375F620E71EA112AB528E5EDA8D /* RxOptional */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = RxOptional; path = RxOptional.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		7E82F7F9BC47BCD107DED34C6F140C32 /* NSPointerArray+QMUI.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSPointerArray+QMUI.h"; path = "QMUIKit/UIKitExtensions/NSPointerArray+QMUI.h"; sourceTree = "<group>"; };
 		7E8D64D1DBEB5905CA26999374A4FA28 /* QMUILogItem.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = QMUILogItem.m; path = QMUIKit/QMUIComponents/QMUILog/QMUILogItem.m; sourceTree = "<group>"; };
-		7EBD9C7038057DB29B689DBE4E226236 /* URLNavigator.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = URLNavigator.modulemap; sourceTree = "<group>"; };
 		7F084117668653460123BE78951A5520 /* CocoaLumberjack.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = CocoaLumberjack.modulemap; sourceTree = "<group>"; };
 		7F188F8760703F16991CD1FBACE4821F /* FormatIndicatedCacheSerializer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = FormatIndicatedCacheSerializer.swift; path = Sources/Cache/FormatIndicatedCacheSerializer.swift; sourceTree = "<group>"; };
 		7F8FDAE086CEF35B39CD98EE3A42B9D1 /* ESPullToRefresh.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = ESPullToRefresh.release.xcconfig; sourceTree = "<group>"; };
@@ -2872,6 +2874,7 @@
 		825EC94854EB1E8CF321EAE6506ED2C0 /* RxRelay-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "RxRelay-prefix.pch"; sourceTree = "<group>"; };
 		8272E8224BE13BC95DFF7609C8E0F4C8 /* SwiftyUserDefaults-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "SwiftyUserDefaults-Info.plist"; sourceTree = "<group>"; };
 		82AD40DC1E8999D7E875D52B54E8874B /* QMUIScrollAnimator.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = QMUIScrollAnimator.h; path = QMUIKit/QMUIComponents/QMUIScrollAnimator/QMUIScrollAnimator.h; sourceTree = "<group>"; };
+		82B54C60BCFF87A51ABC7FA1DB854436 /* URLNavigator.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = URLNavigator.debug.xcconfig; sourceTree = "<group>"; };
 		82F29B76D9461C001B5CE1EC81128BFC /* RetryStrategy.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RetryStrategy.swift; path = Sources/Networking/RetryStrategy.swift; sourceTree = "<group>"; };
 		830540241CAA07785E2B87E927B48E60 /* GroupBy.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = GroupBy.swift; path = RxSwift/Observables/GroupBy.swift; sourceTree = "<group>"; };
 		830969D27CED43D72A4DD57645F15335 /* YYTextAsyncLayer.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = YYTextAsyncLayer.m; path = YYText/Utility/YYTextAsyncLayer.m; sourceTree = "<group>"; };
@@ -2951,7 +2954,6 @@
 		8D0A38D21D87E9D4E593E86FBEAC67E5 /* RLMArray.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMArray.mm; path = Realm/RLMArray.mm; sourceTree = "<group>"; };
 		8D2CA99232523BCAF896D1193E678282 /* YYTextUtilities.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = YYTextUtilities.m; path = YYText/Utility/YYTextUtilities.m; sourceTree = "<group>"; };
 		8E0095F6A76CD3057EA2F9E548C047E1 /* DDLog.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DDLog.h; path = Sources/CocoaLumberjack/include/CocoaLumberjack/DDLog.h; sourceTree = "<group>"; };
-		8E0438C2E16434078B44A6C0C9ED5CC7 /* URLNavigator-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "URLNavigator-prefix.pch"; sourceTree = "<group>"; };
 		8E11385ACFC4BE30F883FEC1E78869CD /* WeakMapTable-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "WeakMapTable-umbrella.h"; sourceTree = "<group>"; };
 		8E46F09207CA56D245CF496EBD6FB555 /* UITableViewCell+QMUI.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UITableViewCell+QMUI.m"; path = "QMUIKit/UIKitExtensions/UITableViewCell+QMUI.m"; sourceTree = "<group>"; };
 		8E57E865F6646269DB5004E095F98FDC /* Take.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Take.swift; path = RxSwift/Observables/Take.swift; sourceTree = "<group>"; };
@@ -2984,6 +2986,7 @@
 		9202FC6B3B8DC88E50981C4A8D2B6A87 /* QMUIAssetsManager.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = QMUIAssetsManager.h; path = QMUIKit/QMUIComponents/AssetLibrary/QMUIAssetsManager.h; sourceTree = "<group>"; };
 		92061998C43B4655B51B07BCF664077C /* Runtime.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Runtime.swift; path = Sources/Utility/Runtime.swift; sourceTree = "<group>"; };
 		921BE4A82C4A7A5C72A0C6F8B8FEF200 /* Realm */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = Realm; path = Realm.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+		923148F68079972EE539058BA881D9FC /* URLMatcher.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = URLMatcher.swift; path = Sources/URLMatcher/URLMatcher.swift; sourceTree = "<group>"; };
 		923626C6DEF7C6F4727FA4D3162B3611 /* NSArray+QMUI.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSArray+QMUI.m"; path = "QMUIKit/UIKitExtensions/NSArray+QMUI.m"; sourceTree = "<group>"; };
 		9255DFF80BE97E9A50A608C57F386219 /* Buffer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Buffer.swift; path = RxSwift/Observables/Buffer.swift; sourceTree = "<group>"; };
 		92BAD1D2E7E29EE2BC4F8BA75FE945EC /* UINavigationController+NavigationBarTransition.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UINavigationController+NavigationBarTransition.m"; path = "QMUIKit/QMUIComponents/NavigationBarTransition/UINavigationController+NavigationBarTransition.m"; sourceTree = "<group>"; };
@@ -3012,7 +3015,6 @@
 		96272AA83B372AF63B57910C69B77CF6 /* UITableView+QMUIStaticCell.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UITableView+QMUIStaticCell.m"; path = "QMUIKit/QMUIComponents/StaticTableView/UITableView+QMUIStaticCell.m"; sourceTree = "<group>"; };
 		963691262DADB12F126C128339664105 /* InvocableType.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = InvocableType.swift; path = RxSwift/Schedulers/Internal/InvocableType.swift; sourceTree = "<group>"; };
 		96728F7F701A6FBF45D25C41FFD606D7 /* RxCollectionViewDataSourceProxy.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RxCollectionViewDataSourceProxy.swift; path = RxCocoa/iOS/Proxies/RxCollectionViewDataSourceProxy.swift; sourceTree = "<group>"; };
-		968AA24EEF69EFF1DFE38934E0C5F263 /* URLNavigator-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "URLNavigator-dummy.m"; sourceTree = "<group>"; };
 		96BD68D0E5FF474BEE7A3C89913F5B41 /* UITextField+QMUI.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UITextField+QMUI.h"; path = "QMUIKit/UIKitExtensions/UITextField+QMUI.h"; sourceTree = "<group>"; };
 		96CB030C2C42A93947663A5C4D26C8CB /* SkipWhile.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SkipWhile.swift; path = RxSwift/Observables/SkipWhile.swift; sourceTree = "<group>"; };
 		96CE9F2867A6002AF2D19F7A6B51E438 /* SwiftDate-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "SwiftDate-umbrella.h"; sourceTree = "<group>"; };
@@ -3064,7 +3066,6 @@
 		A015947195F72E774E531852D2AEC78E /* ReactorKit-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "ReactorKit-prefix.pch"; sourceTree = "<group>"; };
 		A02AC62B0153F712FBE2A860BF123EB0 /* SwiftyImage.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = SwiftyImage.release.xcconfig; sourceTree = "<group>"; };
 		A045640202E341A3771AA32771BE7EC6 /* EmptyKit.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = EmptyKit.modulemap; sourceTree = "<group>"; };
-		A04A9AB9AA9EA59F9FA021E69CA5AEA7 /* UIViewController+TopMostViewController.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "UIViewController+TopMostViewController.swift"; path = "Sources/URLNavigator/UIViewController+TopMostViewController.swift"; sourceTree = "<group>"; };
 		A0BC9338AAF6A797F2DD054BE69FFA07 /* RxCocoa.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = RxCocoa.modulemap; sourceTree = "<group>"; };
 		A11DA80F4F96BBA78FF9E7DD512AFFA2 /* RLMBSON.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMBSON.mm; path = Realm/RLMBSON.mm; sourceTree = "<group>"; };
 		A13701B3020C91F80D8B556566A7D353 /* DDFileLogger+Internal.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "DDFileLogger+Internal.h"; path = "Sources/CocoaLumberjack/DDFileLogger+Internal.h"; sourceTree = "<group>"; };
@@ -3077,7 +3078,6 @@
 		A239105C1F8AF4700A092C6CE9DFA285 /* CompositeDisposable.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = CompositeDisposable.swift; path = RxSwift/Disposables/CompositeDisposable.swift; sourceTree = "<group>"; };
 		A25402DCE6FA2120547F516F90C48509 /* UICollectionView+ReusableKit.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "UICollectionView+ReusableKit.swift"; path = "Sources/ReusableKit/UICollectionView+ReusableKit.swift"; sourceTree = "<group>"; };
 		A28FFA5AD40AF2BE31120F5E1935C751 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS14.0.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; };
-		A2E3E9AF476D5584320485DC67E60125 /* NavigatorDelegate.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NavigatorDelegate.swift; path = Sources/URLNavigator/NavigatorDelegate.swift; sourceTree = "<group>"; };
 		A376D319BAE491D6C6825626CB6276DE /* UIImage+QMUI.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIImage+QMUI.h"; path = "QMUIKit/UIKitExtensions/UIImage+QMUI.h"; sourceTree = "<group>"; };
 		A379BA438EAB3CC674A1C2D62AA2C062 /* NSDecimalNumberTransform.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NSDecimalNumberTransform.swift; path = Sources/NSDecimalNumberTransform.swift; sourceTree = "<group>"; };
 		A38C52688DD437CAD3955398B11700E4 /* RLMSyncManager.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMSyncManager.mm; path = Realm/RLMSyncManager.mm; sourceTree = "<group>"; };
@@ -3152,7 +3152,6 @@
 		ACDAFA2D9A31E6E116DB7BEF37E54353 /* ObservableConvertibleType+Driver.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "ObservableConvertibleType+Driver.swift"; path = "RxCocoa/Traits/Driver/ObservableConvertibleType+Driver.swift"; sourceTree = "<group>"; };
 		ACDFD746E6C908AC11D73DA6A778496F /* UIControl+Rx.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "UIControl+Rx.swift"; path = "RxCocoa/iOS/UIControl+Rx.swift"; sourceTree = "<group>"; };
 		ACF9518BA76DE0C655BEC5BE2E81CCDB /* ConstraintInsets.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ConstraintInsets.swift; path = Sources/ConstraintInsets.swift; sourceTree = "<group>"; };
-		AD2C3C103EB330C7BF4ED890EE6E4738 /* URLPathComponent.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = URLPathComponent.swift; path = Sources/URLMatcher/URLPathComponent.swift; sourceTree = "<group>"; };
 		AD356C6D6CD62AA860E276D8D1875246 /* Occupiable.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Occupiable.swift; path = Sources/RxOptional/Occupiable.swift; sourceTree = "<group>"; };
 		AD55F2FB4060702035DD04FF31C64C4E /* QMUIKit-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "QMUIKit-umbrella.h"; sourceTree = "<group>"; };
 		AE224248747A952A6F0756E1FA25573B /* RLMMongoClient.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMMongoClient.h; path = include/RLMMongoClient.h; sourceTree = "<group>"; };
@@ -3243,7 +3242,6 @@
 		BB1450CDB42EA96EA8D53397DE195636 /* VTEnumType.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = VTEnumType.h; path = VTMagic/VTEnumType.h; sourceTree = "<group>"; };
 		BB38D70236D4A1A58B8FF33B4A526978 /* QMUIOrderedDictionary.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = QMUIOrderedDictionary.m; path = QMUIKit/QMUIComponents/QMUIOrderedDictionary.m; sourceTree = "<group>"; };
 		BB4298732B39EFF259E920E31CF7539C /* CachedResponseHandler.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = CachedResponseHandler.swift; path = Source/CachedResponseHandler.swift; sourceTree = "<group>"; };
-		BB92B6D2744FD7C885892836E38D64CF /* UIViewControllerType.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = UIViewControllerType.swift; path = Sources/URLNavigator/UIViewControllerType.swift; sourceTree = "<group>"; };
 		BBA10DA6318ED61AB023E9686242475B /* QMUILogger+QMUIConfigurationTemplate.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "QMUILogger+QMUIConfigurationTemplate.m"; path = "QMUIKit/QMUIComponents/QMUILogger+QMUIConfigurationTemplate.m"; sourceTree = "<group>"; };
 		BBA9F663C882EEFD84361B45184F8AE7 /* RLMAsyncTask.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMAsyncTask.mm; path = Realm/RLMAsyncTask.mm; sourceTree = "<group>"; };
 		BBD752E862DB18F327549AB2C16349E7 /* PriorityQueue.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PriorityQueue.swift; path = Platform/DataStructures/PriorityQueue.swift; sourceTree = "<group>"; };
@@ -3261,11 +3259,11 @@
 		BD3C6D5EE3EC66921C2D71C9038B805A /* QMUIButton.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = QMUIButton.h; path = QMUIKit/QMUIComponents/QMUIButton/QMUIButton.h; sourceTree = "<group>"; };
 		BD3D2693A462BE7547AD14CC0A1141A3 /* QMUINavigationController.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = QMUINavigationController.m; path = QMUIKit/QMUIMainFrame/QMUINavigationController.m; sourceTree = "<group>"; };
 		BD575B32CF3AE9639C44A7BF0EB51B9C /* TransformOperators.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TransformOperators.swift; path = Sources/TransformOperators.swift; sourceTree = "<group>"; };
+		BD79B0F4034471F19AFE25543213F83A /* URLPathComponent.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = URLPathComponent.swift; path = Sources/URLMatcher/URLPathComponent.swift; sourceTree = "<group>"; };
 		BD810337F4A305D60D9250A197AA1EF1 /* Toaster */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = Toaster; path = Toaster.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		BD8AE87A3BC202DA10551B0901EF1FB7 /* QMUIThemePrivate.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = QMUIThemePrivate.h; path = QMUIKit/QMUIComponents/QMUITheme/QMUIThemePrivate.h; sourceTree = "<group>"; };
 		BDA5D68FB5682344B118CA03C960AED8 /* QMUITableViewCell.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = QMUITableViewCell.m; path = QMUIKit/QMUIComponents/QMUITableViewCell.m; sourceTree = "<group>"; };
 		BDC255EBC89BC8C0BDE8BD260587AB33 /* QMUICollectionViewPagingLayout.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = QMUICollectionViewPagingLayout.m; path = QMUIKit/QMUIComponents/QMUICollectionViewPagingLayout.m; sourceTree = "<group>"; };
-		BDE652123293AFAD18C4950CC16BA40F /* URLConvertible.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = URLConvertible.swift; path = Sources/URLMatcher/URLConvertible.swift; sourceTree = "<group>"; };
 		BE2E6D2625F510CF07F59C3357EEA431 /* BehaviorRelay+Driver.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "BehaviorRelay+Driver.swift"; path = "RxCocoa/Traits/Driver/BehaviorRelay+Driver.swift"; sourceTree = "<group>"; };
 		BE5AF83C8E519A9C1D6590DA57471001 /* CGFloatLiteral.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = CGFloatLiteral.swift; path = Sources/CGFloatLiteral/CGFloatLiteral.swift; sourceTree = "<group>"; };
 		BE61475AB73EFB3069C82BC17AF0F3E4 /* SwiftDate.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = SwiftDate.debug.xcconfig; sourceTree = "<group>"; };
@@ -3290,6 +3288,7 @@
 		BFD1EB141F8AF2405E660A673C642890 /* Box.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Box.swift; path = Sources/Utility/Box.swift; sourceTree = "<group>"; };
 		BFD446198B527F8FB3AE285FD6ECDB59 /* Cancellable.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Cancellable.swift; path = Sources/Moya/Cancellable.swift; sourceTree = "<group>"; };
 		BFEDD54F3FFE61992FFE2371882759F2 /* RxPickerViewDataSourceProxy.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RxPickerViewDataSourceProxy.swift; path = RxCocoa/iOS/Proxies/RxPickerViewDataSourceProxy.swift; sourceTree = "<group>"; };
+		C004C67302EDF04B1EFA3966E2E4B415 /* URLNavigator.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = URLNavigator.release.xcconfig; sourceTree = "<group>"; };
 		C03838346EEDAE6B1892E38440214885 /* QMUILog+QMUIConsole.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "QMUILog+QMUIConsole.m"; path = "QMUIKit/QMUIComponents/QMUIConsole/QMUILog+QMUIConsole.m"; sourceTree = "<group>"; };
 		C0A398FF6AD1144429391E6D7C7124BD /* CredentialsPlugin.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = CredentialsPlugin.swift; path = Sources/Moya/Plugins/CredentialsPlugin.swift; sourceTree = "<group>"; };
 		C0C5289A0DE06074238B9530407C95F2 /* Kingfisher-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Kingfisher-umbrella.h"; sourceTree = "<group>"; };
@@ -3351,7 +3350,6 @@
 		C87F2DDC211097314E7A52D883EE3E21 /* ReusableKit */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = ReusableKit; path = ReusableKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		C8BD7BBBCC57A5B17B1BDA5491B61C59 /* Date+Math.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "Date+Math.swift"; path = "Sources/SwiftDate/Date/Date+Math.swift"; sourceTree = "<group>"; };
 		C8F742BB49BC50A77E185FB0AA38E033 /* UIBarButtonItem+Rx.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "UIBarButtonItem+Rx.swift"; path = "RxCocoa/iOS/UIBarButtonItem+Rx.swift"; sourceTree = "<group>"; };
-		C8FA305CA426AF1A5E57E87FE34946AE /* URLPatchComponentMatchResult.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = URLPatchComponentMatchResult.swift; path = Sources/URLMatcher/URLPatchComponentMatchResult.swift; sourceTree = "<group>"; };
 		C94B5BB729F18ED4B87FFB5EAD67F511 /* SwiftyColor-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "SwiftyColor-Info.plist"; sourceTree = "<group>"; };
 		C9ACD877B8FEB845D71F1FDBF7B801AB /* RLMEvent.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMEvent.h; path = include/RLMEvent.h; sourceTree = "<group>"; };
 		C9AF7E492ACAA49748224D8374DC8786 /* Dollar.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = Dollar.debug.xcconfig; sourceTree = "<group>"; };
@@ -3415,7 +3413,6 @@
 		D24D177916591FB9A06DA9A2473A52FA /* QMUIPopupMenuView.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = QMUIPopupMenuView.h; path = QMUIKit/QMUIComponents/QMUIPopupMenuView/QMUIPopupMenuView.h; sourceTree = "<group>"; };
 		D266BD16FCCB13DCD2ACA307FB99C5B2 /* UIActivityIndicatorView+QMUI.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIActivityIndicatorView+QMUI.m"; path = "QMUIKit/UIKitExtensions/UIActivityIndicatorView+QMUI.m"; sourceTree = "<group>"; };
 		D27352505E032D1C8A73F22A51808F64 /* RLMSyncConfiguration.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMSyncConfiguration.mm; path = Realm/RLMSyncConfiguration.mm; sourceTree = "<group>"; };
-		D299F45ACDCCBEEAFD09338C80EF6C87 /* URLMatchResult.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = URLMatchResult.swift; path = Sources/URLMatcher/URLMatchResult.swift; sourceTree = "<group>"; };
 		D29E713F0B8489F3689979BE7562F13C /* RLMObjectBase_Dynamic.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMObjectBase_Dynamic.h; path = include/RLMObjectBase_Dynamic.h; sourceTree = "<group>"; };
 		D2D7474B84E7CF77AB33081BB428A73C /* Differentiator-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Differentiator-prefix.pch"; sourceTree = "<group>"; };
 		D32B17C3C4CA302938E60F4B91F456F4 /* SwiftyColor-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "SwiftyColor-prefix.pch"; sourceTree = "<group>"; };
@@ -3450,6 +3447,7 @@
 		D817366465AF4B1AE0CD6105CB1D04BC /* SharedSequence.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SharedSequence.swift; path = RxCocoa/Traits/SharedSequence/SharedSequence.swift; sourceTree = "<group>"; };
 		D81BD27A67F0D506B80BB35E827AB938 /* NSString+QMUI.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSString+QMUI.m"; path = "QMUIKit/UIKitExtensions/NSString+QMUI.m"; sourceTree = "<group>"; };
 		D84EE1F992C0DE1ACBE4E5C6D8142C87 /* KVORepresentable+CoreGraphics.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "KVORepresentable+CoreGraphics.swift"; path = "RxCocoa/Foundation/KVORepresentable+CoreGraphics.swift"; sourceTree = "<group>"; };
+		D866E978858454960A2D3A0760CDFB53 /* URLConvertible.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = URLConvertible.swift; path = Sources/URLMatcher/URLConvertible.swift; sourceTree = "<group>"; };
 		D8D2091EB2A57CF03417BF2B6474BBC8 /* Projection.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Projection.swift; path = RealmSwift/Projection.swift; sourceTree = "<group>"; };
 		D8ED3244D520E08BAA065196CFF6C404 /* RLMSet.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMSet.h; path = include/RLMSet.h; sourceTree = "<group>"; };
 		D92D179DDE73237802AB86C89A971168 /* NSShadow+QMUI.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSShadow+QMUI.h"; path = "QMUIKit/UIKitExtensions/NSShadow+QMUI.h"; sourceTree = "<group>"; };
@@ -3471,6 +3469,7 @@
 		DBC7236EDE1B35840DEA58BBEBD01883 /* Events.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Events.swift; path = RealmSwift/Events.swift; sourceTree = "<group>"; };
 		DC1D484B9D60F266AD3863C11220F929 /* RLMThreadSafeReference.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMThreadSafeReference.h; path = include/RLMThreadSafeReference.h; sourceTree = "<group>"; };
 		DC6BE6300846BF25885DF0C9BE35A477 /* Differentiator.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = Differentiator.debug.xcconfig; sourceTree = "<group>"; };
+		DC6F1D41B3BF242CBD49D28B7F79F895 /* URLNavigator-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "URLNavigator-dummy.m"; sourceTree = "<group>"; };
 		DC791EBE7672FAAFEBE96BDA322D3674 /* Kingfisher.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Kingfisher.swift; path = Sources/General/Kingfisher.swift; sourceTree = "<group>"; };
 		DC889B2F9CA9A33997E92E844C57C663 /* RLMSet_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMSet_Private.h; path = include/RLMSet_Private.h; sourceTree = "<group>"; };
 		DCCB5F93C023B71FEE6B6D1040864C15 /* UIInterface+QMUI.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIInterface+QMUI.h"; path = "QMUIKit/UIKitExtensions/UIInterface+QMUI.h"; sourceTree = "<group>"; };
@@ -3572,13 +3571,12 @@
 		EB936FC2941E014177E274B79A8B4578 /* QMUIPieProgressView.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = QMUIPieProgressView.m; path = QMUIKit/QMUIComponents/QMUIPieProgressView.m; sourceTree = "<group>"; };
 		EBC6D0C73BE3467BD6DBA8A4515573C9 /* NSString+QMUI.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSString+QMUI.h"; path = "QMUIKit/UIKitExtensions/NSString+QMUI.h"; sourceTree = "<group>"; };
 		EBF025A89B5AAE5163E50999A28EBC41 /* Deferred.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Deferred.swift; path = RxSwift/Observables/Deferred.swift; sourceTree = "<group>"; };
-		EC2635374E3A7D8BD350F08DBD347B19 /* NavigatorProtocol.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NavigatorProtocol.swift; path = Sources/URLNavigator/NavigatorProtocol.swift; sourceTree = "<group>"; };
+		EBF1C2D1B23B99DB8E2BA685E74DD086 /* URLNavigator.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = URLNavigator.modulemap; sourceTree = "<group>"; };
 		EC35B42949DDD3EEEF65FDFBC2C8F6DC /* RLMRealmUtil.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMRealmUtil.mm; path = Realm/RLMRealmUtil.mm; sourceTree = "<group>"; };
 		EC949075646199BBCCD8412C205088AB /* QMUIImagePickerViewController.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = QMUIImagePickerViewController.h; path = QMUIKit/QMUIComponents/ImagePickerLibrary/QMUIImagePickerViewController.h; sourceTree = "<group>"; };
 		ECCDB97490F0E7821B3AE378C9072917 /* UIView+QMUIBadge.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIView+QMUIBadge.h"; path = "QMUIKit/QMUIComponents/QMUIBadge/UIView+QMUIBadge.h"; sourceTree = "<group>"; };
 		ECE077F223DBE629DAA14D6678BE6A07 /* Response.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Response.swift; path = Sources/Moya/Response.swift; sourceTree = "<group>"; };
 		ED1015BA54E7E4C61EEF18E113A2D425 /* SwiftTimer */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = SwiftTimer; path = SwiftTimer.framework; sourceTree = BUILT_PRODUCTS_DIR; };
-		ED25C767AAC833179BC83731604DF10C /* URLNavigator.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = URLNavigator.release.xcconfig; sourceTree = "<group>"; };
 		ED518A5A233A43B27570602BD64E9EDA /* UINavigationBar+Transition.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UINavigationBar+Transition.h"; path = "QMUIKit/QMUIComponents/NavigationBarTransition/UINavigationBar+Transition.h"; sourceTree = "<group>"; };
 		ED5CCFC416565186F20A03958C0E5AF3 /* UIToolbar+QMUI.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIToolbar+QMUI.m"; path = "QMUIKit/UIKitExtensions/UIToolbar+QMUI.m"; sourceTree = "<group>"; };
 		ED6929154D9030596EF0B9EB61730463 /* EmptyView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = EmptyView.swift; path = EmptyKit/EmptyView.swift; sourceTree = "<group>"; };
@@ -3597,6 +3595,7 @@
 		EF51B0226BBEDE1E7A7C7C577C1E7DB4 /* Date+Compare.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "Date+Compare.swift"; path = "Sources/SwiftDate/Date/Date+Compare.swift"; sourceTree = "<group>"; };
 		EF52FE37DCA19F19FF94B92E1517403F /* TailRecursiveSink.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TailRecursiveSink.swift; path = RxSwift/Observers/TailRecursiveSink.swift; sourceTree = "<group>"; };
 		EF6F0EDAA6C55CF207D0136DC33A4CD7 /* RxCollectionViewDelegateProxy.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RxCollectionViewDelegateProxy.swift; path = RxCocoa/iOS/Proxies/RxCollectionViewDelegateProxy.swift; sourceTree = "<group>"; };
+		EF71C51A566FC52D6EF912C2AC882271 /* URLMatchResult.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = URLMatchResult.swift; path = Sources/URLMatcher/URLMatchResult.swift; sourceTree = "<group>"; };
 		EF7AA5DB80CDF9D916FA8D247F1867F2 /* RxGesture-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "RxGesture-dummy.m"; sourceTree = "<group>"; };
 		EFAFA6086F59664FA91DB4620EE424EF /* Delay.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Delay.swift; path = RxSwift/Observables/Delay.swift; sourceTree = "<group>"; };
 		EFC0CC77BAFAA5B0B10181BCEF0A893D /* RLMMongoCollection.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMMongoCollection.mm; path = Realm/RLMMongoCollection.mm; sourceTree = "<group>"; };
@@ -3615,6 +3614,7 @@
 		F1557EA070A8D68CADCCA4F82DA8451D /* KFOptionsSetter.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = KFOptionsSetter.swift; path = Sources/General/KFOptionsSetter.swift; sourceTree = "<group>"; };
 		F1758DF7AAA1562542C982DB60BA3A08 /* GDPerformanceView-Swift.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "GDPerformanceView-Swift.release.xcconfig"; sourceTree = "<group>"; };
 		F1EFFACA27DDCF417E7856A3D1DAF0DF /* SessionDelegate.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SessionDelegate.swift; path = Sources/Networking/SessionDelegate.swift; sourceTree = "<group>"; };
+		F23CE929250FDE9C283C761F359F4EDA /* URLNavigator-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "URLNavigator-umbrella.h"; sourceTree = "<group>"; };
 		F241AA0B57F0DD7CE38A6EEC1CC2E946 /* Then.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = Then.modulemap; sourceTree = "<group>"; };
 		F2502965F6B49BEED56959147781FD25 /* DateInRegion+Components.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "DateInRegion+Components.swift"; path = "Sources/SwiftDate/DateInRegion/DateInRegion+Components.swift"; sourceTree = "<group>"; };
 		F2AF1C3C1D12F5168749AC5ED5776B63 /* UIStepper+Rx.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "UIStepper+Rx.swift"; path = "RxCocoa/iOS/UIStepper+Rx.swift"; sourceTree = "<group>"; };
@@ -3625,7 +3625,6 @@
 		F350D25AC6B546C1C1CC9DD2F07412A6 /* QMUIImagePreviewViewTransitionAnimator.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = QMUIImagePreviewViewTransitionAnimator.h; path = QMUIKit/QMUIComponents/QMUIImagePreviewView/QMUIImagePreviewViewTransitionAnimator.h; sourceTree = "<group>"; };
 		F361124FE5AED03AB7582D6CDEE0E361 /* YYTextRunDelegate.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = YYTextRunDelegate.h; path = YYText/String/YYTextRunDelegate.h; sourceTree = "<group>"; };
 		F3A626AA86C8144D3833E8DBE42B7222 /* DateTransform.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = DateTransform.swift; path = Sources/DateTransform.swift; sourceTree = "<group>"; };
-		F3F4A202ED73579A91143995DB5F9A2E /* Navigator.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Navigator.swift; path = Sources/URLNavigator/Navigator.swift; sourceTree = "<group>"; };
 		F4184FE3D658194F21399B9915B5DAB9 /* DDLoggerNames.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DDLoggerNames.m; path = Sources/CocoaLumberjack/DDLoggerNames.m; sourceTree = "<group>"; };
 		F4691F0526061B9C43EE3EBE6B8103D5 /* ReusableKit.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = ReusableKit.debug.xcconfig; sourceTree = "<group>"; };
 		F48C6F49F27F2D14D2E8D76F014C8388 /* SwiftyColor.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = SwiftyColor.debug.xcconfig; sourceTree = "<group>"; };
@@ -3666,6 +3665,7 @@
 		F9D7BA613CE440CF66FC2227D61083FB /* RLMObject_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMObject_Private.h; path = include/RLMObject_Private.h; sourceTree = "<group>"; };
 		F9F072561768C5D049152E2B22F9A448 /* UIScrollView+VTMagic.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIScrollView+VTMagic.h"; path = "VTMagic/UIScrollView+VTMagic.h"; sourceTree = "<group>"; };
 		FA3A74B610A644652AE8504BE6B35681 /* QMUIStringPrivate.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = QMUIStringPrivate.h; path = QMUIKit/UIKitExtensions/QMUIStringPrivate.h; sourceTree = "<group>"; };
+		FA54C532883855DDF17DE8CA61ADDC01 /* Navigator.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Navigator.swift; path = Sources/URLNavigator/Navigator.swift; sourceTree = "<group>"; };
 		FA59F75D90B8F599D7D0A060C6413874 /* RedirectHandler.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RedirectHandler.swift; path = Sources/Networking/RedirectHandler.swift; sourceTree = "<group>"; };
 		FA6C3E14222E08B86394FA0F30BA2D3B /* TimePeriodProtocol.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TimePeriodProtocol.swift; path = Sources/SwiftDate/TimePeriod/TimePeriodProtocol.swift; sourceTree = "<group>"; };
 		FA8A48901E7A81CC6E2052F3629C1578 /* UIProgressView+Rx.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "UIProgressView+Rx.swift"; path = "RxCocoa/iOS/UIProgressView+Rx.swift"; sourceTree = "<group>"; };
@@ -4957,25 +4957,6 @@
 			name = Headers;
 			sourceTree = "<group>";
 		};
-		52C3CCCBC6C6AE9CD12BA347E038551C /* URLNavigator */ = {
-			isa = PBXGroup;
-			children = (
-				F3F4A202ED73579A91143995DB5F9A2E /* Navigator.swift */,
-				A2E3E9AF476D5584320485DC67E60125 /* NavigatorDelegate.swift */,
-				EC2635374E3A7D8BD350F08DBD347B19 /* NavigatorProtocol.swift */,
-				A04A9AB9AA9EA59F9FA021E69CA5AEA7 /* UIViewController+TopMostViewController.swift */,
-				BB92B6D2744FD7C885892836E38D64CF /* UIViewControllerType.swift */,
-				BDE652123293AFAD18C4950CC16BA40F /* URLConvertible.swift */,
-				015A11982F54456EE8C2A792F265A5DA /* URLMatcher.swift */,
-				D299F45ACDCCBEEAFD09338C80EF6C87 /* URLMatchResult.swift */,
-				C8FA305CA426AF1A5E57E87FE34946AE /* URLPatchComponentMatchResult.swift */,
-				AD2C3C103EB330C7BF4ED890EE6E4738 /* URLPathComponent.swift */,
-				BAB4AD9F16B4D4490D5FE5E383BB0983 /* Support Files */,
-			);
-			name = URLNavigator;
-			path = URLNavigator;
-			sourceTree = "<group>";
-		};
 		54C11D890FDC891C2DA3D236DF94CDB7 /* Support Files */ = {
 			isa = PBXGroup;
 			children = (
@@ -5007,6 +4988,21 @@
 			path = "../Target Support Files/ESPullToRefresh";
 			sourceTree = "<group>";
 		};
+		5642E9DADF338B40EC7547CE063D05BB /* Support Files */ = {
+			isa = PBXGroup;
+			children = (
+				EBF1C2D1B23B99DB8E2BA685E74DD086 /* URLNavigator.modulemap */,
+				DC6F1D41B3BF242CBD49D28B7F79F895 /* URLNavigator-dummy.m */,
+				576963704B65C5D0D742512AD9291635 /* URLNavigator-Info.plist */,
+				5381019763296D1153BA2EFFF956B239 /* URLNavigator-prefix.pch */,
+				F23CE929250FDE9C283C761F359F4EDA /* URLNavigator-umbrella.h */,
+				82B54C60BCFF87A51ABC7FA1DB854436 /* URLNavigator.debug.xcconfig */,
+				C004C67302EDF04B1EFA3966E2E4B415 /* URLNavigator.release.xcconfig */,
+			);
+			name = "Support Files";
+			path = "../Target Support Files/URLNavigator";
+			sourceTree = "<group>";
+		};
 		572030140ED63714D3E852D42D9BB218 /* Support Files */ = {
 			isa = PBXGroup;
 			children = (
@@ -5878,6 +5874,25 @@
 			path = "../Target Support Files/RealmSwift";
 			sourceTree = "<group>";
 		};
+		9CEFB1D010230D4ACA0C98629A83E14B /* URLNavigator */ = {
+			isa = PBXGroup;
+			children = (
+				FA54C532883855DDF17DE8CA61ADDC01 /* Navigator.swift */,
+				41DA50CAD1386AC15411BDB20C75B849 /* NavigatorDelegate.swift */,
+				7C464F8951D64A073119BD14D3504D27 /* NavigatorType.swift */,
+				3BBF41284CADBE7BD04147D8537C0D4A /* UIViewController+TopMostViewController.swift */,
+				485A5C1D60D6769796318F1C4996B8F1 /* UIViewControllerType.swift */,
+				D866E978858454960A2D3A0760CDFB53 /* URLConvertible.swift */,
+				923148F68079972EE539058BA881D9FC /* URLMatcher.swift */,
+				EF71C51A566FC52D6EF912C2AC882271 /* URLMatchResult.swift */,
+				6256D969FDE419DCC9EF9A45BBC48D5D /* URLPatchComponentMatchResult.swift */,
+				BD79B0F4034471F19AFE25543213F83A /* URLPathComponent.swift */,
+				5642E9DADF338B40EC7547CE063D05BB /* Support Files */,
+			);
+			name = URLNavigator;
+			path = URLNavigator;
+			sourceTree = "<group>";
+		};
 		9E94DCA84DFBF09DEAF2AF39895203F4 /* Support Files */ = {
 			isa = PBXGroup;
 			children = (
@@ -6050,21 +6065,6 @@
 			name = QMUIZoomImageView;
 			sourceTree = "<group>";
 		};
-		BAB4AD9F16B4D4490D5FE5E383BB0983 /* Support Files */ = {
-			isa = PBXGroup;
-			children = (
-				7EBD9C7038057DB29B689DBE4E226236 /* URLNavigator.modulemap */,
-				968AA24EEF69EFF1DFE38934E0C5F263 /* URLNavigator-dummy.m */,
-				3C36366803EDE007B754CEB6B07D3DBE /* URLNavigator-Info.plist */,
-				8E0438C2E16434078B44A6C0C9ED5CC7 /* URLNavigator-prefix.pch */,
-				3AD25D0D847D7EA8C7A6304E590FB750 /* URLNavigator-umbrella.h */,
-				22834AD078AD0E5AC27717A0389DBE78 /* URLNavigator.debug.xcconfig */,
-				ED25C767AAC833179BC83731604DF10C /* URLNavigator.release.xcconfig */,
-			);
-			name = "Support Files";
-			path = "../Target Support Files/URLNavigator";
-			sourceTree = "<group>";
-		};
 		BB10CF3349C3EC24835713E44B407620 /* ReachabilitySwift */ = {
 			isa = PBXGroup;
 			children = (
@@ -6344,7 +6344,7 @@
 				90A2F5623304C454153C6A97451541E2 /* SwiftyUserDefaults */,
 				392DD749E0115BAA450159C5E520EEB2 /* Then */,
 				0B2DEB151C47C24F2C16EF55F3E0D9AD /* Toaster */,
-				52C3CCCBC6C6AE9CD12BA347E038551C /* URLNavigator */,
+				9CEFB1D010230D4ACA0C98629A83E14B /* URLNavigator */,
 				839E70BF45E23C172A6F5090F6775F52 /* VTMagic */,
 				818ABBF437C7FC32AD9F1E87C2534B73 /* WeakMapTable */,
 				EA7302C024AD5FF0AC42DF1DA902D727 /* YYText */,
@@ -7873,7 +7873,7 @@
 			buildConfigurationList = DCE25DD4B2F6954A32E82B7D095BC63E /* Build configuration list for PBXNativeTarget "URLNavigator" */;
 			buildPhases = (
 				0BACC4EE9C059583D396E93DE92C4EE5 /* Headers */,
-				6ED18D0C0431D4B345E36D9001C1CB77 /* Sources */,
+				8F87FDC6274150E6E72257D6F44024FE /* Sources */,
 				93C94B92EAF4BFF6F3B843D4F7713310 /* Frameworks */,
 				E7140525B50CAFC6EEC46A6432F58F0E /* Resources */,
 			);
@@ -9553,24 +9553,6 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
-		6ED18D0C0431D4B345E36D9001C1CB77 /* Sources */ = {
-			isa = PBXSourcesBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-				2827ECA7A7232E3278EB22CF36E9C9B1 /* Navigator.swift in Sources */,
-				99BF176C1A7487E6EED2BDDE37FF9B71 /* NavigatorDelegate.swift in Sources */,
-				B71E0501C4EAA254B21BAD23474F15D6 /* NavigatorProtocol.swift in Sources */,
-				3014E01D888D6B31280E0FEE7CA29A85 /* UIViewController+TopMostViewController.swift in Sources */,
-				C1AADA499E97A0DFE5F52D0227C75145 /* UIViewControllerType.swift in Sources */,
-				BF19F8647E65E59F741A1F65051076D1 /* URLConvertible.swift in Sources */,
-				F97BA357BCC4CB6B9E2E70F4A8CA8435 /* URLMatcher.swift in Sources */,
-				6A6E33CF6687DB2A90875127C21A46A6 /* URLMatchResult.swift in Sources */,
-				81D122D57B02C7A604B3C7121DA7C824 /* URLNavigator-dummy.m in Sources */,
-				327167585B084C3F360FF7498CB8DADA /* URLPatchComponentMatchResult.swift in Sources */,
-				A5576CF93240C7B914A6213C81C40B59 /* URLPathComponent.swift in Sources */,
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-		};
 		7B5BEB32F210EFE38F7D58FF8A1CC2DE /* Sources */ = {
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
@@ -9695,6 +9677,24 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
+		8F87FDC6274150E6E72257D6F44024FE /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				4FAE9CC98EEA936D705D84DE9B9F57B1 /* Navigator.swift in Sources */,
+				1B5F478892CFF682720BF3336F1A7575 /* NavigatorDelegate.swift in Sources */,
+				DF605A22A5D371C66ED79532CDEBA647 /* NavigatorType.swift in Sources */,
+				316AA4C2C204FE29F00D8996C2171ECD /* UIViewController+TopMostViewController.swift in Sources */,
+				F10D12BBC36177350D3C6685C005FDC4 /* UIViewControllerType.swift in Sources */,
+				C18E4CAC68EF64A35DB0E7DFB9D630B6 /* URLConvertible.swift in Sources */,
+				7C93AF1CF51A2E8777669AE3371058FE /* URLMatcher.swift in Sources */,
+				E146CB3E6720ED2C5A7A961BF8A1A984 /* URLMatchResult.swift in Sources */,
+				F4A46693AC3A15C53B6FCA6C5F8F7521 /* URLNavigator-dummy.m in Sources */,
+				C54967210D76333CA09233FE499819C5 /* URLPatchComponentMatchResult.swift in Sources */,
+				24AD273272A49A0C36649ADA5F54A492 /* URLPathComponent.swift in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 		96CD62B8B9B81FD71243F8614AF0B04E /* Sources */ = {
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
@@ -11808,41 +11808,6 @@
 			};
 			name = Debug;
 		};
-		513B9E7DD99B57C02B6F830AE948C5A8 /* Debug */ = {
-			isa = XCBuildConfiguration;
-			baseConfigurationReference = 22834AD078AD0E5AC27717A0389DBE78 /* URLNavigator.debug.xcconfig */;
-			buildSettings = {
-				"CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
-				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
-				"CODE_SIGN_IDENTITY[sdk=watchos*]" = "";
-				CURRENT_PROJECT_VERSION = 1;
-				DEFINES_MODULE = YES;
-				DYLIB_COMPATIBILITY_VERSION = 1;
-				DYLIB_CURRENT_VERSION = 1;
-				DYLIB_INSTALL_NAME_BASE = "@rpath";
-				ENABLE_BITCODE = NO;
-				GCC_PREFIX_HEADER = "Target Support Files/URLNavigator/URLNavigator-prefix.pch";
-				INFOPLIST_FILE = "Target Support Files/URLNavigator/URLNavigator-Info.plist";
-				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
-				IPHONEOS_DEPLOYMENT_TARGET = 12.0;
-				LD_RUNPATH_SEARCH_PATHS = (
-					"$(inherited)",
-					"@executable_path/Frameworks",
-					"@loader_path/Frameworks",
-				);
-				MODULEMAP_FILE = "Target Support Files/URLNavigator/URLNavigator.modulemap";
-				PRODUCT_MODULE_NAME = URLNavigator;
-				PRODUCT_NAME = URLNavigator;
-				SDKROOT = iphoneos;
-				SKIP_INSTALL = YES;
-				SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) ";
-				SWIFT_VERSION = 5.0;
-				TARGETED_DEVICE_FAMILY = "1,2";
-				VERSIONING_SYSTEM = "apple-generic";
-				VERSION_INFO_PREFIX = "";
-			};
-			name = Debug;
-		};
 		52235FB3181E517F1F2D37DE71959EE4 /* Debug */ = {
 			isa = XCBuildConfiguration;
 			baseConfigurationReference = 4168BA1D0C93687E6BF38DD75416D223 /* Then.debug.xcconfig */;
@@ -12332,6 +12297,42 @@
 			};
 			name = Debug;
 		};
+		80E4E83647D96143AFBAD59581375034 /* Release */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = C004C67302EDF04B1EFA3966E2E4B415 /* URLNavigator.release.xcconfig */;
+			buildSettings = {
+				"CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
+				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+				"CODE_SIGN_IDENTITY[sdk=watchos*]" = "";
+				CURRENT_PROJECT_VERSION = 1;
+				DEFINES_MODULE = YES;
+				DYLIB_COMPATIBILITY_VERSION = 1;
+				DYLIB_CURRENT_VERSION = 1;
+				DYLIB_INSTALL_NAME_BASE = "@rpath";
+				ENABLE_BITCODE = NO;
+				GCC_PREFIX_HEADER = "Target Support Files/URLNavigator/URLNavigator-prefix.pch";
+				INFOPLIST_FILE = "Target Support Files/URLNavigator/URLNavigator-Info.plist";
+				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+				IPHONEOS_DEPLOYMENT_TARGET = 12.0;
+				LD_RUNPATH_SEARCH_PATHS = (
+					"$(inherited)",
+					"@executable_path/Frameworks",
+					"@loader_path/Frameworks",
+				);
+				MODULEMAP_FILE = "Target Support Files/URLNavigator/URLNavigator.modulemap";
+				PRODUCT_MODULE_NAME = URLNavigator;
+				PRODUCT_NAME = URLNavigator;
+				SDKROOT = iphoneos;
+				SKIP_INSTALL = YES;
+				SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) ";
+				SWIFT_VERSION = 5.0;
+				TARGETED_DEVICE_FAMILY = "1,2";
+				VALIDATE_PRODUCT = YES;
+				VERSIONING_SYSTEM = "apple-generic";
+				VERSION_INFO_PREFIX = "";
+			};
+			name = Release;
+		};
 		8629CD9EBD7FA8E65D21992E8C163D60 /* Debug */ = {
 			isa = XCBuildConfiguration;
 			baseConfigurationReference = BE9D2DFE633898860966F2765BA81CD6 /* RxViewController.debug.xcconfig */;
@@ -12799,42 +12800,6 @@
 			};
 			name = Release;
 		};
-		AEA7273E8B743B563B4EB407B402ACB0 /* Release */ = {
-			isa = XCBuildConfiguration;
-			baseConfigurationReference = ED25C767AAC833179BC83731604DF10C /* URLNavigator.release.xcconfig */;
-			buildSettings = {
-				"CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
-				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
-				"CODE_SIGN_IDENTITY[sdk=watchos*]" = "";
-				CURRENT_PROJECT_VERSION = 1;
-				DEFINES_MODULE = YES;
-				DYLIB_COMPATIBILITY_VERSION = 1;
-				DYLIB_CURRENT_VERSION = 1;
-				DYLIB_INSTALL_NAME_BASE = "@rpath";
-				ENABLE_BITCODE = NO;
-				GCC_PREFIX_HEADER = "Target Support Files/URLNavigator/URLNavigator-prefix.pch";
-				INFOPLIST_FILE = "Target Support Files/URLNavigator/URLNavigator-Info.plist";
-				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
-				IPHONEOS_DEPLOYMENT_TARGET = 12.0;
-				LD_RUNPATH_SEARCH_PATHS = (
-					"$(inherited)",
-					"@executable_path/Frameworks",
-					"@loader_path/Frameworks",
-				);
-				MODULEMAP_FILE = "Target Support Files/URLNavigator/URLNavigator.modulemap";
-				PRODUCT_MODULE_NAME = URLNavigator;
-				PRODUCT_NAME = URLNavigator;
-				SDKROOT = iphoneos;
-				SKIP_INSTALL = YES;
-				SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) ";
-				SWIFT_VERSION = 5.0;
-				TARGETED_DEVICE_FAMILY = "1,2";
-				VALIDATE_PRODUCT = YES;
-				VERSIONING_SYSTEM = "apple-generic";
-				VERSION_INFO_PREFIX = "";
-			};
-			name = Release;
-		};
 		B190171B8735ED1929BF4A62AC9E147E /* Release */ = {
 			isa = XCBuildConfiguration;
 			baseConfigurationReference = 5ABBEB20F98A999FCF55DA32716882F0 /* ObjectMapper.release.xcconfig */;
@@ -13354,6 +13319,41 @@
 			};
 			name = Debug;
 		};
+		C86A07877DEC7450452DF3A304F73982 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = 82B54C60BCFF87A51ABC7FA1DB854436 /* URLNavigator.debug.xcconfig */;
+			buildSettings = {
+				"CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
+				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+				"CODE_SIGN_IDENTITY[sdk=watchos*]" = "";
+				CURRENT_PROJECT_VERSION = 1;
+				DEFINES_MODULE = YES;
+				DYLIB_COMPATIBILITY_VERSION = 1;
+				DYLIB_CURRENT_VERSION = 1;
+				DYLIB_INSTALL_NAME_BASE = "@rpath";
+				ENABLE_BITCODE = NO;
+				GCC_PREFIX_HEADER = "Target Support Files/URLNavigator/URLNavigator-prefix.pch";
+				INFOPLIST_FILE = "Target Support Files/URLNavigator/URLNavigator-Info.plist";
+				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+				IPHONEOS_DEPLOYMENT_TARGET = 12.0;
+				LD_RUNPATH_SEARCH_PATHS = (
+					"$(inherited)",
+					"@executable_path/Frameworks",
+					"@loader_path/Frameworks",
+				);
+				MODULEMAP_FILE = "Target Support Files/URLNavigator/URLNavigator.modulemap";
+				PRODUCT_MODULE_NAME = URLNavigator;
+				PRODUCT_NAME = URLNavigator;
+				SDKROOT = iphoneos;
+				SKIP_INSTALL = YES;
+				SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) ";
+				SWIFT_VERSION = 5.0;
+				TARGETED_DEVICE_FAMILY = "1,2";
+				VERSIONING_SYSTEM = "apple-generic";
+				VERSION_INFO_PREFIX = "";
+			};
+			name = Debug;
+		};
 		CC021847E9310C14988B21B8F26981EA /* Release */ = {
 			isa = XCBuildConfiguration;
 			baseConfigurationReference = E9AB8301B7DB652016D30B01999E4CF4 /* ReactorKit.release.xcconfig */;
@@ -14311,8 +14311,8 @@
 		DCE25DD4B2F6954A32E82B7D095BC63E /* Build configuration list for PBXNativeTarget "URLNavigator" */ = {
 			isa = XCConfigurationList;
 			buildConfigurations = (
-				513B9E7DD99B57C02B6F830AE948C5A8 /* Debug */,
-				AEA7273E8B743B563B4EB407B402ACB0 /* Release */,
+				C86A07877DEC7450452DF3A304F73982 /* Debug */,
+				80E4E83647D96143AFBAD59581375034 /* Release */,
 			);
 			defaultConfigurationIsVisible = 0;
 			defaultConfigurationName = Release;

+ 1 - 1
Pods/Target Support Files/URLNavigator/URLNavigator-Info.plist

@@ -15,7 +15,7 @@
   <key>CFBundlePackageType</key>
   <string>FMWK</string>
   <key>CFBundleShortVersionString</key>
-  <string>2.4.1</string>
+  <string>2.2.0</string>
   <key>CFBundleSignature</key>
   <string>????</string>
   <key>CFBundleVersion</key>

+ 5 - 5
Pods/URLNavigator/README.md

@@ -2,7 +2,7 @@
 
 ![Swift](https://img.shields.io/badge/Swift-5.0-orange.svg)
 [![CocoaPods](http://img.shields.io/cocoapods/v/URLNavigator.svg)](https://cocoapods.org/pods/URLNavigator)
-[![Build Status](https://github.com/devxoul/URLNavigator/workflows/CI/badge.svg)](https://github.com/devxoul/URLNavigator/actions)
+[![Build Status](https://travis-ci.org/devxoul/URLNavigator.svg?branch=master)](https://travis-ci.org/devxoul/URLNavigator)
 [![CodeCov](https://img.shields.io/codecov/c/github/devxoul/URLNavigator.svg)](https://codecov.io/gh/devxoul/URLNavigator)
 
 ⛵️ URLNavigator provides an elegant way to navigate through view controllers by URLs. URL patterns can be mapped by using `URLNavigator.register(_:_:)` function.
@@ -29,7 +29,7 @@ But it doesn't match with:
 
 #### 2. Mapping View Controllers and URL Open Handlers
 
-URLNavigator allows to map view controllers and URL open handlers with URL patterns. Here's an example of mapping URL patterns with view controllers and a closure. Each closures has three parameters: `url`, `values` and `context`.
+URLNavigator allows to map view controllers ans URL open handlers with URL patterns. Here's an example of mapping URL patterns with view controllers and a closure. Each closures has three parameters: `url`, `values` and `context`.
 
 * `url` is an URL that is passed from `push()` and `present()`.
 * `values` is a dictionary that contains URL placeholder keys and values.
@@ -110,8 +110,8 @@ You can find an example app [here](https://github.com/devxoul/URLNavigator/tree/
 2. Register to an IoC container:
 
     ```swift
-    container.register(NavigatorProtocol.self) { _ in Navigator() } // Swinject
-    let navigator = container.resolve(NavigatorProtocol.self)!
+    container.register(NavigatorType.self) { _ in Navigator() } // Swinject
+    let navigator = container.resolve(NavigatorType.self)!
     ```
 
 3. Inject dependency from a composition root.
@@ -123,7 +123,7 @@ I'd prefer using separated URL map file.
 
 ```swift
 struct URLNavigationMap {
-  static func initialize(navigator: NavigatorProtocol) {
+  static func initialize(navigator: NavigatorType) {
     navigator.register("myapp://user/<int:id>") { ... }
     navigator.register("myapp://post/<title>") { ... }
     navigator.handle("myapp://alert") { ... }

+ 5 - 21
Pods/URLNavigator/Sources/URLMatcher/URLMatcher.swift

@@ -51,18 +51,14 @@ open class URLMatcher {
     let scheme = url.urlValue?.scheme
     let stringPathComponents = self.stringPathComponents(from :url)
 
-    var results = [URLMatchResult]()
-    
     for candidate in candidates {
       guard scheme == candidate.urlValue?.scheme else { continue }
       if let result = self.match(stringPathComponents, with: candidate) {
-        results.append(result)
+        return result
       }
     }
 
-    return results.max {
-       self.numberOfPlainPathComponent(in: $0.pattern) < self.numberOfPlainPathComponent(in: $1.pattern)
-    }
+    return nil
   }
 
   func match(_ stringPathComponents: [String], with candidate: URLPattern) -> URLMatchResult? {
@@ -128,14 +124,9 @@ open class URLMatcher {
   }
 
   func stringPathComponents(from url: URLConvertible) -> [String] {
-    return url.urlStringValue.components(separatedBy: "/").lazy.enumerated()
-      .filter { index, component in !component.isEmpty }
-      .filter { index, component in !self.isScheme(index, component) }
-      .map { index, component in component }
-  }
-
-  private func isScheme(_ index: Int, _ component: String) -> Bool {
-    return index == 0 && component.hasSuffix(":")
+    return url.urlStringValue.components(separatedBy: "/").lazy
+      .filter { !$0.isEmpty }
+      .filter { !$0.hasSuffix(":") }
   }
 
   func pathComponents(from url: URLPattern) -> [URLPathComponent] {
@@ -166,11 +157,4 @@ open class URLMatcher {
       }
     }
   }
-
-  private func numberOfPlainPathComponent(in pattern: URLPattern) -> Int {
-    return self.pathComponents(from: pattern).lazy.filter {
-      guard case .plain = $0 else { return false }
-      return true
-    }.count
-  }
 }

+ 2 - 105
Pods/URLNavigator/Sources/URLNavigator/Navigator.swift

@@ -5,46 +5,25 @@ import UIKit
 import URLMatcher
 #endif
 
-public typealias URLPattern = String
-public typealias ViewControllerFactory = (_ url: URLConvertible, _ values: [String: Any], _ context: Any?) -> UIViewController?
-public typealias URLOpenHandlerFactory = (_ url: URLConvertible, _ values: [String: Any], _ context: Any?) -> Bool
-public typealias URLOpenHandler = () -> Bool
-
-open class Navigator: NavigatorProtocol {
-
-  // MARK: Properties
-
+open class Navigator: NavigatorType {
   public let matcher = URLMatcher()
   open weak var delegate: NavigatorDelegate?
 
   private var viewControllerFactories = [URLPattern: ViewControllerFactory]()
   private var handlerFactories = [URLPattern: URLOpenHandlerFactory]()
 
-
-  // MARK: Initializing
-
   public init() {
     // ⛵ I'm a Navigator!
   }
 
-
-  // MARK: Registering URLs
-
-  /// Registers a view controller factory to the URL pattern.
   open func register(_ pattern: URLPattern, _ factory: @escaping ViewControllerFactory) {
     self.viewControllerFactories[pattern] = factory
   }
 
-  /// Registers an URL open handler to the URL pattern.
   open func handle(_ pattern: URLPattern, _ factory: @escaping URLOpenHandlerFactory) {
     self.handlerFactories[pattern] = factory
   }
 
-  /// Returns a matching view controller from the specified URL.
-  ///
-  /// - parameter url: An URL to find view controllers.
-  ///
-  /// - returns: A match view controller or `nil` if not matched.
   open func viewController(for url: URLConvertible, context: Any? = nil) -> UIViewController? {
     let urlPatterns = Array(self.viewControllerFactories.keys)
     guard let match = self.matcher.match(url, from: urlPatterns) else { return nil }
@@ -52,93 +31,11 @@ open class Navigator: NavigatorProtocol {
     return factory(url, match.values, context)
   }
 
-  /// Returns a matching URL handler from the specified URL.
-  ///
-  /// - parameter url: An URL to find url handlers.
-  ///
-  /// - returns: A matching handler factory or `nil` if not matched.
-  open func handler(for url: URLConvertible, context: Any? = nil) -> URLOpenHandler? {
+  open func handler(for url: URLConvertible, context: Any?) -> URLOpenHandler? {
     let urlPatterns = Array(self.handlerFactories.keys)
     guard let match = self.matcher.match(url, from: urlPatterns) else { return nil }
     guard let handler = self.handlerFactories[match.pattern] else { return nil }
     return { handler(url, match.values, context) }
   }
-
-
-  // MARK: Push
-
-  /// Pushes a matching view controller to the navigation controller stack.
-  ///
-  /// - note: It is not a good idea to use this method directly because this method requires all
-  ///         parameters. This method eventually gets called when pushing a view controller with
-  ///         an URL, so it's recommended to implement this method only for mocking.
-  @discardableResult
-  open func push(_ url: URLConvertible, context: Any? = nil, from: UINavigationControllerType? = nil, animated: Bool = true) -> UIViewController? {
-    guard let viewController = self.viewController(for: url, context: context) else { return nil }
-    return self.push(viewController, from: from, animated: animated)
-  }
-
-  /// Pushes the view controller to the navigation controller stack.
-  ///
-  /// - note: It is not a good idea to use this method directly because this method requires all
-  ///         parameters. This method eventually gets called when pushing a view controller, so
-  ///         it's recommended to implement this method only for mocking.
-  @discardableResult
-  open func push(_ viewController: UIViewController, from: UINavigationControllerType? = nil, animated: Bool = true) -> UIViewController? {
-    guard (viewController is UINavigationController) == false else { return nil }
-    guard let navigationController = from ?? UIViewController.topMost?.navigationController else { return nil }
-    guard self.delegate?.shouldPush(viewController: viewController, from: navigationController) != false else { return nil }
-    navigationController.pushViewController(viewController, animated: animated)
-    return viewController
-  }
-
-
-  // MARK: Present
-
-  /// Presents a matching view controller.
-  ///
-  /// - note: It is not a good idea to use this method directly because this method requires all
-  ///         parameters. This method eventually gets called when presenting a view controller with
-  ///         an URL, so it's recommended to implement this method only for mocking.
-  @discardableResult
-  open func present(_ url: URLConvertible, context: Any? = nil, wrap: UINavigationController.Type? = nil, from: UIViewControllerType? = nil, animated: Bool = true, completion: (() -> Void)? = nil) -> UIViewController? {
-    guard let viewController = self.viewController(for: url, context: context) else { return nil }
-    return self.present(viewController, wrap: wrap, from: from, animated: animated, completion: completion)
-  }
-
-  /// Presents the view controller.
-  ///
-  /// - note: It is not a good idea to use this method directly because this method requires all
-  ///         parameters. This method eventually gets called when presenting a view controller, so
-  ///         it's recommended to implement this method only for mocking.
-  @discardableResult
-  open func present(_ viewController: UIViewController, wrap: UINavigationController.Type? = nil, from: UIViewControllerType? = nil, animated: Bool = true, completion: (() -> Void)? = nil) -> UIViewController? {
-    guard let fromViewController = from ?? UIViewController.topMost else { return nil }
-
-    let viewControllerToPresent: UIViewController
-    if let navigationControllerClass = wrap, (viewController is UINavigationController) == false {
-      viewControllerToPresent = navigationControllerClass.init(rootViewController: viewController)
-    } else {
-      viewControllerToPresent = viewController
-    }
-
-    guard self.delegate?.shouldPresent(viewController: viewController, from: fromViewController) != false else { return nil }
-    fromViewController.present(viewControllerToPresent, animated: animated, completion: completion)
-    return viewController
-  }
-
-
-  // MARK: Open
-
-  /// Executes an URL open handler.
-  ///
-  /// - note: It is not a good idea to use this method directly because this method requires all
-  ///         parameters. This method eventually gets called when opening an url, so it's
-  ///         recommended to implement this method only for mocking.
-  @discardableResult
-  open func open(_ url: URLConvertible, context: Any? = nil) -> Bool {
-    guard let handler = self.handler(for: url, context: context) else { return false }
-    return handler()
-  }
 }
 #endif

+ 1 - 1
Pods/URLNavigator/Sources/URLNavigator/NavigatorDelegate.swift

@@ -1,7 +1,7 @@
 #if os(iOS) || os(tvOS)
 import UIKit
 
-public protocol NavigatorDelegate: AnyObject {
+public protocol NavigatorDelegate: class {
   /// Returns whether the navigator should push the view controller or not. It returns `true` for
   /// default.
   func shouldPush(viewController: UIViewController, from: UINavigationControllerType) -> Bool

+ 2 - 2
Pods/URLNavigator/Sources/URLNavigator/UIViewControllerType.swift

@@ -1,11 +1,11 @@
 #if os(iOS) || os(tvOS)
 import UIKit
 
-public protocol UINavigationControllerType: AnyObject {
+public protocol UINavigationControllerType {
   func pushViewController(_ viewController: UIViewController, animated: Bool)
 }
 
-public protocol UIViewControllerType: AnyObject {
+public protocol UIViewControllerType {
   func present(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)?)
 }