├── .gitignore ├── .swiftlint.yml ├── LICENSE ├── LocalLib ├── BBNetworkEx │ ├── Assets │ │ └── image.xcassets │ │ │ ├── Contents.json │ │ │ ├── bblive_2233_loading_error_128x94_@2x.png │ │ │ ├── bblive_2233_loading_error_128x94_@3x.png │ │ │ ├── bblivebc_battle_pk_panel_load_error_184x140_@2x.png │ │ │ ├── bblivebc_battle_pk_panel_load_error_184x140_@3x.png │ │ │ ├── load_error_1.imageset │ │ │ ├── Contents.json │ │ │ ├── bblive_2233_loading_error_128x94_@2x.png │ │ │ └── bblive_2233_loading_error_128x94_@3x.png │ │ │ ├── load_error_2.imageset │ │ │ ├── Contents.json │ │ │ ├── bblivebc_battle_pk_panel_load_error_184x140_@2x.png │ │ │ └── bblivebc_battle_pk_panel_load_error_184x140_@3x.png │ │ │ ├── loading_1.imageset │ │ │ ├── Contents.json │ │ │ ├── loading_1_280x158_@2x.png │ │ │ └── loading_1_280x158_@3x.png │ │ │ ├── loading_2.imageset │ │ │ ├── Contents.json │ │ │ ├── loading_2_280x158_@2x.png │ │ │ └── loading_2_280x158_@3x.png │ │ │ └── loading_3.imageset │ │ │ ├── Contents.json │ │ │ ├── loading_3_280x158_@2x.png │ │ │ └── loading_3_280x158_@3x.png │ ├── BBNetworkEx.podspec │ ├── Classes │ │ ├── CommonParameters.swift │ │ ├── LoadingView.swift │ │ ├── Network+Cache.swift │ │ ├── Network+Error.swift │ │ ├── Network+Loading.swift │ │ ├── NotReachableView.swift │ │ └── ResourceLoader.swift │ ├── LICENSE │ └── README.md ├── LWExtensionKit │ ├── Classes │ │ ├── Base │ │ │ └── ExtensionCompatible.swift │ │ ├── Foundation │ │ │ └── String │ │ │ │ └── String+Extension.swift │ │ └── UIKit │ │ │ ├── UIColor │ │ │ └── UIColor+Extension.swift │ │ │ ├── UIDevice │ │ │ └── UIDevice+Extension.swift │ │ │ └── UIViewController │ │ │ └── UIViewController+Extension.swift │ ├── LICENSE │ ├── LWExtensionKit.podspec │ └── README.md └── LWNetwork │ ├── Classes │ ├── Cache │ │ ├── CachingKey+Moya.swift │ │ ├── Expirable.swift │ │ ├── Expiry.swift │ │ ├── Observable+Cache.swift │ │ ├── OnCache.swift │ │ ├── Single+Cache.swift │ │ ├── Storable.swift │ │ └── SugarTargetType+Cache.swift │ ├── Core │ │ ├── ActivityIndictor.swift │ │ ├── Configuration.swift │ │ ├── Network.swift │ │ ├── NetworkError.swift │ │ ├── NetworkLoggerPlugin.swift │ │ ├── NetworkReachabilityManager.swift │ │ ├── ObservableType+ObjectMapper.swift │ │ ├── Response+ObjectMapper.swift │ │ └── SugarTargetType+Rx.swift │ └── MoyaSugar │ │ ├── MoyaSugarProvider.swift │ │ ├── MultiSugarTarget.swift │ │ ├── Parameters.swift │ │ ├── Route.swift │ │ └── SugarTargetType.swift │ ├── LICENSE │ ├── LWNetwork.podspec │ └── README.md ├── Podfile ├── Podfile.lock ├── Resource ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ ├── AppIcon29x29@2x.png │ │ ├── AppIcon29x29@2x~ipad.png │ │ ├── AppIcon29x29@3x.png │ │ ├── AppIcon29x29~ipad.png │ │ ├── AppIcon40x40@2x.png │ │ ├── AppIcon40x40@2x~ipad.png │ │ ├── AppIcon40x40@3x.png │ │ ├── AppIcon40x40~ipad.png │ │ ├── AppIcon60x60@2x.png │ │ ├── AppIcon60x60@3x.png │ │ ├── AppIcon76x76@2x~ipad.png │ │ ├── AppIcon76x76~ipad.png │ │ ├── AppIcon83.5x83.5@2x~ipad.png │ │ └── Contents.json │ ├── Common │ │ ├── Contents.json │ │ ├── arrow_right_white_24x24_.imageset │ │ │ ├── Contents.json │ │ │ ├── arrow_right_white_24x24_@2x.png │ │ │ └── arrow_right_white_24x24_@3x.png │ │ └── column_back_white_8x14_.imageset │ │ │ ├── Contents.json │ │ │ ├── column_back_white_8x14_@2x.png │ │ │ └── column_back_white_8x14_@3x.png │ ├── Contents.json │ ├── Empty │ │ ├── Contents.json │ │ └── empty_coupon.imageset │ │ │ ├── Contents.json │ │ │ ├── empty_coupon@2x.png │ │ │ └── empty_coupon@3x.png │ ├── Home │ │ ├── Contents.json │ │ ├── top_btn_game_centerhome_icon_22x22_.imageset │ │ │ ├── Contents.json │ │ │ ├── top_btn_game_centerhome_icon_22x22_@2x.png │ │ │ └── top_btn_game_centerhome_icon_22x22_@3x.png │ │ └── top_btn_linkim_home_icon_22x22_.imageset │ │ │ ├── Contents.json │ │ │ ├── top_btn_linkim_home_icon_22x22_@2x.png │ │ │ └── top_btn_linkim_home_icon_22x22_@3x.png │ ├── Launch │ │ ├── Contents.json │ │ ├── launcher_image_2020_0709.imageset │ │ │ ├── Contents.json │ │ │ ├── launcher_image_2020_0709@2x.png │ │ │ └── launcher_image_2020_0709@3x.png │ │ ├── launcher_logo_2020.imageset │ │ │ ├── Contents.json │ │ │ ├── launcher_logo_2020@2x.png │ │ │ └── launcher_logo_2020@3x.png │ │ └── player_shadow_logo_40x17_.imageset │ │ │ ├── Contents.json │ │ │ ├── player_shadow_logo_40x17_@2x.png │ │ │ └── player_shadow_logo_40x17_@3x.png │ ├── Loading │ │ ├── Contents.json │ │ ├── loading_1_280x158_.imageset │ │ │ ├── Contents.json │ │ │ ├── loading_1_280x158_@2x.png │ │ │ └── loading_1_280x158_@3x.png │ │ ├── loading_2_280x158_.imageset │ │ │ ├── Contents.json │ │ │ ├── loading_2_280x158_@2x.png │ │ │ └── loading_2_280x158_@3x.png │ │ ├── loading_3_280x158_.imageset │ │ │ ├── Contents.json │ │ │ ├── loading_3_280x158_@2x.png │ │ │ └── loading_3_280x158_@3x.png │ │ ├── pull_loading_1_60x60_.imageset │ │ │ ├── Contents.json │ │ │ ├── pull_loading_1_60x60_@2x.png │ │ │ └── pull_loading_1_60x60_@3x.png │ │ ├── pull_loading_2_60x60_.imageset │ │ │ ├── Contents.json │ │ │ ├── pull_loading_2_60x60_@2x.png │ │ │ └── pull_loading_2_60x60_@3x.png │ │ ├── pull_loading_3_60x60_.imageset │ │ │ ├── Contents.json │ │ │ ├── pull_loading_3_60x60_@2x.png │ │ │ └── pull_loading_3_60x60_@3x.png │ │ └── pull_loading_4_60x60_.imageset │ │ │ ├── Contents.json │ │ │ ├── pull_loading_4_60x60_@2x.png │ │ │ └── pull_loading_4_60x60_@3x.png │ ├── NetError │ │ ├── Contents.json │ │ ├── bblive_2233_loading_error_128x94_.imageset │ │ │ ├── Contents.json │ │ │ ├── bblive_2233_loading_error_128x94_@2x.png │ │ │ └── bblive_2233_loading_error_128x94_@3x.png │ │ └── bblivebc_battle_pk_panel_load_error_184x140_.imageset │ │ │ ├── Contents.json │ │ │ ├── bblivebc_battle_pk_panel_load_error_184x140_@2x.png │ │ │ └── bblivebc_battle_pk_panel_load_error_184x140_@3x.png │ └── TabBar │ │ ├── Contents.json │ │ ├── bottom_tabbar_followinghome_normal_22x22_.imageset │ │ ├── Contents.json │ │ ├── bottom_tabbar_followinghome_normal_22x22_@2x.png │ │ └── bottom_tabbar_followinghome_normal_22x22_@3x.png │ │ ├── bottom_tabbar_followinghome_selected_22x22_.imageset │ │ ├── Contents.json │ │ ├── bottom_tabbar_followinghome_selected_22x22_@2x.png │ │ └── bottom_tabbar_followinghome_selected_22x22_@3x.png │ │ ├── bottom_tabbar_mainhome_normal_22x22_.imageset │ │ ├── Contents.json │ │ ├── bottom_tabbar_mainhome_normal_22x22_@2x.png │ │ └── bottom_tabbar_mainhome_normal_22x22_@3x.png │ │ ├── bottom_tabbar_mainhome_selected_22x22_.imageset │ │ ├── Contents.json │ │ ├── bottom_tabbar_mainhome_selected_22x22_@2x.png │ │ └── bottom_tabbar_mainhome_selected_22x22_@3x.png │ │ ├── bottom_tabbar_mallhome_normal_22x22_.imageset │ │ ├── Contents.json │ │ ├── bottom_tabbar_mallhome_normal_22x22_@2x.png │ │ └── bottom_tabbar_mallhome_normal_22x22_@3x.png │ │ ├── bottom_tabbar_mallhome_selected_22x22_.imageset │ │ ├── Contents.json │ │ ├── bottom_tabbar_mallhome_selected_22x22_@2x.png │ │ └── bottom_tabbar_mallhome_selected_22x22_@3x.png │ │ ├── bottom_tabbar_pegasuschannel_normal_22x22_.imageset │ │ ├── Contents.json │ │ ├── bottom_tabbar_pegasuschannel_normal_22x22_@2x.png │ │ └── bottom_tabbar_pegasuschannel_normal_22x22_@3x.png │ │ ├── bottom_tabbar_pegasuschannel_selected_22x22_.imageset │ │ ├── Contents.json │ │ ├── bottom_tabbar_pegasuschannel_selected_22x22_@2x.png │ │ └── bottom_tabbar_pegasuschannel_selected_22x22_@3x.png │ │ ├── bottom_tabbar_user_center_normal_22x22_.imageset │ │ ├── Contents.json │ │ ├── bottom_tabbar_user_center_normal_22x22_@2x.png │ │ └── bottom_tabbar_user_center_normal_22x22_@3x.png │ │ └── bottom_tabbar_user_center_selected_22x22_.imageset │ │ ├── Contents.json │ │ ├── bottom_tabbar_user_center_selected_22x22_@2x.png │ │ └── bottom_tabbar_user_center_selected_22x22_@3x.png ├── Base.lproj │ └── LaunchScreen.storyboard ├── Image │ ├── launcher_logo_2020@2x.png │ └── launcher_logo_2020@3x.png └── JSON │ └── MainStyle.json ├── Sources ├── AppDelegate.swift ├── Base │ ├── BaseNavigationController.swift │ ├── BaseTabBarController.swift │ ├── BaseView.swift │ ├── BaseViewController.swift │ └── BaseWebViewController.swift ├── Components │ ├── LaunchAd │ │ ├── LaunchAd.swift │ │ ├── LaunchAdBottomView.swift │ │ ├── LaunchAdTitleView.swift │ │ ├── LaunchAdVideoView.swift │ │ └── LaunchAdViewController.swift │ └── Toast │ │ └── Toast.swift ├── CompositionRoot.swift ├── Const │ ├── Const.swift │ ├── Font │ │ └── Font.swift │ ├── Image │ │ └── Image.swift │ └── Screen │ │ └── Screen.swift ├── Extensions │ ├── Rx │ │ └── MapAt.swift │ └── UIKit │ │ ├── UIButton+Kingfisher.swift │ │ ├── UIColor+Hex.swift │ │ ├── UIImageView+Kingfisher.swift │ │ ├── UIView+Extension.swift │ │ └── UIViewController+PresentModal.swift ├── Modules │ ├── Channel │ │ └── ChannelViewController.swift │ ├── Dynamic │ │ └── DynamicViewController.swift │ ├── Home │ │ ├── Controllers │ │ │ ├── HomeActivityViewController.swift │ │ │ ├── HomeBangumiViewController.swift │ │ │ ├── HomeCinemaViewController.swift │ │ │ ├── HomeHotViewController.swift │ │ │ ├── HomeLiveViewController.swift │ │ │ ├── HomeMainViewController.swift │ │ │ ├── HomeOptionalViewController.swift │ │ │ └── HomePromoViewController.swift │ │ ├── Custom │ │ │ └── SegmentedDataSource │ │ │ │ ├── BBSegmentedTitleIOrImagetemModel.swift │ │ │ │ ├── BBSegmentedTitleOrImageCell.swift │ │ │ │ └── BBSegmentedTitleOrImageDataSource.swift │ │ └── Models │ │ │ └── HomeTabModel.swift │ ├── LaunchModule │ │ ├── API │ │ │ └── LaunchAPI.swift │ │ ├── Cache │ │ │ └── LaunchUserDefaults.swift │ │ ├── Controllers │ │ │ └── SplashViewController.swift │ │ ├── LaunchImages.xcassets │ │ │ ├── Contents.json │ │ │ ├── default_splash.imageset │ │ │ │ ├── Contents.json │ │ │ │ ├── launcher_image_2020_0709@2x.png │ │ │ │ └── launcher_image_2020_0709@3x.png │ │ │ └── logo.imageset │ │ │ │ ├── Contents.json │ │ │ │ ├── launcher_logo_2020@2x.png │ │ │ │ └── launcher_logo_2020@3x.png │ │ ├── Models │ │ │ └── SplashInfoModel.swift │ │ └── Views │ │ │ └── PrivacyPolicyView.swift │ ├── Login │ │ └── LoginViewController.swift │ ├── Member │ │ └── MemberViewController.swift │ └── Mine │ │ └── MineViewController.swift ├── Router │ ├── Home │ │ ├── HomeRouter.swift │ │ ├── HomeRouterLoginPlugin.swift │ │ ├── HomeRouterOpener.swift │ │ └── HomeRouterType.swift │ ├── Main │ │ ├── Router.swift │ │ ├── RouterOpener.swift │ │ └── RouterType.swift │ └── Navigator │ │ ├── Context.swift │ │ ├── Plugin.swift │ │ ├── Protocol.swift │ │ └── Provider.swift ├── Services │ └── API │ │ ├── ConfigAPI.swift │ │ └── HomeAPI.swift ├── Tools │ ├── AutoInch │ │ ├── Auto.swift │ │ └── Inch.swift │ ├── CGFloatLiteral │ │ └── CGFloatLiteral.swift │ ├── Logger │ │ └── Logger.swift │ ├── ManualLayout │ │ ├── CALayer+ManualLayout.swift │ │ ├── CGRect+ManualLayout.swift │ │ ├── HelperFunctions.swift │ │ ├── SmartAssign.swift │ │ ├── UIScrollView+ManualLayout.swift │ │ ├── UIView+ManualLayout.swift │ │ └── UIViewController+ManualLayout.swift │ ├── ObjectMapperTransform │ │ └── HexColorTransform.swift │ ├── Protocol │ │ ├── AssociatedObjectStore.swift │ │ ├── Base.swift │ │ ├── EmptyDataSetable.swift │ │ ├── Nibloadable.swift │ │ └── Refreshable.swift │ ├── Theme │ │ ├── Theme.swift │ │ ├── ThemeManager.swift │ │ └── ThemeModel.swift │ └── Timer │ │ ├── SwiftCountDownTimer.swift │ │ └── SwiftTimer.swift └── Utils │ ├── Cache │ ├── AdCacheManager.swift │ └── SplashCacheManager.swift │ ├── LaunchAdManager │ ├── AdInfoModel.swift │ └── LaunchAdManager.swift │ ├── PermissionManager │ └── PermissionManager.swift │ ├── UserDefaultsManager │ ├── DefaultsKeys+UserDefaults.swift │ └── UserDefaultsManager.swift │ └── Utils.swift ├── Supporting Files └── Info.plist ├── SwiftBilibili.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── xcshareddata │ └── xcschemes │ └── SwiftBilibili.xcscheme └── SwiftBilibili.xcworkspace ├── contents.xcworkspacedata ├── xcshareddata └── IDEWorkspaceChecks.plist └── xcuserdata └── fd.xcuserdatad └── xcdebugger └── Breakpoints_v2.xcbkptlist /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | .DS_Store 3 | */build/* 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | xcuserdata 13 | profile 14 | *.moved-aside 15 | DerivedData 16 | .idea/ 17 | *.hmap 18 | *.xccheckout 19 | *.xcworkspace 20 | !default.xcworkspace 21 | 22 | #CocoaPods 23 | Pods 24 | Podfile.lock 25 | -------------------------------------------------------------------------------- /.swiftlint.yml: -------------------------------------------------------------------------------- 1 | disabled_rules: # 执行时排除掉的规则 2 | - colon 3 | - comma 4 | - control_statement 5 | - missing_docs 6 | - large_tuple 7 | - multiple_closures_with_trailing_closure 8 | - redundant_string_enum_value 9 | - function_body_length 10 | opt_in_rules: # 一些规则仅仅是可选的 11 | - empty_count 12 | # 可以通过执行如下指令来查找所有可用的规则: 13 | # swiftlint rules 14 | excluded: # 执行 linting 时忽略的路径。 优先级比 `included` 更高。 15 | - Carthage 16 | - Pods 17 | - Sources/ExcludedFolder 18 | - Sources/ExcludedFile.swift 19 | 20 | # 可配置的规则可以通过这个配置文件来自定义 21 | # 二进制规则可以设置他们的严格程度 22 | force_cast: warning # 隐式 23 | force_try: warning # 显式 24 | # 同时有警告和错误等级的规则,可以只设置它的警告等级 25 | # 隐式 26 | line_length: 200 27 | # 可以通过一个数组同时进行隐式设置 28 | type_body_length: 29 | - 300 # warning 30 | - 400 # error 31 | # 或者也可以同时进行显式设置 32 | file_length: 33 | warning: 500 34 | error: 1200 35 | # 命名规则可以设置最小长度和最大程度的警告/错误 36 | # 此外它们也可以设置排除在外的名字 37 | type_name: 38 | min_length: 2 # 只是警告 39 | max_length: # 警告和错误 40 | warning: 40 41 | error: 50 42 | excluded: iPhone # 排除某个名字 43 | allowed_symbols: ["_"] 44 | identifier_name: 45 | min_length: # only min_length 46 | warning: 1 47 | error: 1 # only error 48 | excluded: # excluded via string array 49 | - id 50 | - URL 51 | - GlobalAPIKey 52 | reporter: "xcode" # 报告类型 (xcode, json, csv, checkstyle) 53 | cyclomatic_complexity: 30 54 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 罗文 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /LocalLib/BBNetworkEx/Assets/image.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /LocalLib/BBNetworkEx/Assets/image.xcassets/bblive_2233_loading_error_128x94_@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luowen1991/SwiftBilibili/fcf130b78be1e4b844a5e26df40e862a02bb986c/LocalLib/BBNetworkEx/Assets/image.xcassets/bblive_2233_loading_error_128x94_@2x.png -------------------------------------------------------------------------------- /LocalLib/BBNetworkEx/Assets/image.xcassets/bblive_2233_loading_error_128x94_@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luowen1991/SwiftBilibili/fcf130b78be1e4b844a5e26df40e862a02bb986c/LocalLib/BBNetworkEx/Assets/image.xcassets/bblive_2233_loading_error_128x94_@3x.png -------------------------------------------------------------------------------- /LocalLib/BBNetworkEx/Assets/image.xcassets/bblivebc_battle_pk_panel_load_error_184x140_@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luowen1991/SwiftBilibili/fcf130b78be1e4b844a5e26df40e862a02bb986c/LocalLib/BBNetworkEx/Assets/image.xcassets/bblivebc_battle_pk_panel_load_error_184x140_@2x.png -------------------------------------------------------------------------------- /LocalLib/BBNetworkEx/Assets/image.xcassets/bblivebc_battle_pk_panel_load_error_184x140_@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luowen1991/SwiftBilibili/fcf130b78be1e4b844a5e26df40e862a02bb986c/LocalLib/BBNetworkEx/Assets/image.xcassets/bblivebc_battle_pk_panel_load_error_184x140_@3x.png -------------------------------------------------------------------------------- /LocalLib/BBNetworkEx/Assets/image.xcassets/load_error_1.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "bblive_2233_loading_error_128x94_@2x.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "bblive_2233_loading_error_128x94_@3x.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /LocalLib/BBNetworkEx/Assets/image.xcassets/load_error_1.imageset/bblive_2233_loading_error_128x94_@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luowen1991/SwiftBilibili/fcf130b78be1e4b844a5e26df40e862a02bb986c/LocalLib/BBNetworkEx/Assets/image.xcassets/load_error_1.imageset/bblive_2233_loading_error_128x94_@2x.png -------------------------------------------------------------------------------- /LocalLib/BBNetworkEx/Assets/image.xcassets/load_error_1.imageset/bblive_2233_loading_error_128x94_@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luowen1991/SwiftBilibili/fcf130b78be1e4b844a5e26df40e862a02bb986c/LocalLib/BBNetworkEx/Assets/image.xcassets/load_error_1.imageset/bblive_2233_loading_error_128x94_@3x.png -------------------------------------------------------------------------------- /LocalLib/BBNetworkEx/Assets/image.xcassets/load_error_2.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "bblivebc_battle_pk_panel_load_error_184x140_@2x.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "bblivebc_battle_pk_panel_load_error_184x140_@3x.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /LocalLib/BBNetworkEx/Assets/image.xcassets/load_error_2.imageset/bblivebc_battle_pk_panel_load_error_184x140_@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luowen1991/SwiftBilibili/fcf130b78be1e4b844a5e26df40e862a02bb986c/LocalLib/BBNetworkEx/Assets/image.xcassets/load_error_2.imageset/bblivebc_battle_pk_panel_load_error_184x140_@2x.png -------------------------------------------------------------------------------- /LocalLib/BBNetworkEx/Assets/image.xcassets/load_error_2.imageset/bblivebc_battle_pk_panel_load_error_184x140_@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luowen1991/SwiftBilibili/fcf130b78be1e4b844a5e26df40e862a02bb986c/LocalLib/BBNetworkEx/Assets/image.xcassets/load_error_2.imageset/bblivebc_battle_pk_panel_load_error_184x140_@3x.png -------------------------------------------------------------------------------- /LocalLib/BBNetworkEx/Assets/image.xcassets/loading_1.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "loading_1_280x158_@2x.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "loading_1_280x158_@3x.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /LocalLib/BBNetworkEx/Assets/image.xcassets/loading_1.imageset/loading_1_280x158_@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luowen1991/SwiftBilibili/fcf130b78be1e4b844a5e26df40e862a02bb986c/LocalLib/BBNetworkEx/Assets/image.xcassets/loading_1.imageset/loading_1_280x158_@2x.png -------------------------------------------------------------------------------- /LocalLib/BBNetworkEx/Assets/image.xcassets/loading_1.imageset/loading_1_280x158_@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luowen1991/SwiftBilibili/fcf130b78be1e4b844a5e26df40e862a02bb986c/LocalLib/BBNetworkEx/Assets/image.xcassets/loading_1.imageset/loading_1_280x158_@3x.png -------------------------------------------------------------------------------- /LocalLib/BBNetworkEx/Assets/image.xcassets/loading_2.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "loading_2_280x158_@2x.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "loading_2_280x158_@3x.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /LocalLib/BBNetworkEx/Assets/image.xcassets/loading_2.imageset/loading_2_280x158_@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luowen1991/SwiftBilibili/fcf130b78be1e4b844a5e26df40e862a02bb986c/LocalLib/BBNetworkEx/Assets/image.xcassets/loading_2.imageset/loading_2_280x158_@2x.png -------------------------------------------------------------------------------- /LocalLib/BBNetworkEx/Assets/image.xcassets/loading_2.imageset/loading_2_280x158_@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luowen1991/SwiftBilibili/fcf130b78be1e4b844a5e26df40e862a02bb986c/LocalLib/BBNetworkEx/Assets/image.xcassets/loading_2.imageset/loading_2_280x158_@3x.png -------------------------------------------------------------------------------- /LocalLib/BBNetworkEx/Assets/image.xcassets/loading_3.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "loading_3_280x158_@2x.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "loading_3_280x158_@3x.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /LocalLib/BBNetworkEx/Assets/image.xcassets/loading_3.imageset/loading_3_280x158_@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luowen1991/SwiftBilibili/fcf130b78be1e4b844a5e26df40e862a02bb986c/LocalLib/BBNetworkEx/Assets/image.xcassets/loading_3.imageset/loading_3_280x158_@2x.png -------------------------------------------------------------------------------- /LocalLib/BBNetworkEx/Assets/image.xcassets/loading_3.imageset/loading_3_280x158_@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luowen1991/SwiftBilibili/fcf130b78be1e4b844a5e26df40e862a02bb986c/LocalLib/BBNetworkEx/Assets/image.xcassets/loading_3.imageset/loading_3_280x158_@3x.png -------------------------------------------------------------------------------- /LocalLib/BBNetworkEx/BBNetworkEx.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # Be sure to run `pod spec lint LWNetworking.podspec' to ensure this is a 3 | # valid spec and to remove all comments including this before submitting the spec. 4 | # 5 | # To learn more about Podspec attributes see https://guides.cocoapods.org/syntax/podspec.html 6 | # To see working Podspecs in the CocoaPods repo see https://github.com/CocoaPods/Specs/ 7 | # 8 | 9 | Pod::Spec.new do |spec| 10 | 11 | spec.name = "BBNetworkEx" 12 | spec.version = "1.0.0" 13 | spec.summary = "网络业务库" 14 | spec.description = "基于LWNetwork实现的业务库" 15 | 16 | spec.homepage = "https://www.luowen.cn" 17 | 18 | spec.license = { :type => 'MIT', :file => 'LICENSE' } 19 | 20 | spec.author = { "luowen" => "luowen@dushu.io" } 21 | spec.source = { :git => 'git@gitlab.dushuclub.io:swift/LWNetwork.git', :tag => "#{spec.version}" } 22 | 23 | spec.platform = :ios, "11.0" 24 | spec.swift_version = '5.0' 25 | 26 | spec.resource_bundles = { 27 | 'BBNetworkEx' => ['Assets/*.xcassets'] 28 | } 29 | 30 | spec.source_files = 'Classes/**/*' 31 | spec.dependency 'LWNetwork' 32 | spec.dependency 'SnapKit' 33 | spec.dependency 'Then' 34 | spec.dependency 'Toast-Swift' 35 | spec.dependency 'ESPullToRefresh' 36 | spec.dependency 'LWExtensionKit' 37 | 38 | end 39 | -------------------------------------------------------------------------------- /LocalLib/BBNetworkEx/Classes/CommonParameters.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CommonParamters.swift 3 | // BBNetworkEx 4 | // 5 | // Created by luowen on 2021/10/19. 6 | // 7 | 8 | import LWExtensionKit 9 | 10 | // 由于sing是根据所有参数md5加密而来 在不知道盐值的情况下只能按抓包的写死 11 | public enum ParamrtersUtils { 12 | 13 | // token 如果存在必须在第一个参数 14 | public static let accessKey: String? = nil 15 | 16 | public static var commonParameters: [String: Any] = [ 17 | "appkey": "27eb53fc9058f8c3", 18 | "actionKey": "appkey", 19 | "build": 64400100, 20 | "c_locale": "zh-Hans_CN", 21 | "device": "phone", 22 | "mobi_app": "iphone", 23 | "platform": "ios", 24 | "s_locale": "zh-Hans_CN", 25 | "statistics": "%7B%22appId%22%3A1%2C%22version%22%3A%226.44.0%22%2C%22abtest%22%3A%22%22%2C%22platform%22%3A1%7D" 26 | ] 27 | 28 | } 29 | 30 | extension ParamrtersUtils { 31 | 32 | static var language: String { 33 | guard let languages = UserDefaults.standard.object(forKey: "AppleLanguages") as? [String], 34 | let defaultLanguage = languages.first 35 | else { return "zh-Hans_CN" } 36 | return defaultLanguage 37 | } 38 | 39 | static var statistics: String { 40 | let dict: [String: Any] = ["abtest": "","appId": 1,"platform": 1,"version": "6.44.0"] 41 | guard let data = try? JSONSerialization.data(withJSONObject: dict, options: .fragmentsAllowed), 42 | let str = String(data: data, encoding: .utf8) 43 | else { 44 | return "" 45 | } 46 | return str 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /LocalLib/BBNetworkEx/Classes/LoadingView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LoadingView.swift 3 | // SwiftBilibili 4 | // 5 | // Created by luowen on 2021/10/13. 6 | // Copyright © 2021 luowen. All rights reserved. 7 | // 发起请求后的加载视图 8 | 9 | import Foundation 10 | import UIKit 11 | import Then 12 | 13 | final class LoadingView: UIView { 14 | 15 | override init(frame: CGRect) { 16 | super.init(frame: frame) 17 | setupViews() 18 | } 19 | 20 | required init?(coder: NSCoder) { 21 | fatalError("init(coder:) has not been implemented") 22 | } 23 | 24 | func setupViews() { 25 | addSubview(containerView) 26 | containerView.addSubview(animationImageView) 27 | containerView.addSubview(textLabel) 28 | } 29 | 30 | override func didMoveToSuperview() { 31 | super.didMoveToSuperview() 32 | animationImageView.startAnimating() 33 | } 34 | 35 | override func removeFromSuperview() { 36 | animationImageView.stopAnimating() 37 | super.removeFromSuperview() 38 | } 39 | 40 | override func layoutSubviews() { 41 | super.layoutSubviews() 42 | 43 | containerView.snp.makeConstraints { 44 | $0.center.equalToSuperview() 45 | } 46 | 47 | animationImageView.snp.makeConstraints { 48 | $0.top.left.right.equalToSuperview() 49 | } 50 | 51 | textLabel.snp.makeConstraints { 52 | $0.left.right.bottom.equalToSuperview() 53 | $0.top.equalTo(animationImageView.snp.bottom).offset(20) 54 | } 55 | } 56 | 57 | private let containerView = UIView().then { 58 | $0.backgroundColor = .clear 59 | } 60 | 61 | private let textLabel = UILabel().then { 62 | $0.text = "正在努力加载数据中..." 63 | $0.font = .systemFont(ofSize: 15) 64 | $0.textColor = UIColor(hex: "FF999999") 65 | $0.textAlignment = .center 66 | } 67 | 68 | private let animationImageView = UIImageView().then { 69 | $0.animationRepeatCount = 0 70 | var animationImages: [UIImage] = [] 71 | for i in 0..<3 { 72 | let imageName = "loading_\(i+1)" 73 | guard let image = ResourceLoader.imageNamed(imageName) else { continue } 74 | animationImages.append(image) 75 | } 76 | $0.animationImages = animationImages 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /LocalLib/BBNetworkEx/Classes/Network+Cache.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Network+Cache.swift 3 | // SwiftBilibili 4 | // 5 | // Created by luowen on 2021/10/9. 6 | // Copyright © 2021 luowen. All rights reserved. 7 | // 8 | 9 | import Moya 10 | import LWNetwork 11 | import LWExtensionKit 12 | 13 | 14 | extension Storable where Self: SugarTargetType { 15 | 16 | private var cachedURL: URL { 17 | guard let path = NSSearchPathForDirectoriesInDomains( 18 | .cachesDirectory, 19 | .userDomainMask, 20 | true).last 21 | else { 22 | fatalError("Couldn't search cache's directory.") 23 | } 24 | return URL(fileURLWithPath: path) 25 | } 26 | 27 | public var allowsStorage: (Response) -> Bool { 28 | return { _ in true } 29 | } 30 | 31 | public func cachedResponse(for key: CachingKey) throws -> Response { 32 | let data = try Data(contentsOf: md5Key(key)) 33 | return Response(statusCode: 200, data: data) 34 | } 35 | 36 | public func storeCachedResponse(_ cachedResponse: Response, for key: CachingKey) throws { 37 | try cachedResponse.data.write(to: md5Key(key)) 38 | } 39 | 40 | public func removeCachedResponse(for key: CachingKey) throws { 41 | try FileManager.default.removeItem(at: md5Key(key)) 42 | } 43 | 44 | public func removeAllCachedResponses() throws { 45 | try FileManager.default.removeItem(at: cachedURL) 46 | } 47 | 48 | private func md5Key(_ key: CachingKey) -> URL { 49 | return cachedURL.appendingPathComponent(key.stringValue.ex.toMD5) 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /LocalLib/BBNetworkEx/Classes/ResourceLoader.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ResourceLoader.swift 3 | // BBNetworkEx 4 | // 5 | // Created by luowen on 2021/10/14. 6 | // 7 | 8 | final class ResourceLoader { 9 | 10 | private static let bundle = Bundle(path: Bundle(for: ResourceLoader.self).path(forResource: "BBNetworkEx", ofType: "bundle") ?? "") 11 | 12 | static func imageNamed(_ name: String) -> UIImage? { 13 | guard !name.isEmpty, 14 | let image = UIImage(named: name, in: bundle, compatibleWith: nil) 15 | else { return nil } 16 | return image 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /LocalLib/BBNetworkEx/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright © 2018 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /LocalLib/BBNetworkEx/README.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /LocalLib/LWExtensionKit/Classes/Base/ExtensionCompatible.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LWExtensionCompatible.swift 3 | // LWExtensionKit 4 | // 5 | // Created by luowen on 2021/10/14. 6 | // 7 | 8 | public protocol ExtensionCompatible {} 9 | 10 | public extension ExtensionCompatible { 11 | 12 | var ex: ExtensionNameSpace { 13 | ExtensionNameSpace(self) 14 | } 15 | 16 | static var ex: ExtensionNameSpaceStatic { 17 | ExtensionNameSpaceStatic(Self.self) 18 | } 19 | } 20 | 21 | public class ExtensionNameSpace { 22 | var base: Base 23 | init(_ instance: Base) { 24 | self.base = instance 25 | } 26 | } 27 | 28 | public class ExtensionNameSpaceStatic { 29 | var baseType: Base.Type 30 | init(_ instance: Base.Type) { 31 | self.baseType = instance 32 | } 33 | } 34 | 35 | extension String: ExtensionCompatible {} 36 | 37 | extension UIColor: ExtensionCompatible {} 38 | extension UIDevice: ExtensionCompatible {} 39 | extension UIViewController: ExtensionCompatible {} 40 | -------------------------------------------------------------------------------- /LocalLib/LWExtensionKit/Classes/Foundation/String/String+Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // String+Extension.swift 3 | // LWExtensionKit 4 | // 5 | // Created by luowen on 2021/10/19. 6 | // 7 | 8 | import Foundation 9 | import CommonCrypto 10 | 11 | public extension ExtensionNameSpace where Base == String { 12 | 13 | var toMD5: String { 14 | let str = base.cString(using: .utf8) 15 | let strLen = CUnsignedInt(base.lengthOfBytes(using: .utf8)) 16 | let digestLen = Int(CC_MD5_DIGEST_LENGTH) 17 | let result = UnsafeMutablePointer.allocate(capacity: 16) 18 | defer { free(result) } 19 | CC_MD5(str!, strLen, result) 20 | return (0..> 8) / 15.0 30 | green = CGFloat((hexValue & 0x0F0) >> 4) / 15.0 31 | blue = CGFloat(hexValue & 0x00F) / 15.0 32 | case 4: 33 | red = CGFloat((hexValue & 0xF000) >> 12) / 15.0 34 | green = CGFloat((hexValue & 0x0F00) >> 8) / 15.0 35 | blue = CGFloat((hexValue & 0x00F0) >> 4) / 15.0 36 | alpha = CGFloat(hexValue & 0x000F) / 15.0 37 | case 6: 38 | red = CGFloat((hexValue & 0xFF0000) >> 16) / 255.0 39 | green = CGFloat((hexValue & 0x00FF00) >> 8) / 255.0 40 | blue = CGFloat(hexValue & 0x0000FF) / 255.0 41 | case 8: 42 | red = CGFloat((hexValue & 0xFF000000) >> 24) / 255.0 43 | green = CGFloat((hexValue & 0x00FF0000) >> 16) / 255.0 44 | blue = CGFloat((hexValue & 0x0000FF00) >> 8) / 255.0 45 | alpha = CGFloat(hexValue & 0x000000FF) / 255.0 46 | default: 47 | print("Invalid RGB string, number of characters after '#' should br either 3, 4, 6 or 8") 48 | } 49 | } else { 50 | print("Scan hex error") 51 | } 52 | self.init(red: red, green: green, blue: blue, alpha: alpha) 53 | } 54 | } 55 | 56 | extension ExtensionNameSpaceStatic where Base: UIColor { 57 | 58 | public var random: UIColor { 59 | let red = CGFloat.random(in: 0...255) 60 | let green = CGFloat.random(in: 0...255) 61 | let blue = CGFloat.random(in: 0...255) 62 | return UIColor(red: red / 255.0, green: green / 255.0, blue: blue / 255.0, alpha: 1) 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /LocalLib/LWExtensionKit/Classes/UIKit/UIDevice/UIDevice+Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIDevice+Extension.swift 3 | // LWExtensionKit 4 | // 5 | // Created by luowen on 2021/10/15. 6 | // 7 | 8 | import UIKit 9 | 10 | public extension ExtensionNameSpaceStatic where Base: UIDevice { 11 | 12 | var isIphoneX: Bool { 13 | UIApplication.shared.delegate?.window??.safeAreaInsets.bottom ?? 0 > 0 14 | } 15 | 16 | var screenScale: CGFloat { UIScreen.main.scale } 17 | 18 | var screenWidth: CGFloat { UIScreen.main.bounds.size.width } 19 | 20 | var screenHeight: CGFloat { UIScreen.main.bounds.size.height } 21 | 22 | var statusBarHeight: CGFloat { isIphoneX ? 44 : 20 } 23 | 24 | var navigationBarHeight: CGFloat { statusBarHeight + 44 } 25 | 26 | var bottomSafeAreaHeight: CGFloat { isIphoneX ? 34 : 0 } 27 | 28 | var tabBarHeight: CGFloat { bottomSafeAreaHeight + 49 } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /LocalLib/LWExtensionKit/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright © 2018 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /LocalLib/LWExtensionKit/LWExtensionKit.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # Be sure to run `pod spec lint LWNetworking.podspec' to ensure this is a 3 | # valid spec and to remove all comments including this before submitting the spec. 4 | # 5 | # To learn more about Podspec attributes see https://guides.cocoapods.org/syntax/podspec.html 6 | # To see working Podspecs in the CocoaPods repo see https://github.com/CocoaPods/Specs/ 7 | # 8 | 9 | Pod::Spec.new do |spec| 10 | 11 | spec.name = "LWExtensionKit" 12 | spec.version = "1.0.0" 13 | spec.summary = "系统库的extension" 14 | spec.description = "拓展" 15 | 16 | spec.homepage = "https://www.luowen.cn" 17 | 18 | spec.license = { :type => 'MIT', :file => 'LICENSE' } 19 | 20 | spec.author = { "luowen" => "luowen@dushu.io" } 21 | spec.source = { :git => 'git@gitlab.dushuclub.io:swift/LWExtensionKit.git', :tag => "#{spec.version}" } 22 | 23 | spec.platform = :ios, "11.0" 24 | spec.swift_version = '5.0' 25 | 26 | spec.subspec 'Base' do |s| 27 | s.source_files = 'Classes/Base/*.swift' 28 | end 29 | 30 | spec.subspec 'Foundation' do |s| 31 | s.dependency 'LWExtensionKit/Base' 32 | s.subspec 'String' do |ss| 33 | ss.source_files = 'Classes/Foundation/String/*.swift' 34 | end 35 | 36 | end 37 | 38 | spec.subspec 'UIKit' do |s| 39 | s.dependency 'LWExtensionKit/Base' 40 | s.subspec 'UIColor' do |ss| 41 | ss.source_files = 'Classes/UIKit/UIColor/*.swift' 42 | end 43 | s.subspec 'UIDevice' do |ss| 44 | ss.source_files = 'Classes/UIKit/UIDevice/*.swift' 45 | end 46 | s.subspec 'UIViewController' do |ss| 47 | ss.source_files = 'Classes/UIKit/UIViewController/*.swift' 48 | end 49 | end 50 | # 51 | # spec.subspec 'Rx' do |s| 52 | # s.dependency 'RxSwift' 53 | # s.dependency 'RxCocoa' 54 | # end 55 | 56 | end 57 | -------------------------------------------------------------------------------- /LocalLib/LWExtensionKit/README.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /LocalLib/LWNetwork/Classes/Cache/CachingKey+Moya.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CachingKey+Moya.swift 3 | // LWNetwork 4 | // 5 | // Created by luowen on 2021/10/9. 6 | // 7 | 8 | extension StoringKey where Self: SugarTargetType { 9 | 10 | public var stringValue: String { 11 | return cachedKey 12 | } 13 | } 14 | 15 | extension SugarTargetType { 16 | 17 | var cachedKey: String { 18 | if let dictionary = parameters?.values { 19 | var newDictionary: [String: Any] = [:] 20 | for key in dictionary.keys.sorted() { 21 | newDictionary.updateValue(dictionary[key], forKey: key) 22 | } 23 | let newData = try? JSONSerialization.data(withJSONObject: newDictionary, options: .fragmentsAllowed) 24 | let body = String(data: newData ?? Data(), encoding: .utf8) ?? "" 25 | return "\(method.rawValue):\(defaultURL.absoluteString)?\(body)" 26 | } 27 | 28 | return "\(method.rawValue):\(defaultURL.absoluteString)" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /LocalLib/LWNetwork/Classes/Cache/Expirable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Expirable.swift 3 | // LWNetwork 4 | // 5 | // Created by luowen on 2021/10/9. 6 | // 7 | 8 | public protocol Expirable { 9 | 10 | /// 缓存过期的时间 11 | var expiry: Expiry { get } 12 | } 13 | 14 | public extension Expirable { 15 | 16 | func update(expiry: Expiry, for key: StoringKey) { 17 | UserDefaults.standard.update(expiry: expiry.date, for: key.stringValue) 18 | } 19 | 20 | func removeExpiry(for key: StoringKey) { 21 | UserDefaults.standard.removeExpiryDate(for: key.stringValue) 22 | } 23 | 24 | func expiry(for key: StoringKey) throws -> Expiry { 25 | guard let date = UserDefaults.standard.expiryDate(for: key.stringValue) else { 26 | throw Expiry.Error.noCache 27 | } 28 | 29 | return .date(date) 30 | } 31 | } 32 | 33 | private extension UserDefaults { 34 | 35 | static let expiryKey = "com.pircate.github.expiry.key" 36 | 37 | func expiryDate(for key: String) -> Date? { 38 | guard let object = object(forKey: UserDefaults.expiryKey) as? [String: Date] else { return nil } 39 | 40 | return object[key] 41 | } 42 | 43 | func update(expiry date: Date, for key: String) { 44 | guard var object = object(forKey: UserDefaults.expiryKey) as? [String: Date] else { 45 | set([key: date], forKey: UserDefaults.expiryKey) 46 | return 47 | } 48 | 49 | object.updateValue(date, forKey: key) 50 | set(object, forKey: UserDefaults.expiryKey) 51 | } 52 | 53 | func removeExpiryDate(for key: String) { 54 | guard var object = object(forKey: UserDefaults.expiryKey) as? [String: Date] else { 55 | return 56 | } 57 | 58 | object.removeValue(forKey: key) 59 | set(object, forKey: UserDefaults.expiryKey) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /LocalLib/LWNetwork/Classes/Cache/Expiry.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Expiry.swift 3 | // LWNetwork 4 | // 5 | // Created by luowen on 2021/10/9. 6 | // 7 | 8 | public enum Expiry { 9 | /// Object will be expired in the nearest future 10 | case never 11 | /// Object will be expired in the specified amount of seconds 12 | case seconds(TimeInterval) 13 | /// Object will be expired on the specified date 14 | case date(Date) 15 | 16 | /// Returns the appropriate date object 17 | public var date: Date { 18 | switch self { 19 | case .never: 20 | return Date(timeIntervalSince1970: 60 * 60 * 24 * 365 * 68) 21 | case .seconds(let seconds): 22 | return Date().addingTimeInterval(seconds) 23 | case .date(let date): 24 | return date 25 | } 26 | } 27 | 28 | /// Checks if cached object is expired according to expiration date 29 | public var isExpired: Bool { 30 | return date.timeIntervalSinceNow < 0 31 | } 32 | } 33 | 34 | public extension Expiry { 35 | 36 | enum Error: Swift.Error { 37 | case noCache 38 | case expired(Date) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /LocalLib/LWNetwork/Classes/Cache/Observable+Cache.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Observable+Cache.swift 3 | // LWNetwork 4 | // 5 | // Created by luowen on 2021/10/9. 6 | // 7 | 8 | import RxSwift 9 | import Moya 10 | 11 | extension ObservableType 12 | where Element: SugarTargetType, Element: Cacheable, Element.ResponseType == Moya.Response { 13 | 14 | public func request(timeoutInterval: TimeInterval? = nil, 15 | callbackQueue: DispatchQueue? = nil) 16 | -> Observable { 17 | 18 | return flatMap { target -> Observable in 19 | let source = target.request() 20 | .storeCachedResponse(for: target) 21 | 22 | if let response = try? target.cachedResponse(), 23 | target.allowsStorage(response) { 24 | return source.startWith(response) 25 | } 26 | 27 | return source 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /LocalLib/LWNetwork/Classes/Cache/OnCache.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OnCache.swift 3 | // LWNetwork 4 | // 5 | // Created by luowen on 2021/10/9. 6 | // 7 | 8 | import Moya 9 | import RxSwift 10 | import ObjectMapper 11 | 12 | public struct OnCache 13 | where Target: Cacheable, Target.ResponseType == Moya.Response { 14 | 15 | public let target: Target 16 | public let modelKey: String? 17 | public let delimiter: String 18 | 19 | public init(target: Target, 20 | modelKey: String?, 21 | delimiter: String) { 22 | self.target = target 23 | self.modelKey = modelKey 24 | self.delimiter = delimiter 25 | } 26 | 27 | public func requestObject(timeoutInterval: TimeInterval? = nil, 28 | callbackQueue: DispatchQueue? = nil) -> Observable { 29 | return target.request(timeoutInterval: timeoutInterval, callbackQueue: callbackQueue) 30 | .storeCachedResponse(for: target) 31 | .asObservable() 32 | .mapObject(C.self, modelKey: modelKey, delimiter: delimiter) 33 | } 34 | 35 | public func requestArray(timeoutInterval: TimeInterval? = nil, 36 | callbackQueue: DispatchQueue? = nil) -> Observable<[C]> { 37 | return target.request(timeoutInterval: timeoutInterval, callbackQueue: callbackQueue) 38 | .storeCachedResponse(for: target) 39 | .asObservable() 40 | .mapArray(C.self, modelKey: modelKey, delimiter: delimiter) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /LocalLib/LWNetwork/Classes/Cache/Single+Cache.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Single+Cache.swift 3 | // LWNetwork 4 | // 5 | // Created by luowen on 2021/10/9. 6 | // 7 | 8 | import RxSwift 9 | import Moya 10 | 11 | extension ObservableType where Element == Moya.Response { 12 | 13 | public func storeCachedResponse(for target: Target) 14 | -> Observable 15 | where Target: SugarTargetType, Target: Cacheable, Target.ResponseType == Element { 16 | return map { response -> Element in 17 | if target.allowsStorage(response) { 18 | try? target.storeCachedResponse(response) 19 | } 20 | return response 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /LocalLib/LWNetwork/Classes/Cache/Storable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Storable.swift 3 | // LWNetwork 4 | // 5 | // Created by luowen on 2021/10/9. 6 | // 7 | 8 | public protocol StoringKey { 9 | var stringValue: String { get } 10 | } 11 | 12 | public protocol Storable { 13 | 14 | associatedtype ResponseType 15 | 16 | /// 是否允许缓存,建议只缓存可以被成功解析的数据 17 | var allowsStorage: (ResponseType) -> Bool { get } 18 | 19 | /// 读取缓存的响应数据 20 | /// 21 | /// - Parameter key: 缓存的键 22 | /// - Returns: 缓存的响应数据 23 | /// - Throws: 读取缓存可能产生的错误 24 | func cachedResponse(for key: StoringKey) throws -> ResponseType 25 | 26 | /// 存储缓存的响应数据 27 | /// 28 | /// - Parameters: 29 | /// - cachedResponse: 缓存的响应数据 30 | /// - key: 缓存的键 31 | /// - Throws: 存储缓存可能产生的错误 32 | func storeCachedResponse(_ cachedResponse: ResponseType, for key: StoringKey) throws 33 | 34 | /// 移除缓存的响应数据 35 | /// 36 | /// - Parameter key: 缓存的键 37 | /// - Throws: 移除缓存可能产生的错误 38 | func removeCachedResponse(for key: StoringKey) throws 39 | 40 | /// 移除所有的缓存数据 41 | /// 42 | /// - Throws: 移除缓存可能产生的错误 43 | func removeAllCachedResponses() throws 44 | } 45 | -------------------------------------------------------------------------------- /LocalLib/LWNetwork/Classes/Cache/SugarTargetType+Cache.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TargetType+Cache.swift 3 | // LWNetwork 4 | // 5 | // Created by luowen on 2021/10/9. 6 | // 7 | 8 | import Moya 9 | import RxSwift 10 | import ObjectMapper 11 | 12 | public typealias CachingKey = StoringKey 13 | 14 | public typealias Cacheable = Storable & StoringKey & Expirable 15 | 16 | public extension Expirable { 17 | 18 | var expiry: Expiry { 19 | return .never 20 | } 21 | } 22 | 23 | public extension SugarTargetType where Self: Cacheable, Self.ResponseType == Moya.Response { 24 | 25 | /// 获取缓存对象 26 | func onCacheObject( 27 | _ type: C.Type, 28 | modelKey: String? = nil, 29 | delimiter: String = ".", 30 | closure: (C) -> Void) 31 | -> OnCache { 32 | if let response = try? cachedResponse(), 33 | let object = try? response.mapObject(type, modelKey: modelKey, delimiter: delimiter) { 34 | closure(object) 35 | } 36 | return OnCache(target: self, modelKey: modelKey, delimiter: delimiter) 37 | } 38 | 39 | /// 获取缓存对象数组 40 | func onCacheArray( 41 | _ type: C.Type, 42 | modelKey: String? = nil, 43 | delimiter: String = ".", 44 | closure: ([C]) -> Void) 45 | -> OnCache { 46 | if let response = try? cachedResponse(), 47 | let objects = try? response.mapArray(type, modelKey: modelKey, delimiter: delimiter) { 48 | closure(objects) 49 | } 50 | return OnCache(target: self, modelKey: modelKey, delimiter: delimiter) 51 | } 52 | } 53 | 54 | public extension SugarTargetType where Self: Cacheable, Self.ResponseType == Moya.Response { 55 | 56 | var cache: Observable { 57 | return Observable.just(self) 58 | } 59 | 60 | func cachedResponse() throws -> Moya.Response { 61 | let expiry = try self.expiry(for: self) 62 | 63 | guard expiry.isExpired else { 64 | let response = try cachedResponse(for: self) 65 | return Response(statusCode: response.statusCode, data: response.data) 66 | } 67 | 68 | throw Expiry.Error.expired(expiry.date) 69 | } 70 | 71 | func storeCachedResponse(_ cachedResponse: Moya.Response) throws { 72 | try storeCachedResponse(cachedResponse, for: self) 73 | 74 | update(expiry: expiry, for: self) 75 | } 76 | 77 | func removeCachedResponse() throws { 78 | try removeCachedResponse(for: self) 79 | 80 | removeExpiry(for: self) 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /LocalLib/LWNetwork/Classes/Core/ActivityIndictor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ActivityIndictor.swift 3 | // LWNetwork 4 | // 5 | // Created by luowen on 2021/10/8. 6 | // 7 | 8 | import RxSwift 9 | import RxCocoa 10 | 11 | public class ActivityIndicator: SharedSequenceConvertibleType { 12 | public typealias Element = Bool 13 | public typealias SharingStrategy = DriverSharingStrategy 14 | 15 | private let _lock = NSRecursiveLock() 16 | private let _behavior = BehaviorRelay(value: false) 17 | private let _loading: SharedSequence 18 | 19 | public init() { 20 | _loading = _behavior.asDriver() 21 | .distinctUntilChanged() 22 | } 23 | 24 | fileprivate func trackActivityOfObservable(_ source: O) -> Observable { 25 | return source.asObservable() 26 | .do(onNext: { _ in 27 | self.sendStopLoading() 28 | }, onError: { _ in 29 | self.sendStopLoading() 30 | }, onCompleted: { 31 | self.sendStopLoading() 32 | }, onSubscribe: subscribed) 33 | } 34 | 35 | private func subscribed() { 36 | _lock.lock() 37 | _behavior.accept(true) 38 | _lock.unlock() 39 | } 40 | 41 | private func sendStopLoading() { 42 | _lock.lock() 43 | _behavior.accept(false) 44 | _lock.unlock() 45 | } 46 | 47 | public func asSharedSequence() -> SharedSequence { 48 | return _loading 49 | } 50 | } 51 | 52 | extension ObservableConvertibleType { 53 | public func trackActivity(_ activityIndicator: ActivityIndicator) -> Observable { 54 | return activityIndicator.trackActivityOfObservable(self) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /LocalLib/LWNetwork/Classes/Core/Configuration.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Configuration.swift 3 | // 4 | // 5 | // Created by luowen on 2020/9/15. 6 | // Copyright © 2020 luowen. All rights reserved. 7 | // 8 | 9 | /* 10 | 网络配置 支持统一配置header以及公共参数 设置统一的超时时间 11 | 12 | DefaultMapperKeyValue 配置接口返回的参数key 13 | 14 | */ 15 | 16 | import Moya 17 | 18 | extension Network { 19 | 20 | public class Configuration { 21 | 22 | public static let shared = Configuration() 23 | 24 | public var addingHeaders: (SugarTargetType) -> [String: String] = { _ in [:] } 25 | 26 | public var replacingTask: (SugarTargetType) -> Task = { $0.task } 27 | 28 | public var timeoutInterval: TimeInterval = 20 29 | 30 | public var plugins: [PluginType] = [NetworkLoggerPlugin()] 31 | 32 | public var mapperKeyValue: MapperKeyValueType = DefaultMapperKeyValue() 33 | 34 | public init() {} 35 | } 36 | } 37 | 38 | public protocol MapperKeyValueType { 39 | 40 | /// 请求成功时状态码对应的值 41 | var successValues: [String] { get set } 42 | /// 状态码对应的键 43 | var statusCodeKeys: [String] { get set } 44 | /// 请求后的提示语对应的键 45 | var msgStrKeys: [String] { get set } 46 | /// 请求后的主要模型数据的键 47 | var modelKeys: [String] { get set } 48 | } 49 | 50 | public struct DefaultMapperKeyValue: MapperKeyValueType { 51 | public var successValues: [String] = ["0"] 52 | public var statusCodeKeys: [String] = ["code"] 53 | public var msgStrKeys: [String] = ["message"] 54 | public var modelKeys: [String] = ["data"] 55 | } 56 | -------------------------------------------------------------------------------- /LocalLib/LWNetwork/Classes/Core/Network.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Network.swift 3 | // 4 | // 5 | // Created by 罗文 on 2020/9/6. 6 | // Copyright © 2020 luowen. All rights reserved. 7 | // 8 | 9 | import Moya 10 | 11 | public class Network { 12 | 13 | public static let shared = Network() 14 | 15 | public var timeoutInterval: TimeInterval? 16 | 17 | public let provider: MoyaSugarProvider 18 | 19 | public init() { 20 | provider = MoyaSugarProvider(configuration: .shared) 21 | } 22 | } 23 | 24 | public extension MoyaSugarProvider { 25 | 26 | convenience init(configuration: Network.Configuration) { 27 | 28 | let endpointClosure = { (target: Target) -> Endpoint in 29 | return MoyaSugarProvider.defaultEndpointMapping(for: target) 30 | .adding(newHTTPHeaderFields: configuration.addingHeaders(target)) 31 | .replacing(task: configuration.replacingTask(target)) 32 | } 33 | 34 | let requestClosure = { (endpoint: Endpoint, closure: RequestResultClosure) -> Void in 35 | do { 36 | var request = try endpoint.urlRequest() 37 | request.timeoutInterval = Network.shared.timeoutInterval ?? configuration.timeoutInterval 38 | // 在实际开发中不需要 抓包的参数已经转义了 而moya内部会再转义一遍 所以需要去掉moya的转义 39 | request.url = URL(string: request.url?.absoluteString.removingPercentEncoding ?? "") 40 | closure(.success(request)) 41 | } catch MoyaError.requestMapping(let url) { 42 | closure(.failure(.requestMapping(url))) 43 | } catch MoyaError.parameterEncoding(let error) { 44 | closure(.failure(.parameterEncoding(error))) 45 | } catch { 46 | closure(.failure(.underlying(error, nil))) 47 | } 48 | } 49 | 50 | self.init( 51 | endpointClosure: endpointClosure, 52 | requestClosure: requestClosure, 53 | plugins: configuration.plugins 54 | ) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /LocalLib/LWNetwork/Classes/Core/NetworkError.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NetworkError.swift 3 | // LWNetwork 4 | // 5 | // Created by luowen on 2021/10/8. 6 | // 7 | 8 | import Foundation 9 | import Moya 10 | 11 | public enum NetworkError: Swift.Error { 12 | 13 | /// 连接异常 14 | /// 15 | /// - networkException: 网络异常 16 | /// - invalidURL: 请求地址异常 17 | public enum RequestException { 18 | case networkException(Swift.Error?) 19 | case invalidURL(baseURL: String, path: String) 20 | } 21 | 22 | /// 响应异常 23 | /// 24 | /// - serverException: 服务器异常 500 25 | /// - notFound: 方法不存在 404 26 | /// - unacceptableContentType: ContentType 不被接受 27 | /// - unacceptableStatusCode: 响应状态异常 28 | public enum ResponseException { 29 | case serverException 30 | case notFound 31 | case unacceptableContentType(acceptableContentTypes: [String], responseContentType: String) 32 | case unacceptableStatusCode(code: Int, response: Moya.Response) 33 | } 34 | 35 | /// 序列化异常 36 | /// 37 | /// - dataNotFound: data缺失 38 | /// - jsonSerializationFailed: JSON序列化异常 39 | /// - objectFailed: 对象转换失败 40 | public enum ResponseSerializationException { 41 | case dataNotFound 42 | case jsonSerializationFailed(Error?) 43 | case objectFailed 44 | } 45 | 46 | /// 执行异常 47 | /// 48 | /// - executeFail: 执行结果异常,操作失败 49 | /// - unlegal: 执行结果状态吗不合理 50 | public enum ExecuteException { 51 | case executeFail(msg: String?, response: [String: Any]) 52 | case unlegal 53 | } 54 | case requestException(_ exception: RequestException) 55 | case responseException(_ exception: ResponseException) 56 | case responseSerializationException(_ exception: ResponseSerializationException) 57 | case executeException(_ exception: ExecuteException) 58 | } 59 | -------------------------------------------------------------------------------- /LocalLib/LWNetwork/Classes/Core/NetworkLoggerPlugin.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NetworkLoggerPlugin.swift 3 | // LWNetwork 4 | // 5 | // Created by luowen on 2021/10/9. 6 | // 7 | 8 | import Moya 9 | 10 | final class NetworkLoggerPlugin: PluginType { 11 | 12 | func didReceive(_ result: Result, target: TargetType) { 13 | switch result { 14 | case .success(let response): 15 | var items: [String] = [] 16 | items.append(" ┌─────────────────────────────────────────────────────────────────────────\n") 17 | items.append(" ├┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄🍎网络请求完成🍎┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄\n") 18 | items.append(" ├ 🍎请求类型: \(response.request?.httpMethod ?? "无") \n") 19 | items.append(" ├ 🍎请求地址: \((response.request?.url?.absoluteString ?? "")) \n") 20 | items.append(" ├ 🍎响应状态码: \(response.statusCode) \n") 21 | items.append(" ├ 🍎请求头部: \(response.request?.allHTTPHeaderFields ?? [:]) \n") 22 | items.append(" ├ 🍎请求参数: \(response.request?.httpBody?.stringValue ?? "无") \n") 23 | items.append(" ├ 🍎服务器返回数据: \((try? response.mapString()) ?? "无") \n") 24 | items.append(" └──────────────────────────────────────────────────────────────────────── \n") 25 | outputItems(items) 26 | case .failure(let error): 27 | var items: [String] = [] 28 | items.append(" ┌─────────────────────────────────────────────────────────────────────────\n") 29 | items.append(" ├┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄🍎网络请求完成🍎┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄\n") 30 | items.append(" ├ 🍎请求类型: \(target.method) \n") 31 | items.append(" ├ 🍎请求地址: \(target.baseURL.absoluteString + target.path) \n") 32 | items.append(" ├ 🍎请求头部: \(target.headers ?? [:]) \n") 33 | items.append(" ├ 🍎错误信息: \(error.localizedDescription) \n") 34 | items.append(" └──────────────────────────────────────────────────────────────────────── \n") 35 | outputItems(items) 36 | } 37 | } 38 | 39 | private func outputItems(_ items: [String]) { 40 | #if DEBUG 41 | print(items.joined(separator: "")) 42 | #endif 43 | } 44 | 45 | } 46 | 47 | extension Data { 48 | var stringValue: String { 49 | return String(data: self, encoding: .utf8) ?? "" 50 | } 51 | } 52 | 53 | extension Dictionary { 54 | var stringValue: String { 55 | guard let data = try? JSONSerialization.data(withJSONObject: self, options: .fragmentsAllowed) 56 | else { return "" } 57 | return data.stringValue 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /LocalLib/LWNetwork/Classes/Core/NetworkReachabilityManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NetworkReachabilityManager.swift 3 | // LWNetwork 4 | // 5 | // Created by luowen on 2021/10/8. 6 | // 7 | 8 | import Alamofire 9 | import RxSwift 10 | import RxRelay 11 | 12 | public enum NetworkReachabilityManager { 13 | 14 | public enum NetworkReachabilityStatus { 15 | case unknow 16 | case notReachable 17 | case ethernetOrWiFi 18 | case cellular 19 | } 20 | 21 | public static var status: NetworkReachabilityStatus { 22 | guard let status = Alamofire.NetworkReachabilityManager.default?.status else { 23 | return .notReachable 24 | } 25 | return transformStatus(status) 26 | } 27 | 28 | public static var connectionObserver: Observable { 29 | return connectionSubject.asObservable() 30 | } 31 | 32 | /// 是否有网 33 | public static var isReachable: Bool { 34 | Alamofire.NetworkReachabilityManager.default?.isReachable ?? false 35 | } 36 | 37 | /// 只有移动网络 38 | public static var isReachableOnCellular: Bool { 39 | status == .cellular 40 | } 41 | 42 | /// 只有WiFI网络 43 | public static var isReachableOnEthernetOrWiFi: Bool { 44 | status == .ethernetOrWiFi 45 | } 46 | 47 | public static func startListening(onQueue queue: DispatchQueue = .main, 48 | onUpdatePerforming listener: ((NetworkReachabilityStatus) -> Void)? = nil ) { 49 | Alamofire.NetworkReachabilityManager.default?.startListening(onQueue: queue, onUpdatePerforming: { (status) in 50 | let netStatus = transformStatus(status) 51 | listener?(netStatus) 52 | connectionSubject.onNext(netStatus) 53 | }) 54 | } 55 | 56 | private static let connectionSubject = PublishSubject() 57 | 58 | private static func transformStatus(_ status: Alamofire.NetworkReachabilityManager.NetworkReachabilityStatus) -> NetworkReachabilityStatus { 59 | 60 | switch status { 61 | case .notReachable: 62 | return .notReachable 63 | case .unknown: 64 | return .unknow 65 | case .reachable(let type): 66 | switch type { 67 | case .cellular: 68 | return .cellular 69 | case .ethernetOrWiFi: 70 | return .ethernetOrWiFi 71 | } 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /LocalLib/LWNetwork/Classes/Core/ObservableType+ObjectMapper.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Response+ObjectMapper.swift 3 | // LWNetwork 4 | // 5 | // Created by luowen on 2021/10/8. 6 | // 7 | 8 | import Moya 9 | import ObjectMapper 10 | import RxSwift 11 | 12 | extension ObservableType where Element == Response { 13 | 14 | /// 转字典 15 | /// - Parameters: 16 | /// - modelKey: 解析的key 空则是全量解析 nil则以全局设置的解析key为准 默认为data 17 | /// - delimiter: 嵌套解析的标识符 比如"data.model" 则是直接取data下的key model 18 | /// - Returns: 字典 19 | public func mapDictionary(modelKey: String? = nil, 20 | delimiter: String = ".") 21 | 22 | -> Observable<[String: Any]> { 23 | 24 | return flatMap { response -> Observable<[String: Any]> in 25 | return Observable.just(try response.mapDictionary(modelKey: modelKey, delimiter: delimiter)) 26 | } 27 | } 28 | 29 | /// 转对象模型 30 | /// - Parameters: 31 | /// - type: 对象模型的类型 32 | /// - modelKey: 解析的key 空则是全量解析 nil则以全局设置的解析key为准 默认为data 33 | /// - delimiter: 嵌套解析的标识符 比如"data.model" 则是直接取data下的key model来转模型 34 | /// - Returns: 模型对象 35 | public func mapObject(_ type: T.Type, 36 | modelKey: String? = nil, 37 | delimiter: String = ".") 38 | -> Observable { 39 | 40 | return flatMap { response -> Observable in 41 | return Observable.just(try response.mapObject(type, modelKey: modelKey, delimiter: delimiter)) 42 | } 43 | } 44 | 45 | /// 转对象数组 46 | /// - Parameters: 47 | /// - type: 模型的类型 48 | /// - modelKey: 解析的key nil则以全局设置的解析key为准 默认为data 49 | /// - delimiter: 嵌套解析的标识符 比如"data.model" 则是直接取data下的key model来转模型 50 | /// - Returns: 模型对象 51 | public func mapArray(_ type: T.Type, 52 | modelKey: String? = nil, 53 | delimiter: String = ".") 54 | -> Observable<[T]> { 55 | 56 | return flatMap { response -> Observable<[T]> in 57 | return Observable.just(try response.mapArray(type, modelKey: modelKey, delimiter: delimiter)) 58 | } 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /LocalLib/LWNetwork/Classes/MoyaSugar/MoyaSugarProvider.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MoyaSugarProvider.swift 3 | // LWNetworking 4 | // 5 | // Created by luowen on 2021/10/8. 6 | // 7 | 8 | import Foundation 9 | import Moya 10 | 11 | /// `MoyaSugarProvider` overrides `parameterEncoding` and `httpHeaderFields` of the 12 | /// `endpointClosure` with `SugarTargetType`. `MoyaSugarProvider` can be used only with 13 | /// `SugarTargetType`. 14 | open class MoyaSugarProvider: MoyaProvider { 15 | 16 | override public init( 17 | endpointClosure: @escaping EndpointClosure = MoyaProvider.defaultEndpointMapping, 18 | requestClosure: @escaping RequestClosure = MoyaProvider.defaultRequestMapping, 19 | stubClosure: @escaping StubClosure = MoyaProvider.neverStub, 20 | callbackQueue: DispatchQueue? = nil, 21 | session: Session = MoyaProvider.defaultAlamofireSession(), 22 | plugins: [PluginType] = [], 23 | trackInflights: Bool = false 24 | ) { 25 | func sugarEndpointClosure(target: Target) -> Endpoint { 26 | let endpoint = endpointClosure(target) 27 | return Endpoint( 28 | url: target.url.absoluteString, 29 | sampleResponseClosure: endpoint.sampleResponseClosure, 30 | method: endpoint.method, 31 | task: endpoint.task, 32 | httpHeaderFields: endpoint.httpHeaderFields 33 | ) 34 | } 35 | super.init( 36 | endpointClosure: sugarEndpointClosure, 37 | requestClosure: requestClosure, 38 | stubClosure: stubClosure, 39 | callbackQueue: callbackQueue, 40 | session: session, 41 | plugins: plugins, 42 | trackInflights: trackInflights 43 | ) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /LocalLib/LWNetwork/Classes/MoyaSugar/MultiSugarTarget.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MultiSugarTarget.swift 3 | // LWNetworking 4 | // 5 | // Created by luowen on 2021/10/8. 6 | // 7 | 8 | import Foundation 9 | import Moya 10 | 11 | public enum MultiSugarTarget: SugarTargetType { 12 | case target(SugarTargetType) 13 | 14 | public init(_ target: SugarTargetType) { 15 | self = .target(target) 16 | } 17 | 18 | public var target: SugarTargetType { 19 | switch self { 20 | case let .target(target): 21 | return target 22 | } 23 | } 24 | 25 | public var baseURL: URL { 26 | return target.baseURL 27 | } 28 | 29 | public var url: URL { 30 | return target.url 31 | } 32 | 33 | public var defaultURL: URL { 34 | return target.defaultURL 35 | } 36 | 37 | public var route: Route { 38 | return target.route 39 | } 40 | 41 | public var parameters: Parameters? { 42 | return target.parameters 43 | } 44 | 45 | public var headers: [String: String]? { 46 | return target.headers 47 | } 48 | 49 | public var task: Task { 50 | return target.task 51 | } 52 | 53 | public var validationType: ValidationType { 54 | return target.validationType 55 | } 56 | 57 | public var sampleData: Data { 58 | return target.sampleData 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /LocalLib/LWNetwork/Classes/MoyaSugar/Parameters.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Parameters.swift 3 | // LWNetworking 4 | // 5 | // Created by luowen on 2021/10/8. 6 | // 7 | 8 | import Alamofire 9 | import Moya 10 | 11 | /// `Parameters` has parameter encoding and values. Use `=>` operator for syntactic sugar. 12 | /// 13 | /// Example: 14 | /// 15 | /// ``` 16 | /// JSONEncoding() => [ 17 | /// "key1": "value1", 18 | /// "key2": "value2", 19 | /// "key3": nil, // will be ignored 20 | /// ] 21 | /// ``` 22 | 23 | public struct Parameters { 24 | public var encoding: Alamofire.ParameterEncoding 25 | public var values: [String: Any] 26 | 27 | public init(encoding: Alamofire.ParameterEncoding, values: [String: Any?]) { 28 | self.encoding = encoding 29 | self.values = filterNil(values) 30 | } 31 | } 32 | 33 | extension Parameters: ExpressibleByDictionaryLiteral { 34 | public init(dictionaryLiteral elements: (String, Any?)...) { 35 | var values: [String: Any?] = [:] 36 | for (key, value) in elements { 37 | values[key] = value 38 | } 39 | self.init(encoding: Alamofire.URLEncoding(), values: values) 40 | } 41 | } 42 | 43 | infix operator => 44 | 45 | public func => (encoding: Alamofire.ParameterEncoding, values: [String: Any?]) -> Parameters { 46 | return Parameters(encoding: encoding, values: values) 47 | } 48 | 49 | /// Returns a new dictinoary by filtering out nil values. 50 | private func filterNil(_ dictionary: [String: Any?]) -> [String: Any] { 51 | var newDictionary: [String: Any] = [:] 52 | for (key, value) in dictionary { 53 | guard let value = value else { continue } 54 | newDictionary[key] = value 55 | } 56 | return newDictionary 57 | } 58 | -------------------------------------------------------------------------------- /LocalLib/LWNetwork/Classes/MoyaSugar/Route.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Route.swift 3 | // LWNetworking 4 | // 5 | // Created by luowen on 2021/10/8. 6 | // 7 | 8 | import Moya 9 | 10 | /// `Route` has HTTP method and URL path. 11 | /// 12 | /// Example: 13 | /// 14 | /// ``` 15 | /// .get("/me") 16 | /// ``` 17 | 18 | public enum Route { 19 | case get(String) 20 | case post(String) 21 | case put(String) 22 | case delete(String) 23 | case options(String) 24 | case head(String) 25 | case patch(String) 26 | case trace(String) 27 | case connect(String) 28 | 29 | public var path: String { 30 | switch self { 31 | case .get(let path): return path 32 | case .post(let path): return path 33 | case .put(let path): return path 34 | case .delete(let path): return path 35 | case .options(let path): return path 36 | case .head(let path): return path 37 | case .patch(let path): return path 38 | case .trace(let path): return path 39 | case .connect(let path): return path 40 | } 41 | } 42 | 43 | public var method: Moya.Method { 44 | switch self { 45 | case .get: return .get 46 | case .post: return .post 47 | case .put: return .put 48 | case .delete: return .delete 49 | case .options: return .options 50 | case .head: return .head 51 | case .patch: return .patch 52 | case .trace: return .trace 53 | case .connect: return .connect 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /LocalLib/LWNetwork/Classes/MoyaSugar/SugarTargetType.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SugarTargetType.swift 3 | // LWNetworking 4 | // 5 | // Created by luowen on 2021/10/8. 6 | // 7 | 8 | import Moya 9 | import Foundation 10 | 11 | public protocol SugarTargetType: TargetType { 12 | 13 | var url: URL { get } 14 | var route: Route { get } 15 | var parameters: Parameters? { get } 16 | } 17 | 18 | extension SugarTargetType { 19 | 20 | public var url: URL { defaultURL } 21 | 22 | public var defaultURL: URL { 23 | return path.isEmpty ? baseURL : baseURL.appendingPathComponent(self.path) 24 | } 25 | 26 | public var path: String { route.path } 27 | 28 | public var method: Moya.Method { route.method } 29 | 30 | public var task: Task { 31 | guard let parameters = parameters else { return .requestPlain } 32 | return .requestParameters(parameters: parameters.values, encoding: parameters.encoding) 33 | } 34 | 35 | public var sampleData: Data { Data() } 36 | 37 | public var headers: [String : String]? { nil } 38 | } 39 | -------------------------------------------------------------------------------- /LocalLib/LWNetwork/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright © 2018 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /LocalLib/LWNetwork/LWNetwork.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # Be sure to run `pod spec lint LWNetworking.podspec' to ensure this is a 3 | # valid spec and to remove all comments including this before submitting the spec. 4 | # 5 | # To learn more about Podspec attributes see https://guides.cocoapods.org/syntax/podspec.html 6 | # To see working Podspecs in the CocoaPods repo see https://github.com/CocoaPods/Specs/ 7 | # 8 | 9 | Pod::Spec.new do |spec| 10 | 11 | spec.name = "LWNetwork" 12 | spec.version = "1.0.0" 13 | spec.summary = "网络库" 14 | spec.description = "基于Moya与RxSwift的网络组件库" 15 | 16 | spec.homepage = "https://www.luowen.cn" 17 | 18 | spec.license = { :type => 'MIT', :file => 'LICENSE' } 19 | 20 | spec.author = { "luowen" => "luowen@dushu.io" } 21 | spec.source = { :git => 'git@gitlab.dushuclub.io:swift/LWNetwork.git', :tag => "#{spec.version}" } 22 | 23 | spec.platform = :ios, "11.0" 24 | spec.swift_version = '5.0' 25 | 26 | spec.dependency 'RxSwift' 27 | spec.dependency 'RxCocoa' 28 | spec.dependency 'ObjectMapper' 29 | spec.dependency 'Moya' 30 | 31 | spec.subspec 'Core' do |s| 32 | s.dependency 'LWNetwork/MoyaSugar' 33 | s.source_files = 'Classes/Core/**/*.swift' 34 | end 35 | 36 | spec.subspec 'MoyaSugar' do |s| 37 | s.source_files = 'Classes/MoyaSugar/**/*.swift' 38 | end 39 | 40 | spec.subspec 'Cache' do |s| 41 | s.dependency 'LWNetwork/Core' 42 | s.source_files = 'Classes/Cache/**/*.swift' 43 | end 44 | 45 | end 46 | -------------------------------------------------------------------------------- /LocalLib/LWNetwork/README.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | source 'https://github.com/CocoaPods/Specs.git' 2 | 3 | platform :ios, '11.0' 4 | inhibit_all_warnings! 5 | 6 | use_frameworks! 7 | 8 | target ‘SwiftBilibili’ do 9 | 10 | 11 | # 业务组件 12 | pod 'BBNetworkEx', :path => './LocalLib/BBNetworkEx' 13 | 14 | # 基础组件 15 | pod 'LWNetwork', :path => './LocalLib/LWNetwork' 16 | pod 'LWExtensionKit', :path => './LocalLib/LWExtensionKit' 17 | 18 | # 三方库组件 19 | pod 'ReactorKit', '~> 2.1.0' # MVVM 20 | pod 'ZRouter', '1.1.1' 21 | pod 'Kingfisher', '~> 5.15.3' # 图片加载 22 | # Rx 23 | pod 'RxCocoa', '~> 5.1.1' 24 | pod 'RxDataSources', '~> 4.0.1' 25 | pod 'RxGesture', '~> 3.0.2' 26 | pod 'RxOptional', '~> 4.1.0' 27 | pod 'RxTheme', '~> 4.1.1' 28 | # pod 'RealmSwift', '~> 10.1.3' 29 | # pod 'Realm' 30 | pod 'SnapKit', '~> 5.0.1' # 布局 31 | pod 'ActiveLabel', '~> 1.1.0' # Label部分内容点击 32 | pod 'JXSegmentedView', '~> 1.2.7' # 分页控制 33 | pod 'FSPagerView', '~> 0.8.3' # 轮播图 34 | pod 'Toast-Swift', '~> 5.0.1' # 提示 35 | pod 'SwiftEntryKit', '~> 1.2.5' # 弹框 36 | pod 'EachNavigationBar', '~> 1.14.4' # 自定义导航栏 37 | pod 'CocoaLumberjack/Swift', '~> 3.6.2' # 日志 38 | 39 | # Misc. 40 | pod 'EmptyKit', '~> 4.2.0' # 空白页 41 | pod 'ESPullToRefresh', '~> 2.9.3' # 刷新 42 | pod 'Then', '~> 2.7.0' # 语法糖 43 | pod 'URLNavigator', '~> 2.3.0' # 路由 44 | pod 'SwiftyImage', '~> 1.5.2' # 图片 45 | pod 'SwiftDate', '~> 6.1.0' 46 | pod 'SwiftyUserDefaults', '~> 5.0.0' 47 | pod 'Dollar', '~> 9.0.0' # 数组和字典的处理 48 | pod 'BonMot', '~> 5.6.0' # attributedString的简化 49 | pod 'Tiercel', '~> 3.2.0' # 下载 50 | pod 'dsBridge', '~> 3.0.6' # 原生与H5交互 51 | pod 'ObjectMapper', '~> 4.2.0' # 字典转模型 52 | pod 'Moya/RxSwift', '~> 14.0.0' # 网络库 53 | pod 'SwiftLint', '~> 0.40.3' 54 | 55 | end 56 | -------------------------------------------------------------------------------- /Resource/Assets.xcassets/AppIcon.appiconset/AppIcon29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luowen1991/SwiftBilibili/fcf130b78be1e4b844a5e26df40e862a02bb986c/Resource/Assets.xcassets/AppIcon.appiconset/AppIcon29x29@2x.png -------------------------------------------------------------------------------- /Resource/Assets.xcassets/AppIcon.appiconset/AppIcon29x29@2x~ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luowen1991/SwiftBilibili/fcf130b78be1e4b844a5e26df40e862a02bb986c/Resource/Assets.xcassets/AppIcon.appiconset/AppIcon29x29@2x~ipad.png -------------------------------------------------------------------------------- /Resource/Assets.xcassets/AppIcon.appiconset/AppIcon29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luowen1991/SwiftBilibili/fcf130b78be1e4b844a5e26df40e862a02bb986c/Resource/Assets.xcassets/AppIcon.appiconset/AppIcon29x29@3x.png -------------------------------------------------------------------------------- /Resource/Assets.xcassets/AppIcon.appiconset/AppIcon29x29~ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luowen1991/SwiftBilibili/fcf130b78be1e4b844a5e26df40e862a02bb986c/Resource/Assets.xcassets/AppIcon.appiconset/AppIcon29x29~ipad.png -------------------------------------------------------------------------------- /Resource/Assets.xcassets/AppIcon.appiconset/AppIcon40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luowen1991/SwiftBilibili/fcf130b78be1e4b844a5e26df40e862a02bb986c/Resource/Assets.xcassets/AppIcon.appiconset/AppIcon40x40@2x.png -------------------------------------------------------------------------------- /Resource/Assets.xcassets/AppIcon.appiconset/AppIcon40x40@2x~ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luowen1991/SwiftBilibili/fcf130b78be1e4b844a5e26df40e862a02bb986c/Resource/Assets.xcassets/AppIcon.appiconset/AppIcon40x40@2x~ipad.png -------------------------------------------------------------------------------- /Resource/Assets.xcassets/AppIcon.appiconset/AppIcon40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luowen1991/SwiftBilibili/fcf130b78be1e4b844a5e26df40e862a02bb986c/Resource/Assets.xcassets/AppIcon.appiconset/AppIcon40x40@3x.png -------------------------------------------------------------------------------- /Resource/Assets.xcassets/AppIcon.appiconset/AppIcon40x40~ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luowen1991/SwiftBilibili/fcf130b78be1e4b844a5e26df40e862a02bb986c/Resource/Assets.xcassets/AppIcon.appiconset/AppIcon40x40~ipad.png -------------------------------------------------------------------------------- /Resource/Assets.xcassets/AppIcon.appiconset/AppIcon60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luowen1991/SwiftBilibili/fcf130b78be1e4b844a5e26df40e862a02bb986c/Resource/Assets.xcassets/AppIcon.appiconset/AppIcon60x60@2x.png -------------------------------------------------------------------------------- /Resource/Assets.xcassets/AppIcon.appiconset/AppIcon60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luowen1991/SwiftBilibili/fcf130b78be1e4b844a5e26df40e862a02bb986c/Resource/Assets.xcassets/AppIcon.appiconset/AppIcon60x60@3x.png -------------------------------------------------------------------------------- /Resource/Assets.xcassets/AppIcon.appiconset/AppIcon76x76@2x~ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luowen1991/SwiftBilibili/fcf130b78be1e4b844a5e26df40e862a02bb986c/Resource/Assets.xcassets/AppIcon.appiconset/AppIcon76x76@2x~ipad.png -------------------------------------------------------------------------------- /Resource/Assets.xcassets/AppIcon.appiconset/AppIcon76x76~ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luowen1991/SwiftBilibili/fcf130b78be1e4b844a5e26df40e862a02bb986c/Resource/Assets.xcassets/AppIcon.appiconset/AppIcon76x76~ipad.png -------------------------------------------------------------------------------- /Resource/Assets.xcassets/AppIcon.appiconset/AppIcon83.5x83.5@2x~ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luowen1991/SwiftBilibili/fcf130b78be1e4b844a5e26df40e862a02bb986c/Resource/Assets.xcassets/AppIcon.appiconset/AppIcon83.5x83.5@2x~ipad.png -------------------------------------------------------------------------------- /Resource/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "2x", 6 | "size" : "20x20" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "scale" : "3x", 11 | "size" : "20x20" 12 | }, 13 | { 14 | "filename" : "AppIcon29x29@2x.png", 15 | "idiom" : "iphone", 16 | "scale" : "2x", 17 | "size" : "29x29" 18 | }, 19 | { 20 | "filename" : "AppIcon29x29@3x.png", 21 | "idiom" : "iphone", 22 | "scale" : "3x", 23 | "size" : "29x29" 24 | }, 25 | { 26 | "filename" : "AppIcon40x40@2x.png", 27 | "idiom" : "iphone", 28 | "scale" : "2x", 29 | "size" : "40x40" 30 | }, 31 | { 32 | "filename" : "AppIcon40x40@3x.png", 33 | "idiom" : "iphone", 34 | "scale" : "3x", 35 | "size" : "40x40" 36 | }, 37 | { 38 | "filename" : "AppIcon60x60@2x.png", 39 | "idiom" : "iphone", 40 | "scale" : "2x", 41 | "size" : "60x60" 42 | }, 43 | { 44 | "filename" : "AppIcon60x60@3x.png", 45 | "idiom" : "iphone", 46 | "scale" : "3x", 47 | "size" : "60x60" 48 | }, 49 | { 50 | "idiom" : "ipad", 51 | "scale" : "1x", 52 | "size" : "20x20" 53 | }, 54 | { 55 | "idiom" : "ipad", 56 | "scale" : "2x", 57 | "size" : "20x20" 58 | }, 59 | { 60 | "filename" : "AppIcon29x29~ipad.png", 61 | "idiom" : "ipad", 62 | "scale" : "1x", 63 | "size" : "29x29" 64 | }, 65 | { 66 | "filename" : "AppIcon29x29@2x~ipad.png", 67 | "idiom" : "ipad", 68 | "scale" : "2x", 69 | "size" : "29x29" 70 | }, 71 | { 72 | "filename" : "AppIcon40x40~ipad.png", 73 | "idiom" : "ipad", 74 | "scale" : "1x", 75 | "size" : "40x40" 76 | }, 77 | { 78 | "filename" : "AppIcon40x40@2x~ipad.png", 79 | "idiom" : "ipad", 80 | "scale" : "2x", 81 | "size" : "40x40" 82 | }, 83 | { 84 | "filename" : "AppIcon76x76~ipad.png", 85 | "idiom" : "ipad", 86 | "scale" : "1x", 87 | "size" : "76x76" 88 | }, 89 | { 90 | "filename" : "AppIcon76x76@2x~ipad.png", 91 | "idiom" : "ipad", 92 | "scale" : "2x", 93 | "size" : "76x76" 94 | }, 95 | { 96 | "filename" : "AppIcon83.5x83.5@2x~ipad.png", 97 | "idiom" : "ipad", 98 | "scale" : "2x", 99 | "size" : "83.5x83.5" 100 | }, 101 | { 102 | "idiom" : "ios-marketing", 103 | "scale" : "1x", 104 | "size" : "1024x1024" 105 | } 106 | ], 107 | "info" : { 108 | "author" : "xcode", 109 | "version" : 1 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /Resource/Assets.xcassets/Common/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Resource/Assets.xcassets/Common/arrow_right_white_24x24_.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "arrow_right_white_24x24_@2x.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "arrow_right_white_24x24_@3x.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Resource/Assets.xcassets/Common/arrow_right_white_24x24_.imageset/arrow_right_white_24x24_@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luowen1991/SwiftBilibili/fcf130b78be1e4b844a5e26df40e862a02bb986c/Resource/Assets.xcassets/Common/arrow_right_white_24x24_.imageset/arrow_right_white_24x24_@2x.png -------------------------------------------------------------------------------- /Resource/Assets.xcassets/Common/arrow_right_white_24x24_.imageset/arrow_right_white_24x24_@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luowen1991/SwiftBilibili/fcf130b78be1e4b844a5e26df40e862a02bb986c/Resource/Assets.xcassets/Common/arrow_right_white_24x24_.imageset/arrow_right_white_24x24_@3x.png -------------------------------------------------------------------------------- /Resource/Assets.xcassets/Common/column_back_white_8x14_.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "column_back_white_8x14_@2x.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "column_back_white_8x14_@3x.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Resource/Assets.xcassets/Common/column_back_white_8x14_.imageset/column_back_white_8x14_@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luowen1991/SwiftBilibili/fcf130b78be1e4b844a5e26df40e862a02bb986c/Resource/Assets.xcassets/Common/column_back_white_8x14_.imageset/column_back_white_8x14_@2x.png -------------------------------------------------------------------------------- /Resource/Assets.xcassets/Common/column_back_white_8x14_.imageset/column_back_white_8x14_@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luowen1991/SwiftBilibili/fcf130b78be1e4b844a5e26df40e862a02bb986c/Resource/Assets.xcassets/Common/column_back_white_8x14_.imageset/column_back_white_8x14_@3x.png -------------------------------------------------------------------------------- /Resource/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Resource/Assets.xcassets/Empty/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Resource/Assets.xcassets/Empty/empty_coupon.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "empty_coupon@2x.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "empty_coupon@3x.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Resource/Assets.xcassets/Empty/empty_coupon.imageset/empty_coupon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luowen1991/SwiftBilibili/fcf130b78be1e4b844a5e26df40e862a02bb986c/Resource/Assets.xcassets/Empty/empty_coupon.imageset/empty_coupon@2x.png -------------------------------------------------------------------------------- /Resource/Assets.xcassets/Empty/empty_coupon.imageset/empty_coupon@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luowen1991/SwiftBilibili/fcf130b78be1e4b844a5e26df40e862a02bb986c/Resource/Assets.xcassets/Empty/empty_coupon.imageset/empty_coupon@3x.png -------------------------------------------------------------------------------- /Resource/Assets.xcassets/Home/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Resource/Assets.xcassets/Home/top_btn_game_centerhome_icon_22x22_.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "top_btn_game_centerhome_icon_22x22_@2x.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "top_btn_game_centerhome_icon_22x22_@3x.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Resource/Assets.xcassets/Home/top_btn_game_centerhome_icon_22x22_.imageset/top_btn_game_centerhome_icon_22x22_@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luowen1991/SwiftBilibili/fcf130b78be1e4b844a5e26df40e862a02bb986c/Resource/Assets.xcassets/Home/top_btn_game_centerhome_icon_22x22_.imageset/top_btn_game_centerhome_icon_22x22_@2x.png -------------------------------------------------------------------------------- /Resource/Assets.xcassets/Home/top_btn_game_centerhome_icon_22x22_.imageset/top_btn_game_centerhome_icon_22x22_@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luowen1991/SwiftBilibili/fcf130b78be1e4b844a5e26df40e862a02bb986c/Resource/Assets.xcassets/Home/top_btn_game_centerhome_icon_22x22_.imageset/top_btn_game_centerhome_icon_22x22_@3x.png -------------------------------------------------------------------------------- /Resource/Assets.xcassets/Home/top_btn_linkim_home_icon_22x22_.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "top_btn_linkim_home_icon_22x22_@2x.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "top_btn_linkim_home_icon_22x22_@3x.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Resource/Assets.xcassets/Home/top_btn_linkim_home_icon_22x22_.imageset/top_btn_linkim_home_icon_22x22_@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luowen1991/SwiftBilibili/fcf130b78be1e4b844a5e26df40e862a02bb986c/Resource/Assets.xcassets/Home/top_btn_linkim_home_icon_22x22_.imageset/top_btn_linkim_home_icon_22x22_@2x.png -------------------------------------------------------------------------------- /Resource/Assets.xcassets/Home/top_btn_linkim_home_icon_22x22_.imageset/top_btn_linkim_home_icon_22x22_@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luowen1991/SwiftBilibili/fcf130b78be1e4b844a5e26df40e862a02bb986c/Resource/Assets.xcassets/Home/top_btn_linkim_home_icon_22x22_.imageset/top_btn_linkim_home_icon_22x22_@3x.png -------------------------------------------------------------------------------- /Resource/Assets.xcassets/Launch/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Resource/Assets.xcassets/Launch/launcher_image_2020_0709.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "launcher_image_2020_0709@2x.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "launcher_image_2020_0709@3x.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Resource/Assets.xcassets/Launch/launcher_image_2020_0709.imageset/launcher_image_2020_0709@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luowen1991/SwiftBilibili/fcf130b78be1e4b844a5e26df40e862a02bb986c/Resource/Assets.xcassets/Launch/launcher_image_2020_0709.imageset/launcher_image_2020_0709@2x.png -------------------------------------------------------------------------------- /Resource/Assets.xcassets/Launch/launcher_image_2020_0709.imageset/launcher_image_2020_0709@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luowen1991/SwiftBilibili/fcf130b78be1e4b844a5e26df40e862a02bb986c/Resource/Assets.xcassets/Launch/launcher_image_2020_0709.imageset/launcher_image_2020_0709@3x.png -------------------------------------------------------------------------------- /Resource/Assets.xcassets/Launch/launcher_logo_2020.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "launcher_logo_2020@2x.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "launcher_logo_2020@3x.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Resource/Assets.xcassets/Launch/launcher_logo_2020.imageset/launcher_logo_2020@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luowen1991/SwiftBilibili/fcf130b78be1e4b844a5e26df40e862a02bb986c/Resource/Assets.xcassets/Launch/launcher_logo_2020.imageset/launcher_logo_2020@2x.png -------------------------------------------------------------------------------- /Resource/Assets.xcassets/Launch/launcher_logo_2020.imageset/launcher_logo_2020@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luowen1991/SwiftBilibili/fcf130b78be1e4b844a5e26df40e862a02bb986c/Resource/Assets.xcassets/Launch/launcher_logo_2020.imageset/launcher_logo_2020@3x.png -------------------------------------------------------------------------------- /Resource/Assets.xcassets/Launch/player_shadow_logo_40x17_.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "player_shadow_logo_40x17_@2x.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "player_shadow_logo_40x17_@3x.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Resource/Assets.xcassets/Launch/player_shadow_logo_40x17_.imageset/player_shadow_logo_40x17_@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luowen1991/SwiftBilibili/fcf130b78be1e4b844a5e26df40e862a02bb986c/Resource/Assets.xcassets/Launch/player_shadow_logo_40x17_.imageset/player_shadow_logo_40x17_@2x.png -------------------------------------------------------------------------------- /Resource/Assets.xcassets/Launch/player_shadow_logo_40x17_.imageset/player_shadow_logo_40x17_@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luowen1991/SwiftBilibili/fcf130b78be1e4b844a5e26df40e862a02bb986c/Resource/Assets.xcassets/Launch/player_shadow_logo_40x17_.imageset/player_shadow_logo_40x17_@3x.png -------------------------------------------------------------------------------- /Resource/Assets.xcassets/Loading/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Resource/Assets.xcassets/Loading/loading_1_280x158_.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "loading_1_280x158_@2x.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "loading_1_280x158_@3x.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Resource/Assets.xcassets/Loading/loading_1_280x158_.imageset/loading_1_280x158_@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luowen1991/SwiftBilibili/fcf130b78be1e4b844a5e26df40e862a02bb986c/Resource/Assets.xcassets/Loading/loading_1_280x158_.imageset/loading_1_280x158_@2x.png -------------------------------------------------------------------------------- /Resource/Assets.xcassets/Loading/loading_1_280x158_.imageset/loading_1_280x158_@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luowen1991/SwiftBilibili/fcf130b78be1e4b844a5e26df40e862a02bb986c/Resource/Assets.xcassets/Loading/loading_1_280x158_.imageset/loading_1_280x158_@3x.png -------------------------------------------------------------------------------- /Resource/Assets.xcassets/Loading/loading_2_280x158_.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "loading_2_280x158_@2x.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "loading_2_280x158_@3x.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Resource/Assets.xcassets/Loading/loading_2_280x158_.imageset/loading_2_280x158_@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luowen1991/SwiftBilibili/fcf130b78be1e4b844a5e26df40e862a02bb986c/Resource/Assets.xcassets/Loading/loading_2_280x158_.imageset/loading_2_280x158_@2x.png -------------------------------------------------------------------------------- /Resource/Assets.xcassets/Loading/loading_2_280x158_.imageset/loading_2_280x158_@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luowen1991/SwiftBilibili/fcf130b78be1e4b844a5e26df40e862a02bb986c/Resource/Assets.xcassets/Loading/loading_2_280x158_.imageset/loading_2_280x158_@3x.png -------------------------------------------------------------------------------- /Resource/Assets.xcassets/Loading/loading_3_280x158_.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "loading_3_280x158_@2x.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "loading_3_280x158_@3x.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Resource/Assets.xcassets/Loading/loading_3_280x158_.imageset/loading_3_280x158_@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luowen1991/SwiftBilibili/fcf130b78be1e4b844a5e26df40e862a02bb986c/Resource/Assets.xcassets/Loading/loading_3_280x158_.imageset/loading_3_280x158_@2x.png -------------------------------------------------------------------------------- /Resource/Assets.xcassets/Loading/loading_3_280x158_.imageset/loading_3_280x158_@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luowen1991/SwiftBilibili/fcf130b78be1e4b844a5e26df40e862a02bb986c/Resource/Assets.xcassets/Loading/loading_3_280x158_.imageset/loading_3_280x158_@3x.png -------------------------------------------------------------------------------- /Resource/Assets.xcassets/Loading/pull_loading_1_60x60_.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "pull_loading_1_60x60_@2x.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "pull_loading_1_60x60_@3x.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Resource/Assets.xcassets/Loading/pull_loading_1_60x60_.imageset/pull_loading_1_60x60_@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luowen1991/SwiftBilibili/fcf130b78be1e4b844a5e26df40e862a02bb986c/Resource/Assets.xcassets/Loading/pull_loading_1_60x60_.imageset/pull_loading_1_60x60_@2x.png -------------------------------------------------------------------------------- /Resource/Assets.xcassets/Loading/pull_loading_1_60x60_.imageset/pull_loading_1_60x60_@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luowen1991/SwiftBilibili/fcf130b78be1e4b844a5e26df40e862a02bb986c/Resource/Assets.xcassets/Loading/pull_loading_1_60x60_.imageset/pull_loading_1_60x60_@3x.png -------------------------------------------------------------------------------- /Resource/Assets.xcassets/Loading/pull_loading_2_60x60_.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "pull_loading_2_60x60_@2x.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "pull_loading_2_60x60_@3x.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Resource/Assets.xcassets/Loading/pull_loading_2_60x60_.imageset/pull_loading_2_60x60_@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luowen1991/SwiftBilibili/fcf130b78be1e4b844a5e26df40e862a02bb986c/Resource/Assets.xcassets/Loading/pull_loading_2_60x60_.imageset/pull_loading_2_60x60_@2x.png -------------------------------------------------------------------------------- /Resource/Assets.xcassets/Loading/pull_loading_2_60x60_.imageset/pull_loading_2_60x60_@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luowen1991/SwiftBilibili/fcf130b78be1e4b844a5e26df40e862a02bb986c/Resource/Assets.xcassets/Loading/pull_loading_2_60x60_.imageset/pull_loading_2_60x60_@3x.png -------------------------------------------------------------------------------- /Resource/Assets.xcassets/Loading/pull_loading_3_60x60_.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "pull_loading_3_60x60_@2x.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "pull_loading_3_60x60_@3x.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Resource/Assets.xcassets/Loading/pull_loading_3_60x60_.imageset/pull_loading_3_60x60_@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luowen1991/SwiftBilibili/fcf130b78be1e4b844a5e26df40e862a02bb986c/Resource/Assets.xcassets/Loading/pull_loading_3_60x60_.imageset/pull_loading_3_60x60_@2x.png -------------------------------------------------------------------------------- /Resource/Assets.xcassets/Loading/pull_loading_3_60x60_.imageset/pull_loading_3_60x60_@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luowen1991/SwiftBilibili/fcf130b78be1e4b844a5e26df40e862a02bb986c/Resource/Assets.xcassets/Loading/pull_loading_3_60x60_.imageset/pull_loading_3_60x60_@3x.png -------------------------------------------------------------------------------- /Resource/Assets.xcassets/Loading/pull_loading_4_60x60_.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "pull_loading_4_60x60_@2x.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "pull_loading_4_60x60_@3x.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Resource/Assets.xcassets/Loading/pull_loading_4_60x60_.imageset/pull_loading_4_60x60_@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luowen1991/SwiftBilibili/fcf130b78be1e4b844a5e26df40e862a02bb986c/Resource/Assets.xcassets/Loading/pull_loading_4_60x60_.imageset/pull_loading_4_60x60_@2x.png -------------------------------------------------------------------------------- /Resource/Assets.xcassets/Loading/pull_loading_4_60x60_.imageset/pull_loading_4_60x60_@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luowen1991/SwiftBilibili/fcf130b78be1e4b844a5e26df40e862a02bb986c/Resource/Assets.xcassets/Loading/pull_loading_4_60x60_.imageset/pull_loading_4_60x60_@3x.png -------------------------------------------------------------------------------- /Resource/Assets.xcassets/NetError/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Resource/Assets.xcassets/NetError/bblive_2233_loading_error_128x94_.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "bblive_2233_loading_error_128x94_@2x.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "bblive_2233_loading_error_128x94_@3x.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Resource/Assets.xcassets/NetError/bblive_2233_loading_error_128x94_.imageset/bblive_2233_loading_error_128x94_@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luowen1991/SwiftBilibili/fcf130b78be1e4b844a5e26df40e862a02bb986c/Resource/Assets.xcassets/NetError/bblive_2233_loading_error_128x94_.imageset/bblive_2233_loading_error_128x94_@2x.png -------------------------------------------------------------------------------- /Resource/Assets.xcassets/NetError/bblive_2233_loading_error_128x94_.imageset/bblive_2233_loading_error_128x94_@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luowen1991/SwiftBilibili/fcf130b78be1e4b844a5e26df40e862a02bb986c/Resource/Assets.xcassets/NetError/bblive_2233_loading_error_128x94_.imageset/bblive_2233_loading_error_128x94_@3x.png -------------------------------------------------------------------------------- /Resource/Assets.xcassets/NetError/bblivebc_battle_pk_panel_load_error_184x140_.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "bblivebc_battle_pk_panel_load_error_184x140_@2x.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "bblivebc_battle_pk_panel_load_error_184x140_@3x.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Resource/Assets.xcassets/NetError/bblivebc_battle_pk_panel_load_error_184x140_.imageset/bblivebc_battle_pk_panel_load_error_184x140_@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luowen1991/SwiftBilibili/fcf130b78be1e4b844a5e26df40e862a02bb986c/Resource/Assets.xcassets/NetError/bblivebc_battle_pk_panel_load_error_184x140_.imageset/bblivebc_battle_pk_panel_load_error_184x140_@2x.png -------------------------------------------------------------------------------- /Resource/Assets.xcassets/NetError/bblivebc_battle_pk_panel_load_error_184x140_.imageset/bblivebc_battle_pk_panel_load_error_184x140_@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luowen1991/SwiftBilibili/fcf130b78be1e4b844a5e26df40e862a02bb986c/Resource/Assets.xcassets/NetError/bblivebc_battle_pk_panel_load_error_184x140_.imageset/bblivebc_battle_pk_panel_load_error_184x140_@3x.png -------------------------------------------------------------------------------- /Resource/Assets.xcassets/TabBar/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Resource/Assets.xcassets/TabBar/bottom_tabbar_followinghome_normal_22x22_.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "bottom_tabbar_followinghome_normal_22x22_@2x.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "bottom_tabbar_followinghome_normal_22x22_@3x.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Resource/Assets.xcassets/TabBar/bottom_tabbar_followinghome_normal_22x22_.imageset/bottom_tabbar_followinghome_normal_22x22_@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luowen1991/SwiftBilibili/fcf130b78be1e4b844a5e26df40e862a02bb986c/Resource/Assets.xcassets/TabBar/bottom_tabbar_followinghome_normal_22x22_.imageset/bottom_tabbar_followinghome_normal_22x22_@2x.png -------------------------------------------------------------------------------- /Resource/Assets.xcassets/TabBar/bottom_tabbar_followinghome_normal_22x22_.imageset/bottom_tabbar_followinghome_normal_22x22_@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luowen1991/SwiftBilibili/fcf130b78be1e4b844a5e26df40e862a02bb986c/Resource/Assets.xcassets/TabBar/bottom_tabbar_followinghome_normal_22x22_.imageset/bottom_tabbar_followinghome_normal_22x22_@3x.png -------------------------------------------------------------------------------- /Resource/Assets.xcassets/TabBar/bottom_tabbar_followinghome_selected_22x22_.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "bottom_tabbar_followinghome_selected_22x22_@2x.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "bottom_tabbar_followinghome_selected_22x22_@3x.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Resource/Assets.xcassets/TabBar/bottom_tabbar_followinghome_selected_22x22_.imageset/bottom_tabbar_followinghome_selected_22x22_@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luowen1991/SwiftBilibili/fcf130b78be1e4b844a5e26df40e862a02bb986c/Resource/Assets.xcassets/TabBar/bottom_tabbar_followinghome_selected_22x22_.imageset/bottom_tabbar_followinghome_selected_22x22_@2x.png -------------------------------------------------------------------------------- /Resource/Assets.xcassets/TabBar/bottom_tabbar_followinghome_selected_22x22_.imageset/bottom_tabbar_followinghome_selected_22x22_@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luowen1991/SwiftBilibili/fcf130b78be1e4b844a5e26df40e862a02bb986c/Resource/Assets.xcassets/TabBar/bottom_tabbar_followinghome_selected_22x22_.imageset/bottom_tabbar_followinghome_selected_22x22_@3x.png -------------------------------------------------------------------------------- /Resource/Assets.xcassets/TabBar/bottom_tabbar_mainhome_normal_22x22_.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "bottom_tabbar_mainhome_normal_22x22_@2x.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "bottom_tabbar_mainhome_normal_22x22_@3x.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Resource/Assets.xcassets/TabBar/bottom_tabbar_mainhome_normal_22x22_.imageset/bottom_tabbar_mainhome_normal_22x22_@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luowen1991/SwiftBilibili/fcf130b78be1e4b844a5e26df40e862a02bb986c/Resource/Assets.xcassets/TabBar/bottom_tabbar_mainhome_normal_22x22_.imageset/bottom_tabbar_mainhome_normal_22x22_@2x.png -------------------------------------------------------------------------------- /Resource/Assets.xcassets/TabBar/bottom_tabbar_mainhome_normal_22x22_.imageset/bottom_tabbar_mainhome_normal_22x22_@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luowen1991/SwiftBilibili/fcf130b78be1e4b844a5e26df40e862a02bb986c/Resource/Assets.xcassets/TabBar/bottom_tabbar_mainhome_normal_22x22_.imageset/bottom_tabbar_mainhome_normal_22x22_@3x.png -------------------------------------------------------------------------------- /Resource/Assets.xcassets/TabBar/bottom_tabbar_mainhome_selected_22x22_.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "bottom_tabbar_mainhome_selected_22x22_@2x.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "bottom_tabbar_mainhome_selected_22x22_@3x.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Resource/Assets.xcassets/TabBar/bottom_tabbar_mainhome_selected_22x22_.imageset/bottom_tabbar_mainhome_selected_22x22_@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luowen1991/SwiftBilibili/fcf130b78be1e4b844a5e26df40e862a02bb986c/Resource/Assets.xcassets/TabBar/bottom_tabbar_mainhome_selected_22x22_.imageset/bottom_tabbar_mainhome_selected_22x22_@2x.png -------------------------------------------------------------------------------- /Resource/Assets.xcassets/TabBar/bottom_tabbar_mainhome_selected_22x22_.imageset/bottom_tabbar_mainhome_selected_22x22_@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luowen1991/SwiftBilibili/fcf130b78be1e4b844a5e26df40e862a02bb986c/Resource/Assets.xcassets/TabBar/bottom_tabbar_mainhome_selected_22x22_.imageset/bottom_tabbar_mainhome_selected_22x22_@3x.png -------------------------------------------------------------------------------- /Resource/Assets.xcassets/TabBar/bottom_tabbar_mallhome_normal_22x22_.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "bottom_tabbar_mallhome_normal_22x22_@2x.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "bottom_tabbar_mallhome_normal_22x22_@3x.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Resource/Assets.xcassets/TabBar/bottom_tabbar_mallhome_normal_22x22_.imageset/bottom_tabbar_mallhome_normal_22x22_@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luowen1991/SwiftBilibili/fcf130b78be1e4b844a5e26df40e862a02bb986c/Resource/Assets.xcassets/TabBar/bottom_tabbar_mallhome_normal_22x22_.imageset/bottom_tabbar_mallhome_normal_22x22_@2x.png -------------------------------------------------------------------------------- /Resource/Assets.xcassets/TabBar/bottom_tabbar_mallhome_normal_22x22_.imageset/bottom_tabbar_mallhome_normal_22x22_@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luowen1991/SwiftBilibili/fcf130b78be1e4b844a5e26df40e862a02bb986c/Resource/Assets.xcassets/TabBar/bottom_tabbar_mallhome_normal_22x22_.imageset/bottom_tabbar_mallhome_normal_22x22_@3x.png -------------------------------------------------------------------------------- /Resource/Assets.xcassets/TabBar/bottom_tabbar_mallhome_selected_22x22_.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "bottom_tabbar_mallhome_selected_22x22_@2x.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "bottom_tabbar_mallhome_selected_22x22_@3x.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Resource/Assets.xcassets/TabBar/bottom_tabbar_mallhome_selected_22x22_.imageset/bottom_tabbar_mallhome_selected_22x22_@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luowen1991/SwiftBilibili/fcf130b78be1e4b844a5e26df40e862a02bb986c/Resource/Assets.xcassets/TabBar/bottom_tabbar_mallhome_selected_22x22_.imageset/bottom_tabbar_mallhome_selected_22x22_@2x.png -------------------------------------------------------------------------------- /Resource/Assets.xcassets/TabBar/bottom_tabbar_mallhome_selected_22x22_.imageset/bottom_tabbar_mallhome_selected_22x22_@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luowen1991/SwiftBilibili/fcf130b78be1e4b844a5e26df40e862a02bb986c/Resource/Assets.xcassets/TabBar/bottom_tabbar_mallhome_selected_22x22_.imageset/bottom_tabbar_mallhome_selected_22x22_@3x.png -------------------------------------------------------------------------------- /Resource/Assets.xcassets/TabBar/bottom_tabbar_pegasuschannel_normal_22x22_.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "bottom_tabbar_pegasuschannel_normal_22x22_@2x.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "bottom_tabbar_pegasuschannel_normal_22x22_@3x.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Resource/Assets.xcassets/TabBar/bottom_tabbar_pegasuschannel_normal_22x22_.imageset/bottom_tabbar_pegasuschannel_normal_22x22_@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luowen1991/SwiftBilibili/fcf130b78be1e4b844a5e26df40e862a02bb986c/Resource/Assets.xcassets/TabBar/bottom_tabbar_pegasuschannel_normal_22x22_.imageset/bottom_tabbar_pegasuschannel_normal_22x22_@2x.png -------------------------------------------------------------------------------- /Resource/Assets.xcassets/TabBar/bottom_tabbar_pegasuschannel_normal_22x22_.imageset/bottom_tabbar_pegasuschannel_normal_22x22_@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luowen1991/SwiftBilibili/fcf130b78be1e4b844a5e26df40e862a02bb986c/Resource/Assets.xcassets/TabBar/bottom_tabbar_pegasuschannel_normal_22x22_.imageset/bottom_tabbar_pegasuschannel_normal_22x22_@3x.png -------------------------------------------------------------------------------- /Resource/Assets.xcassets/TabBar/bottom_tabbar_pegasuschannel_selected_22x22_.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "bottom_tabbar_pegasuschannel_selected_22x22_@2x.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "bottom_tabbar_pegasuschannel_selected_22x22_@3x.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Resource/Assets.xcassets/TabBar/bottom_tabbar_pegasuschannel_selected_22x22_.imageset/bottom_tabbar_pegasuschannel_selected_22x22_@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luowen1991/SwiftBilibili/fcf130b78be1e4b844a5e26df40e862a02bb986c/Resource/Assets.xcassets/TabBar/bottom_tabbar_pegasuschannel_selected_22x22_.imageset/bottom_tabbar_pegasuschannel_selected_22x22_@2x.png -------------------------------------------------------------------------------- /Resource/Assets.xcassets/TabBar/bottom_tabbar_pegasuschannel_selected_22x22_.imageset/bottom_tabbar_pegasuschannel_selected_22x22_@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luowen1991/SwiftBilibili/fcf130b78be1e4b844a5e26df40e862a02bb986c/Resource/Assets.xcassets/TabBar/bottom_tabbar_pegasuschannel_selected_22x22_.imageset/bottom_tabbar_pegasuschannel_selected_22x22_@3x.png -------------------------------------------------------------------------------- /Resource/Assets.xcassets/TabBar/bottom_tabbar_user_center_normal_22x22_.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "bottom_tabbar_user_center_normal_22x22_@2x.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "bottom_tabbar_user_center_normal_22x22_@3x.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Resource/Assets.xcassets/TabBar/bottom_tabbar_user_center_normal_22x22_.imageset/bottom_tabbar_user_center_normal_22x22_@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luowen1991/SwiftBilibili/fcf130b78be1e4b844a5e26df40e862a02bb986c/Resource/Assets.xcassets/TabBar/bottom_tabbar_user_center_normal_22x22_.imageset/bottom_tabbar_user_center_normal_22x22_@2x.png -------------------------------------------------------------------------------- /Resource/Assets.xcassets/TabBar/bottom_tabbar_user_center_normal_22x22_.imageset/bottom_tabbar_user_center_normal_22x22_@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luowen1991/SwiftBilibili/fcf130b78be1e4b844a5e26df40e862a02bb986c/Resource/Assets.xcassets/TabBar/bottom_tabbar_user_center_normal_22x22_.imageset/bottom_tabbar_user_center_normal_22x22_@3x.png -------------------------------------------------------------------------------- /Resource/Assets.xcassets/TabBar/bottom_tabbar_user_center_selected_22x22_.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "bottom_tabbar_user_center_selected_22x22_@2x.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "bottom_tabbar_user_center_selected_22x22_@3x.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Resource/Assets.xcassets/TabBar/bottom_tabbar_user_center_selected_22x22_.imageset/bottom_tabbar_user_center_selected_22x22_@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luowen1991/SwiftBilibili/fcf130b78be1e4b844a5e26df40e862a02bb986c/Resource/Assets.xcassets/TabBar/bottom_tabbar_user_center_selected_22x22_.imageset/bottom_tabbar_user_center_selected_22x22_@2x.png -------------------------------------------------------------------------------- /Resource/Assets.xcassets/TabBar/bottom_tabbar_user_center_selected_22x22_.imageset/bottom_tabbar_user_center_selected_22x22_@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luowen1991/SwiftBilibili/fcf130b78be1e4b844a5e26df40e862a02bb986c/Resource/Assets.xcassets/TabBar/bottom_tabbar_user_center_selected_22x22_.imageset/bottom_tabbar_user_center_selected_22x22_@3x.png -------------------------------------------------------------------------------- /Resource/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /Resource/Image/launcher_logo_2020@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luowen1991/SwiftBilibili/fcf130b78be1e4b844a5e26df40e862a02bb986c/Resource/Image/launcher_logo_2020@2x.png -------------------------------------------------------------------------------- /Resource/Image/launcher_logo_2020@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luowen1991/SwiftBilibili/fcf130b78be1e4b844a5e26df40e862a02bb986c/Resource/Image/launcher_logo_2020@3x.png -------------------------------------------------------------------------------- /Sources/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // SwiftBilibili 4 | // 5 | // Created by luowen on 2020/9/2. 6 | // Copyright © 2020 luowen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Tiercel 11 | // swiftlint:disable force_cast 12 | let appDelegate = UIApplication.shared.delegate as! AppDelegate 13 | @UIApplicationMain 14 | class AppDelegate: UIResponder, UIApplicationDelegate { 15 | 16 | var window: UIWindow? 17 | var dependency: AppDependency! 18 | var adSessionManager: SessionManager! 19 | 20 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 21 | 22 | self.dependency = self.dependency ?? CompositionRoot.resolve() 23 | self.window = self.dependency.window 24 | self.adSessionManager = self.dependency.adSessionManager 25 | self.dependency.startNetworkStatusNotifier() 26 | self.dependency.loadLocalResource() 27 | self.dependency.setupAppConfig() 28 | self.dependency.setupRootViewController(window!) 29 | 30 | return true 31 | } 32 | 33 | func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: @escaping () -> Void) { 34 | 35 | if adSessionManager.identifier == identifier { 36 | adSessionManager.completionHandler = completionHandler 37 | } 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /Sources/Base/BaseNavigationController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BaseNavigationController.swift 3 | // SwiftBilibili 4 | // 5 | // Created by luowen on 2020/9/4. 6 | // Copyright © 2020 luowen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import EachNavigationBar 11 | 12 | class BaseNavigationController: UINavigationController { 13 | 14 | override var preferredStatusBarStyle: UIStatusBarStyle { 15 | return self.topViewController?.preferredStatusBarStyle ?? .default 16 | } 17 | 18 | override func viewDidLoad() { 19 | super.viewDidLoad() 20 | } 21 | 22 | override func pushViewController(_ viewController: UIViewController, animated: Bool) { 23 | 24 | if children.count >= 1 { 25 | viewController.hidesBottomBarWhenPushed = true 26 | } 27 | super.pushViewController(viewController, animated: animated) 28 | } 29 | 30 | override func viewDidLayoutSubviews() { 31 | super.viewDidLayoutSubviews() 32 | 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Sources/Base/BaseTabBarController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BaseTabBarController.swift 3 | // SwiftBilibili 4 | // 5 | // Created by luowen on 2020/9/4. 6 | // Copyright © 2020 luowen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import EachNavigationBar 11 | 12 | final class BaseTabBarController: UITabBarController { 13 | 14 | fileprivate struct Metric { 15 | static let tabBarHeight = 49.f 16 | } 17 | 18 | override func viewDidLoad() { 19 | super.viewDidLoad() 20 | 21 | tabBar.theme.barTintColor = themed { $0.mainColorModel.ga0S } 22 | 23 | addChildController(HomeMainViewController(), "首页", Image.TabBar.homeNormal, Image.TabBar.homeSelected) 24 | addChildController(ChannelViewController(), "频道", Image.TabBar.channelNormal, Image.TabBar.channelSelected) 25 | addChildController(DynamicViewController(), "动态", Image.TabBar.dynamicNormal, Image.TabBar.dynamicSelected) 26 | addChildController(MemberViewController(), "会员购", Image.TabBar.memberNormal, Image.TabBar.memberSelected) 27 | addChildController(MineViewController(), "我的", Image.TabBar.mineNormal, Image.TabBar.mineSelected) 28 | 29 | // 显示广告 30 | LaunchAdManager.default.display() 31 | 32 | } 33 | 34 | override func viewDidLayoutSubviews() { 35 | super.viewDidLayoutSubviews() 36 | if #available(iOS 11.0, *) { 37 | self.tabBar.height = Metric.tabBarHeight + self.view.safeAreaInsets.bottom 38 | } else { 39 | self.tabBar.height = Metric.tabBarHeight 40 | } 41 | self.tabBar.bottom = self.view.height 42 | } 43 | 44 | private func addChildController(_ child: UIViewController, 45 | _ title: String, 46 | _ normalImage: UIImage?, 47 | _ selectedImage: UIImage?) { 48 | 49 | if #available(iOS 13.0, *) { 50 | tabBar.tintColor = ThemeManager.shared.pinkStyleModel.colors.pi5 51 | } else { 52 | child.tabBarItem.setTitleTextAttributes([.foregroundColor : ThemeManager.shared.pinkStyleModel.colors.pi5], for: .selected) 53 | } 54 | child.tabBarItem.title = title 55 | child.tabBarItem.image = normalImage?.withRenderingMode(.alwaysOriginal) 56 | child.tabBarItem.selectedImage = selectedImage?.withRenderingMode(.alwaysOriginal) 57 | 58 | let navController = BaseNavigationController(rootViewController: child) 59 | navController.navigation.configuration.isEnabled = true 60 | navController.navigation.configuration.isShadowHidden = true 61 | addChild(navController) 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /Sources/Base/BaseView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BaseView.swift 3 | // SwiftBilibili 4 | // 5 | // Created by luowen on 2020/9/24. 6 | // Copyright © 2020 luowen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import RxSwift 11 | 12 | class BaseView: UIView { 13 | 14 | var disposeBag = DisposeBag() 15 | 16 | override init(frame: CGRect) { 17 | super.init(frame: frame) 18 | 19 | setupViews() 20 | setupEvents() 21 | } 22 | 23 | required init?(coder: NSCoder) { 24 | fatalError("init(coder:) has not been implemented") 25 | } 26 | 27 | /// 子类覆写添加子视图 28 | func setupViews() { 29 | fatalError("add subview in this") 30 | } 31 | 32 | func setupEvents() {} 33 | 34 | } 35 | -------------------------------------------------------------------------------- /Sources/Base/BaseViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BaseViewController.swift 3 | // SwiftBilibili 4 | // 5 | // Created by luowen on 2020/9/4. 6 | // Copyright © 2020 luowen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import RxSwift 11 | import EachNavigationBar 12 | 13 | class BaseViewController: UIViewController { 14 | 15 | var disposeBag = DisposeBag() 16 | 17 | // MARK: Properties 18 | lazy private(set) var className: String = { 19 | return type(of: self).description().components(separatedBy: ".").last ?? "" 20 | }() 21 | 22 | private(set) var didSetupConstraints = false 23 | 24 | // MARK: Initializing 25 | init() { 26 | super.init(nibName: nil, bundle: nil) 27 | } 28 | 29 | required convenience init?(coder aDecoder: NSCoder) { 30 | self.init() 31 | } 32 | 33 | deinit { 34 | log.verbose("DEINIT: \(self.className)") 35 | } 36 | 37 | override var preferredStatusBarStyle: UIStatusBarStyle { 38 | return themeService.attrs.isDark ? .lightContent : .default 39 | } 40 | 41 | override func viewDidLoad() { 42 | super.viewDidLoad() 43 | setupUI() 44 | view.setNeedsUpdateConstraints() 45 | setupData() 46 | bindEvent() 47 | setupCommonTheme() 48 | } 49 | 50 | override func updateViewConstraints() { 51 | if !self.didSetupConstraints { 52 | self.setupConstraints() 53 | self.didSetupConstraints = true 54 | } 55 | super.updateViewConstraints() 56 | } 57 | 58 | private func setupCommonTheme() { 59 | 60 | themeService.attrsStream 61 | .subscribe(onNext: {[weak self] (theme) in 62 | guard let self = self else { return } 63 | self.view.backgroundColor = theme.mainColorModel.ga1 64 | self.navigation.bar.titleTextAttributes = [.foregroundColor: theme.mainColorModel.wh0] 65 | self.navigation.bar.barTintColor = theme.mainColorModel.wh0T 66 | if self.navigationController?.viewControllers.count ?? 0 > 1 { 67 | self.navigation.bar.backBarButtonItem = .init(style: .image(Image.Common.whiteBack), tintColor: theme.mainColorModel.ga7T) 68 | } 69 | self.resetTheme(theme: theme) 70 | }) 71 | .disposed(by: disposeBag) 72 | } 73 | 74 | // MARK: 子类覆写 75 | 76 | /// 子类覆写添加视图 77 | func setupUI() { 78 | fatalError("add subview in this") 79 | } 80 | 81 | /// 子类覆写设置视图约束 82 | func setupConstraints() { 83 | // Override point 84 | } 85 | 86 | /// 子类覆写主题改变时设置样式 87 | func resetTheme(theme: Theme) {} 88 | 89 | /// 子类覆写绑定事件 90 | func bindEvent() { 91 | 92 | } 93 | 94 | /// 子类覆写初始化数据 95 | func setupData() {} 96 | } 97 | -------------------------------------------------------------------------------- /Sources/Components/LaunchAd/LaunchAdTitleView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LaunchAdTitleView.swift 3 | // SwiftBilibili 4 | // 5 | // Created by luowen on 2020/10/23. 6 | // Copyright © 2020 luowen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import RxSwift 11 | import RxGesture 12 | 13 | final class LaunchAdTitleView: BaseView { 14 | 15 | var tapObservable: Observable { 16 | return tapSubject.asObserver() 17 | } 18 | 19 | var title: String = "" { 20 | didSet { 21 | titleLabel.text = title 22 | } 23 | } 24 | 25 | private var tapSubject = PublishSubject() 26 | 27 | override init(frame: CGRect) { 28 | super.init(frame: frame) 29 | backgroundColor = UIColor.black.withAlphaComponent(0.8) 30 | self.rx.tapGesture() 31 | .when(.recognized) 32 | .map { _ in () } 33 | .bind(to: tapSubject) 34 | .disposed(by: disposeBag) 35 | } 36 | 37 | required init?(coder: NSCoder) { 38 | fatalError("init(coder:) has not been implemented") 39 | } 40 | 41 | override func setupViews() { 42 | addSubview(arrowImageView) 43 | addSubview(titleLabel) 44 | } 45 | 46 | override func layoutSubviews() { 47 | super.layoutSubviews() 48 | 49 | arrowImageView.snp.makeConstraints { 50 | $0.centerY.equalToSuperview() 51 | $0.right.equalTo(-15) 52 | } 53 | 54 | titleLabel.snp.makeConstraints { 55 | $0.centerY.equalToSuperview() 56 | $0.left.equalTo(15) 57 | } 58 | } 59 | 60 | let titleLabel = UILabel().then { 61 | $0.textColor = .white 62 | $0.font = Font.appFont(ofSize: 15) 63 | } 64 | 65 | let arrowImageView = UIImageView().then { 66 | $0.image = Image.Common.whiteArrow 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /Sources/Components/LaunchAd/LaunchAdVideoView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LaunchAdVideoView.swift 3 | // SwiftBilibili 4 | // 5 | // Created by luowen on 2020/9/27. 6 | // Copyright © 2020 luowen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import AVFoundation 11 | import MediaPlayer 12 | import AVKit 13 | 14 | class LaunchAdVideoView: UIView { 15 | 16 | lazy var videoPlayer: AVPlayerViewController = { 17 | let videoPlayer = AVPlayerViewController() 18 | videoPlayer.showsPlaybackControls = false 19 | videoPlayer.videoGravity = .resizeAspectFill 20 | videoPlayer.view.frame = self.bounds 21 | videoPlayer.view.backgroundColor = .clear 22 | 23 | // 注册通知控制是否循环播放 24 | NotificationCenter.default.addObserver(self, selector: #selector(runLoopTheMovie), name: .AVPlayerItemDidPlayToEndTime, object: nil) 25 | 26 | // 获取音频焦点 27 | try? AVAudioSession.sharedInstance().setCategory(.playback) 28 | try? AVAudioSession.sharedInstance().setActive(true) 29 | 30 | return videoPlayer 31 | }() 32 | 33 | var videoGravity: AVLayerVideoGravity = .resizeAspectFill { 34 | didSet { 35 | videoPlayer.videoGravity = videoGravity 36 | } 37 | } 38 | 39 | var isMuted: Bool = false { 40 | didSet { 41 | videoPlayer.player?.isMuted = isMuted 42 | } 43 | } 44 | 45 | var videoCycleOnce: Bool = false 46 | 47 | var videoURL: URL? { 48 | didSet { 49 | guard let url = videoURL else { 50 | return 51 | } 52 | let movieAsset = AVURLAsset(url: url) 53 | playerItem = AVPlayerItem(asset: movieAsset) 54 | videoPlayer.player = AVPlayer(playerItem: playerItem) 55 | videoPlayer.player?.play() 56 | // 监听播放失败状态 57 | //playerItem?.addObserver(self, forKeyPath: "", options: .new, context: nil) 58 | } 59 | } 60 | 61 | var playerItem: AVPlayerItem? 62 | 63 | override init(frame: CGRect) { 64 | super.init(frame: frame) 65 | 66 | backgroundColor = .clear 67 | addSubview(videoPlayer.view) 68 | } 69 | 70 | required init?(coder: NSCoder) { 71 | fatalError("init(coder:) has not been implemented") 72 | } 73 | 74 | func stop() { 75 | videoPlayer.player?.pause() 76 | videoPlayer.player = nil 77 | try? AVAudioSession.sharedInstance().setActive(false, options: .notifyOthersOnDeactivation) 78 | } 79 | 80 | public override func layoutSubviews() { 81 | super.layoutSubviews() 82 | videoPlayer.view.frame = self.frame 83 | } 84 | 85 | @objc private func runLoopTheMovie(noti: Notification) { 86 | 87 | guard let playerItem = noti.object as? AVPlayerItem else { return } 88 | 89 | if !videoCycleOnce { 90 | playerItem.seek(to: .zero) {[weak self] (_) in 91 | guard let self = self else { return } 92 | self.videoPlayer.player?.play() 93 | } 94 | } 95 | } 96 | 97 | } 98 | -------------------------------------------------------------------------------- /Sources/Components/LaunchAd/LaunchAdViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LaunchAdViewController.swift 3 | // SwiftBilibili 4 | // 5 | // Created by luowen on 2020/9/27. 6 | // Copyright © 2020 luowen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class LaunchAdViewController: UIViewController { 12 | 13 | override var shouldAutorotate: Bool { 14 | return false 15 | } 16 | 17 | override var prefersHomeIndicatorAutoHidden: Bool { 18 | return false 19 | } 20 | 21 | override func viewDidLoad() { 22 | super.viewDidLoad() 23 | 24 | // Do any additional setup after loading the view. 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /Sources/Components/Toast/Toast.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Toast.swift 3 | // SwiftBilibili 4 | // 5 | // Created by luowen on 2020/11/19. 6 | // Copyright © 2020 luowen. All rights reserved. 7 | // 8 | 9 | import Toast_Swift 10 | 11 | struct Toast { 12 | static func show(title: String? = nil, 13 | message: String? = nil, 14 | on: UIView? = nil) { 15 | if let superview = on { 16 | superview.makeToast(message) 17 | } else { 18 | let superview = UIApplication.shared.windows.last 19 | superview?.makeToast(message) 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Sources/Const/Const.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Const.swift 3 | // SwiftBilibili 4 | // 5 | // Created by luowen on 2020/10/23. 6 | // Copyright © 2020 luowen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | struct Const { 12 | 13 | static let splashLogoSize = CGSize(width: 120, height: 60) 14 | static let shadowLogoSize = CGSize(width: 80, height: 40) 15 | static let unLoginAvatarUrl = "http://i0.hdslb.com/bfs/archive/689ed56f5b8b9bd26a90b20c52d464ebc0156185.png" 16 | } 17 | -------------------------------------------------------------------------------- /Sources/Const/Font/Font.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Font.swift 3 | // SwiftBilibili 4 | // 5 | // Created by luowen on 2020/9/24. 6 | // Copyright © 2020 luowen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | enum FontStyle: String { 12 | case `default` = "San Fransico" 13 | case helvetica = "Helvetica" 14 | } 15 | 16 | struct Font { 17 | // app的字体 18 | static func appFont(ofSize fontSize: CGFloat, 19 | style: FontStyle = .default, 20 | weight: UIFont.Weight = .regular) -> UIFont { 21 | switch style { 22 | case .default: 23 | return UIFont.systemFont(ofSize: fontSize.auto(), weight: weight) 24 | case .helvetica: 25 | return UIFont.monospacedDigitSystemFont(ofSize: fontSize.auto(), weight: weight) 26 | } 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /Sources/Const/Image/Image.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Image.swift 3 | // SwiftBilibili 4 | // 5 | // Created by luowen on 2020/9/4. 6 | // Copyright © 2020 luowen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | struct Image { 12 | 13 | struct TabBar { 14 | static let homeNormal = UIImage(named: "bottom_tabbar_mainhome_normal_22x22_") 15 | static let homeSelected = UIImage(named: "bottom_tabbar_mainhome_selected_22x22_") 16 | static let channelNormal = UIImage(named: "bottom_tabbar_pegasuschannel_normal_22x22_") 17 | static let channelSelected = UIImage(named: "bottom_tabbar_pegasuschannel_selected_22x22_") 18 | static let dynamicNormal = UIImage(named: "bottom_tabbar_followinghome_normal_22x22_") 19 | static let dynamicSelected = UIImage(named: "bottom_tabbar_followinghome_selected_22x22_") 20 | static let memberNormal = UIImage(named: "bottom_tabbar_mallhome_normal_22x22_") 21 | static let memberSelected = UIImage(named: "bottom_tabbar_mallhome_selected_22x22_") 22 | static let mineNormal = UIImage(named: "bottom_tabbar_user_center_normal_22x22_") 23 | static let mineSelected = UIImage(named: "bottom_tabbar_user_center_selected_22x22_") 24 | } 25 | 26 | struct Launch { 27 | static let pinkLogo = UIImage(named: "launcher_logo_2020") 28 | static let shadowLogo = UIImage(named: "player_shadow_logo_40x17_") 29 | static let content = UIImage(named: "launcher_image_2020_0709") 30 | } 31 | 32 | struct NetError { 33 | static let `default` = UIImage(named: "bblivebc_battle_pk_panel_load_error_184x140_") 34 | } 35 | 36 | struct Common { 37 | static let whiteBack = UIImage(named: "column_back_white_8x14_") 38 | static let whiteArrow = UIImage(named: "arrow_right_white_24x24_") 39 | } 40 | 41 | struct Home { 42 | static let topGame = UIImage(named: "top_btn_game_centerhome_icon_22x22_") 43 | static let topIM = UIImage(named: "top_btn_linkim_home_icon_22x22_") 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Sources/Const/Screen/Screen.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Screen.swift 3 | // SwiftBilibili 4 | // 5 | // Created by luowen on 2020/9/7. 6 | // Copyright © 2020 luowen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | struct Screen { 12 | 13 | static var isIphoneX: Bool { 14 | return UIApplication.shared.keyWindow!.safeAreaInsets.bottom > 0 15 | } 16 | static let width = UIScreen.main.bounds.width 17 | static let height = UIScreen.main.bounds.height 18 | static let statusBarHeight = isIphoneX ? 44.f : 20.f 19 | static let navigationBarHeight = isIphoneX ? 88.f : 64.f 20 | static let tabBarHeight = isIphoneX ? 83.f : 49.f 21 | static let bottomSafeHeight = isIphoneX ? 34.f : 0.f 22 | 23 | } 24 | -------------------------------------------------------------------------------- /Sources/Extensions/Rx/MapAt.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MapAt.swift 3 | // SwiftBilibili 4 | // 5 | // Created by luowen on 2020/9/16. 6 | // Copyright © 2020 luowen. All rights reserved. 7 | // 8 | 9 | import RxSwift 10 | import RxCocoa 11 | 12 | extension ObservableType { 13 | 14 | /** 15 | Returns an observable sequence containing as many elements as its input but all of them are mapped to the result at the given keyPath 16 | 17 | - parameter keyPath: A key path whose root type matches the element type of the input sequence 18 | - returns: An observable squence containing the values pointed to by the key path 19 | */ 20 | public func mapAt(_ keyPath: KeyPath) -> Observable { 21 | return self.map { $0[keyPath: keyPath] } 22 | } 23 | } 24 | 25 | extension SharedSequenceConvertibleType { 26 | 27 | public func mapAt(_ keyPath: KeyPath) -> SharedSequence { 28 | return self.map { $0[keyPath: keyPath] } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Sources/Extensions/UIKit/UIButton+Kingfisher.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIButton+Kingfisher.swift 3 | // SwiftBilibili 4 | // 5 | // Created by luowen on 2020/11/2. 6 | // Copyright © 2020 luowen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Kingfisher 11 | import RxSwift 12 | import RxCocoa 13 | 14 | extension UIButton { 15 | @discardableResult 16 | func setImage( 17 | with resource: URL?, 18 | for state: UIControl.State = .normal, 19 | placeholder: UIImage? = nil, 20 | completionHandler: ((Result) -> Void)? = nil 21 | ) -> DownloadTask? { 22 | 23 | let options: KingfisherOptionsInfo = [.transition(.fade(0.2)),.scaleFactor(UIScreen.main.scale)] 24 | return self.kf.setImage(with: resource, for: state, placeholder: placeholder, options: options, completionHandler: completionHandler) 25 | } 26 | 27 | @discardableResult 28 | func setBackgroundImage( 29 | with resource: URL?, 30 | for state: UIControl.State = .normal, 31 | placeholder: UIImage? = nil, 32 | completionHandler: ((Result) -> Void)? = nil 33 | ) -> DownloadTask? { 34 | 35 | let options: KingfisherOptionsInfo = [.transition(.fade(0.2)),.scaleFactor(UIScreen.main.scale)] 36 | return self.kf.setBackgroundImage(with: resource, for: state, placeholder: placeholder, options: options, completionHandler: completionHandler) 37 | } 38 | } 39 | 40 | extension Reactive where Base: UIButton { 41 | 42 | var image: Binder { 43 | return Binder(self.base) { button,resource in 44 | button.setImage(with: resource) 45 | } 46 | } 47 | 48 | var backgroundImage: Binder { 49 | return Binder(self.base) { button,resource in 50 | button.setBackgroundImage(with: resource) 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Sources/Extensions/UIKit/UIColor+Hex.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIColor+Hex.swift 3 | // SwiftBilibili 4 | // 5 | // Created by luowen on 2020/9/4. 6 | // Copyright © 2020 luowen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public extension UIColor { 12 | 13 | convenience init(_ red:CGFloat,_ green:CGFloat,_ blue:CGFloat,_ alpha:CGFloat = 1.0) { 14 | self.init(red:red/255.0,green:green/255.0,blue:blue/255.0,alpha:alpha) 15 | } 16 | 17 | convenience init?(_ hex : String, alpha : CGFloat = 1.0) { 18 | 19 | var hex = hex 20 | if hex.hasPrefix("#") { 21 | let index = hex.index(hex.startIndex, offsetBy: 1) 22 | hex = String(hex[index...]) 23 | } 24 | 25 | var red: UInt32 = 0 26 | var green: UInt32 = 0 27 | var blue: UInt32 = 0 28 | var alpha: UInt32 = UInt32(alpha) * 255 29 | 30 | switch hex.count { 31 | case 6: 32 | let redIndex = hex.index(hex.startIndex, offsetBy: 2) 33 | let greenIndex = hex.index(redIndex, offsetBy: 2) 34 | Scanner(string: String(hex[..) -> Void)? = nil 20 | ) -> DownloadTask? { 21 | 22 | let options: KingfisherOptionsInfo = [.transition(.fade(0.2)),.scaleFactor(UIScreen.main.scale)] 23 | return self.kf.setImage(with: resource, placeholder: placeholder, options: options, completionHandler: completionHandler) 24 | } 25 | } 26 | 27 | extension Reactive where Base: UIImageView { 28 | 29 | var image: Binder { 30 | return Binder(self.base) { imageView,resource in 31 | imageView.setImage(with: resource) 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Sources/Extensions/UIKit/UIView+Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+Extension.swift 3 | // SwiftBilibili 4 | // 5 | // Created by luowen on 2020/9/22. 6 | // Copyright © 2020 luowen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension UIView { 12 | 13 | @IBInspectable public var bbCornerRadius: CGFloat { 14 | get { return layer.cornerRadius } 15 | set { 16 | layer.cornerRadius = newValue 17 | layer.masksToBounds = newValue > 0 18 | } 19 | } 20 | @IBInspectable public var bbBorderWidth: CGFloat { 21 | get { return layer.borderWidth } 22 | set { layer.borderWidth = newValue } 23 | } 24 | @IBInspectable public var bbBorderColor: UIColor? { 25 | get { return UIColor(cgColor: layer.borderColor!) } 26 | set { layer.borderColor = newValue?.cgColor } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Sources/Extensions/UIKit/UIViewController+PresentModal.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIViewController+PresentModal.swift 3 | // SwiftBilibili 4 | // 5 | // Created by luowen on 2020/11/19. 6 | // Copyright © 2020 luowen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension UIViewController { 12 | 13 | static func swizzlePresent() { 14 | let orginalSelector = #selector(present(_: animated: completion:)) 15 | let swizzledSelector = #selector(swizzledPresent) 16 | 17 | guard let orginalMethod = class_getInstanceMethod(self, orginalSelector), 18 | let swizzledMethod = class_getInstanceMethod(self, swizzledSelector) 19 | else { return } 20 | 21 | let didAddMethod = class_addMethod(self, 22 | orginalSelector, 23 | method_getImplementation(swizzledMethod), 24 | method_getTypeEncoding(swizzledMethod)) 25 | 26 | if didAddMethod { 27 | class_replaceMethod(self, 28 | swizzledSelector, 29 | method_getImplementation(orginalMethod), 30 | method_getTypeEncoding(orginalMethod)) 31 | } else { 32 | method_exchangeImplementations(orginalMethod, swizzledMethod) 33 | } 34 | } 35 | 36 | @objc private func swizzledPresent(_ viewControllerToPresent: UIViewController, 37 | animated flag: Bool, 38 | completion: (() -> Void)? = nil) { 39 | if #available(iOS 13.0, *) { 40 | if viewControllerToPresent.modalPresentationStyle == .automatic || viewControllerToPresent.modalPresentationStyle == .pageSheet { 41 | viewControllerToPresent.modalPresentationStyle = .fullScreen 42 | } 43 | } 44 | swizzledPresent(viewControllerToPresent, animated: flag, completion: completion) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Sources/Modules/Channel/ChannelViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChannelViewController.swift 3 | // SwiftBilibili 4 | // 5 | // Created by luowen on 2020/9/4. 6 | // Copyright © 2020 luowen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | final class ChannelViewController: BaseViewController { 12 | 13 | override func viewDidLoad() { 14 | super.viewDidLoad() 15 | 16 | themeService.typeStream 17 | .subscribe(onNext: { (style) in 18 | print(style.rawValue) 19 | }) 20 | .disposed(by: disposeBag) 21 | } 22 | 23 | override func setupUI() { 24 | 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /Sources/Modules/Dynamic/DynamicViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DynamicViewController.swift 3 | // SwiftBilibili 4 | // 5 | // Created by luowen on 2020/9/4. 6 | // Copyright © 2020 luowen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | final class DynamicViewController: BaseViewController { 12 | 13 | override func viewDidLoad() { 14 | super.viewDidLoad() 15 | 16 | // Do any additional setup after loading the view. 17 | } 18 | 19 | /* 20 | // MARK: - Navigation 21 | 22 | // In a storyboard-based application, you will often want to do a little preparation before navigation 23 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 24 | // Get the new view controller using segue.destination. 25 | // Pass the selected object to the new view controller. 26 | } 27 | */ 28 | 29 | } 30 | -------------------------------------------------------------------------------- /Sources/Modules/Home/Controllers/HomeActivityViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HomeActivityViewController.swift 3 | // SwiftBilibili 4 | // 5 | // Created by luowen on 2020/10/22. 6 | // Copyright © 2020 luowen. All rights reserved. 7 | // 活动控制器 8 | 9 | import UIKit 10 | import JXSegmentedView 11 | 12 | final class HomeActivityViewController: BaseViewController { 13 | 14 | override func viewDidLoad() { 15 | super.viewDidLoad() 16 | 17 | } 18 | 19 | override func setupUI() {} 20 | } 21 | 22 | extension HomeActivityViewController: JXSegmentedListContainerViewListDelegate { 23 | func listView() -> UIView { 24 | return self.view 25 | } 26 | } 27 | 28 | extension HomeActivityViewController: Routerable {} 29 | -------------------------------------------------------------------------------- /Sources/Modules/Home/Controllers/HomeBangumiViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HomeBangumiViewController.swift 3 | // SwiftBilibili 4 | // 5 | // Created by luowen on 2020/10/14. 6 | // Copyright © 2020 luowen. All rights reserved. 7 | // 追番控制器 8 | 9 | import UIKit 10 | import JXSegmentedView 11 | 12 | final class HomeBangumiViewController: BaseViewController { 13 | 14 | override func viewDidLoad() { 15 | super.viewDidLoad() 16 | // Do any additional setup after loading the view. 17 | } 18 | 19 | override func setupUI() {} 20 | } 21 | 22 | extension HomeBangumiViewController: JXSegmentedListContainerViewListDelegate { 23 | 24 | func listView() -> UIView { 25 | return self.view 26 | } 27 | } 28 | 29 | extension HomeBangumiViewController: Routerable {} 30 | -------------------------------------------------------------------------------- /Sources/Modules/Home/Controllers/HomeCinemaViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HomeCinemaViewController.swift 3 | // SwiftBilibili 4 | // 5 | // Created by luowen on 2020/10/22. 6 | // Copyright © 2020 luowen. All rights reserved. 7 | // 影视控制器 8 | 9 | import UIKit 10 | import JXSegmentedView 11 | 12 | final class HomeCinemaViewController: BaseViewController { 13 | 14 | override func viewDidLoad() { 15 | super.viewDidLoad() 16 | 17 | } 18 | 19 | override func setupUI() {} 20 | } 21 | 22 | extension HomeCinemaViewController: JXSegmentedListContainerViewListDelegate { 23 | func listView() -> UIView { 24 | return self.view 25 | } 26 | } 27 | 28 | extension HomeCinemaViewController: Routerable {} 29 | -------------------------------------------------------------------------------- /Sources/Modules/Home/Controllers/HomeHotViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HomeHotViewController.swift 3 | // SwiftBilibili 4 | // 5 | // Created by luowen on 2020/10/22. 6 | // Copyright © 2020 luowen. All rights reserved. 7 | // 热门控制器 8 | 9 | import UIKit 10 | import JXSegmentedView 11 | 12 | final class HomeHotViewController: BaseViewController { 13 | 14 | override func viewDidLoad() { 15 | super.viewDidLoad() 16 | 17 | } 18 | 19 | override func setupUI() {} 20 | } 21 | 22 | extension HomeHotViewController: JXSegmentedListContainerViewListDelegate { 23 | func listView() -> UIView { 24 | return self.view 25 | } 26 | } 27 | 28 | extension HomeHotViewController: Routerable { 29 | 30 | func open(with completion: @escaping () -> Void) { 31 | UIViewController.topMost?.present(self, animated: true, completion: completion) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Sources/Modules/Home/Controllers/HomeLiveViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HomeLiveViewController.swift 3 | // SwiftBilibili 4 | // 5 | // Created by luowen on 2020/10/14. 6 | // Copyright © 2020 luowen. All rights reserved. 7 | // 直播控制器 8 | 9 | import UIKit 10 | import JXSegmentedView 11 | 12 | final class HomeLiveViewController: BaseViewController { 13 | 14 | override func viewDidLoad() { 15 | super.viewDidLoad() 16 | } 17 | 18 | override func setupUI() {} 19 | } 20 | 21 | extension HomeLiveViewController: JXSegmentedListContainerViewListDelegate { 22 | 23 | func listView() -> UIView { 24 | return self.view 25 | } 26 | } 27 | 28 | extension HomeLiveViewController: Routerable {} 29 | -------------------------------------------------------------------------------- /Sources/Modules/Home/Controllers/HomeOptionalViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HomeOptionalViewController.swift 3 | // SwiftBilibili 4 | // 5 | // Created by luowen on 2020/10/22. 6 | // Copyright © 2020 luowen. All rights reserved. 7 | // 可选控制器 8 | 9 | import UIKit 10 | import JXSegmentedView 11 | 12 | final class HomeOptionalViewController: BaseViewController { 13 | 14 | private let id: Int 15 | 16 | init(id: Int) { 17 | self.id = id 18 | super.init() 19 | log.debug("当前的id:\(id)") 20 | } 21 | 22 | required convenience init?(coder aDecoder: NSCoder) { 23 | fatalError("init(coder:) has not been implemented") 24 | } 25 | 26 | override func viewDidLoad() { 27 | super.viewDidLoad() 28 | 29 | // Do any additional setup after loading the view. 30 | } 31 | 32 | override func setupUI() {} 33 | 34 | } 35 | 36 | extension HomeOptionalViewController: JXSegmentedListContainerViewListDelegate { 37 | func listView() -> UIView { 38 | return self.view 39 | } 40 | } 41 | 42 | extension HomeOptionalViewController: Routerable {} 43 | -------------------------------------------------------------------------------- /Sources/Modules/Home/Controllers/HomePromoViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HomePromoViewController.swift 3 | // SwiftBilibili 4 | // 5 | // Created by 罗文 on 2020/9/4. 6 | // Copyright © 2020 luowen. All rights reserved. 7 | // 推荐控制器 8 | 9 | import UIKit 10 | import JXSegmentedView 11 | 12 | struct EmptyConfig { 13 | 14 | static private let normal = EmptyDataSetConfigure(verticalOffset: -83, tipFont: UIFont.systemFont(ofSize: 15), tipColor: UIColor.white, allowScroll: false,verticalSpace: 25) 15 | 16 | //个人中心优惠券 17 | static let coupon = { () -> EmptyDataSetConfigure in 18 | var config = EmptyConfig.normal 19 | config.tipStr = "还没有优惠券哦~" 20 | config.tipImage = UIImage(named: "empty_coupon") 21 | return config 22 | }() 23 | } 24 | 25 | final class HomePromoViewController: BaseViewController, EmptyDataSetable { 26 | 27 | override func viewDidLoad() { 28 | super.viewDidLoad() 29 | view.backgroundColor = UIColor.cyan 30 | } 31 | 32 | override func setupUI() { 33 | view.addSubview(tableView) 34 | //tableView.reloadData() 35 | 36 | self.lw.updateEmptyDataSet(tableView, config: EmptyConfig.coupon) 37 | } 38 | 39 | override func touchesBegan(_ touches: Set, with event: UIEvent?) { 40 | } 41 | 42 | lazy var tableView: UITableView = { 43 | let tableView = UITableView(frame: view.frame, style: .plain) 44 | tableView.keyboardDismissMode = .onDrag 45 | tableView.estimatedRowHeight = 0 46 | tableView.separatorStyle = .none 47 | tableView.backgroundColor = .clear 48 | tableView.sectionHeaderHeight = 0.1 49 | tableView.sectionFooterHeight = 0.1 50 | tableView.tableFooterView = UIView() 51 | tableView.dataSource = self 52 | return tableView 53 | }() 54 | } 55 | 56 | extension HomePromoViewController: UITableViewDataSource { 57 | 58 | func numberOfSections(in tableView: UITableView) -> Int { 59 | return 2 60 | } 61 | 62 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 63 | if section == 0 { 64 | return 1 65 | } 66 | return 0 67 | } 68 | 69 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 70 | return UITableViewCell() 71 | } 72 | } 73 | 74 | extension HomePromoViewController: JXSegmentedListContainerViewListDelegate { 75 | 76 | func listView() -> UIView { 77 | return self.view 78 | } 79 | } 80 | 81 | extension HomePromoViewController: Routerable {} 82 | -------------------------------------------------------------------------------- /Sources/Modules/Home/Custom/SegmentedDataSource/BBSegmentedTitleIOrImagetemModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BBSegmentedTitleIOrImagetemModel.swift 3 | // SwiftBilibili 4 | // 5 | // Created by luowen on 2020/10/19. 6 | // Copyright © 2020 luowen. All rights reserved. 7 | // 8 | 9 | import JXSegmentedView 10 | 11 | final class BBSegmentedTitleOrImageItemModel: JXSegmentedTitleItemModel { 12 | var showImageInfo: String? 13 | var loadImageClosure: LoadImageClosure? 14 | var imageSize: CGSize = CGSize.zero 15 | } 16 | -------------------------------------------------------------------------------- /Sources/Modules/Home/Custom/SegmentedDataSource/BBSegmentedTitleOrImageDataSource.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BBSegmentedTitleOrImageDataSource.swift 3 | // SwiftBilibili 4 | // 5 | // Created by luowen on 2020/10/19. 6 | // Copyright © 2020 luowen. All rights reserved. 7 | // 8 | 9 | import JXSegmentedView 10 | 11 | final class BBSegmentedTitleOrImageDataSource: JXSegmentedTitleDataSource { 12 | 13 | /// 数量需要和item的数量保持一致。可以是ImageName或者图片地址。选中时不显示图片就填nil 14 | var showImageInfos: [String?]? 15 | /// 内部默认通过UIImage(named:)加载图片。如果传递的是图片地址或者想自己处理图片加载逻辑,可以通过该闭包处理。 16 | var loadImageClosure: LoadImageClosure? 17 | /// 图片尺寸 18 | var imageSize: CGSize = CGSize(width: 60, height: 40) 19 | 20 | override func preferredItemModelInstance() -> JXSegmentedBaseItemModel { 21 | return BBSegmentedTitleOrImageItemModel() 22 | } 23 | 24 | override func reloadData(selectedIndex: Int) { 25 | selectedAnimationDuration = 0.1 26 | 27 | super.reloadData(selectedIndex: selectedIndex) 28 | } 29 | 30 | override func preferredRefreshItemModel( _ itemModel: JXSegmentedBaseItemModel, at index: Int, selectedIndex: Int) { 31 | super.preferredRefreshItemModel(itemModel, at: index, selectedIndex: selectedIndex) 32 | 33 | guard let itemModel = itemModel as? BBSegmentedTitleOrImageItemModel else { 34 | return 35 | } 36 | 37 | itemModel.showImageInfo = showImageInfos?[index] 38 | itemModel.loadImageClosure = loadImageClosure 39 | itemModel.imageSize = imageSize 40 | } 41 | 42 | // MARK: - JXSegmentedViewDataSource 43 | override func registerCellClass(in segmentedView: JXSegmentedView) { 44 | segmentedView.collectionView.register(BBSegmentedTitleOrImageCell.self, forCellWithReuseIdentifier: "cell") 45 | } 46 | 47 | override func segmentedView(_ segmentedView: JXSegmentedView, cellForItemAt index: Int) -> JXSegmentedBaseCell { 48 | let cell = segmentedView.dequeueReusableCell(withReuseIdentifier: "cell", at: index) 49 | return cell 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Sources/Modules/Home/Models/HomeTabModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HomeTabModel.swift 3 | // SwiftBilibili 4 | // 5 | // Created by luowen on 2020/10/14. 6 | // Copyright © 2020 luowen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import ObjectMapper 11 | 12 | struct HomeTabInfoModel: ImmutableMappable { 13 | 14 | var top: [HomeTabItemModel] 15 | var tab: [HomeTabItemModel] 16 | var bottom: [HomeTabItemModel] 17 | 18 | init(map: Map) throws { 19 | top = try map.value("top") 20 | tab = try map.value("tab") 21 | bottom = try map.value("bottom") 22 | } 23 | } 24 | 25 | struct HomeTabItemModel: ImmutableMappable { 26 | 27 | var id: Int 28 | var name: String 29 | var url: String 30 | var tabId: String 31 | var pos: Int 32 | var icon: String? 33 | var `extension`: HomeTabExtensionModel? 34 | var defaultSelected: Int? 35 | 36 | init(map: Map) throws { 37 | id = try map.value("id") 38 | name = try map.value("name") 39 | url = try map.value("uri") 40 | tabId = try map.value("tab_id") 41 | pos = try map.value("pos") 42 | icon = try? map.value("icon") 43 | `extension` = try? map.value("extension") 44 | defaultSelected = try? map.value("default_selected") 45 | } 46 | } 47 | 48 | struct HomeTabExtensionModel: ImmutableMappable { 49 | 50 | var inactiveIcon: String 51 | var activeIcon: String 52 | 53 | init(map: Map) throws { 54 | inactiveIcon = try map.value("inactive_icon") 55 | activeIcon = try map.value("active_icon") 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Sources/Modules/LaunchModule/API/LaunchAPI.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LaunchAPI.swift 3 | // SwiftBilibili 4 | // 5 | // Created by luowen on 2021/10/19. 6 | // Copyright © 2021 luowen. All rights reserved. 7 | // 8 | 9 | import LWNetwork 10 | 11 | enum LaunchAPI { 12 | // 开屏图 13 | case splashInfo 14 | // 广告图 15 | case adInfo 16 | } 17 | 18 | extension LaunchAPI: SugarTargetType, Cacheable { 19 | 20 | var baseURL: URL { 21 | switch self { 22 | case .splashInfo, .adInfo: 23 | return URL(string: "https://app.bilibili.com")! 24 | } 25 | } 26 | 27 | var route: Route { 28 | switch self { 29 | case .splashInfo: 30 | return .get("x/v2/splash/brand/list") 31 | case .adInfo: 32 | return .get("x/v2/splash/list") 33 | } 34 | } 35 | 36 | var parameters: Parameters? { 37 | switch self { 38 | case .splashInfo: 39 | return ["last_read_at": -1,"network": "wifi","screen_height": 2532,"screen_width": 1170,"sign": "480c2bdb137ed9e0b41dc0ccf4343f7c","ts": 1634623698] 40 | case .adInfo: 41 | return nil 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Sources/Modules/LaunchModule/Cache/LaunchUserDefaults.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LaunchUserDefaults.swift 3 | // SwiftBilibili 4 | // 5 | // Created by luowen on 2021/10/19. 6 | // Copyright © 2021 luowen. All rights reserved. 7 | // 8 | 9 | import SwiftyUserDefaults 10 | 11 | enum LaunchUserDefaults { 12 | 13 | @SwiftyUserDefault(keyPath: \.isAgreePolicy) 14 | static var isAgreePolicy: Bool 15 | 16 | } 17 | 18 | extension DefaultsKeys { 19 | // 是否同意过隐私协议 20 | var isAgreePolicy: DefaultsKey { .init("isAgreePolicy", defaultValue: false) } 21 | } 22 | -------------------------------------------------------------------------------- /Sources/Modules/LaunchModule/LaunchImages.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Sources/Modules/LaunchModule/LaunchImages.xcassets/default_splash.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "launcher_image_2020_0709@2x.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "launcher_image_2020_0709@3x.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Sources/Modules/LaunchModule/LaunchImages.xcassets/default_splash.imageset/launcher_image_2020_0709@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luowen1991/SwiftBilibili/fcf130b78be1e4b844a5e26df40e862a02bb986c/Sources/Modules/LaunchModule/LaunchImages.xcassets/default_splash.imageset/launcher_image_2020_0709@2x.png -------------------------------------------------------------------------------- /Sources/Modules/LaunchModule/LaunchImages.xcassets/default_splash.imageset/launcher_image_2020_0709@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luowen1991/SwiftBilibili/fcf130b78be1e4b844a5e26df40e862a02bb986c/Sources/Modules/LaunchModule/LaunchImages.xcassets/default_splash.imageset/launcher_image_2020_0709@3x.png -------------------------------------------------------------------------------- /Sources/Modules/LaunchModule/LaunchImages.xcassets/logo.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "launcher_logo_2020@2x.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "launcher_logo_2020@3x.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Sources/Modules/LaunchModule/LaunchImages.xcassets/logo.imageset/launcher_logo_2020@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luowen1991/SwiftBilibili/fcf130b78be1e4b844a5e26df40e862a02bb986c/Sources/Modules/LaunchModule/LaunchImages.xcassets/logo.imageset/launcher_logo_2020@2x.png -------------------------------------------------------------------------------- /Sources/Modules/LaunchModule/LaunchImages.xcassets/logo.imageset/launcher_logo_2020@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luowen1991/SwiftBilibili/fcf130b78be1e4b844a5e26df40e862a02bb986c/Sources/Modules/LaunchModule/LaunchImages.xcassets/logo.imageset/launcher_logo_2020@3x.png -------------------------------------------------------------------------------- /Sources/Modules/LaunchModule/Models/SplashInfoModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SplashInfoModel.swift 3 | // SwiftBilibili 4 | // 5 | // Created by 罗文 on 2020/9/6. 6 | // Copyright © 2020 luowen. All rights reserved. 7 | // 8 | 9 | import ObjectMapper 10 | 11 | enum SplashShowType: String { 12 | case half = "half" 13 | case full = "full" 14 | } 15 | 16 | enum SplashLogoPosition: String { 17 | case center = "center" 18 | case left = "left" 19 | } 20 | 21 | class SplashInfoModel: Mappable { 22 | 23 | var pullInterval: Double = 1800 24 | var totalItems: [SplashItemModel] = [] 25 | var showItems: [SplashShowModel] = [] 26 | 27 | required convenience init?(map: Map) { 28 | self.init() 29 | } 30 | 31 | func mapping(map: Map) { 32 | pullInterval <- map["pull_interval"] 33 | totalItems <- map["list"] 34 | showItems <- map["show"] 35 | } 36 | } 37 | 38 | class SplashItemModel: Mappable { 39 | var id: Int = 0 40 | var thumb: String = "" 41 | var logoUrl: String = "" 42 | var mode: SplashShowType = .half 43 | 44 | required convenience init?(map: Map) { 45 | self.init() 46 | } 47 | 48 | func mapping(map: Map) { 49 | id <- map["id"] 50 | thumb <- map["thumb"] 51 | logoUrl <- map["logo_url"] 52 | mode <- map["mode"] 53 | } 54 | 55 | } 56 | 57 | class SplashShowModel: Mappable { 58 | var id: Int = 0 59 | var beginTime: Double = 0 60 | var endTime: Double = 0 61 | var duration: Int = 700 62 | var logoPosition: SplashLogoPosition = .center 63 | 64 | required convenience init?(map: Map) { 65 | self.init() 66 | } 67 | 68 | func mapping(map: Map) { 69 | id <- map["id"] 70 | beginTime <- map["begin_time"] 71 | endTime <- map["end_time"] 72 | duration <- map["duration"] 73 | logoPosition <- map["logo_position"] 74 | } 75 | } 76 | 77 | //@objc 78 | //class SplashShowRealmModel: Object { 79 | // @objc dynamic var id: Int = 0 80 | // @objc dynamic var beginTime: Double = 0 81 | // @objc dynamic var endTime: Double = 0 82 | // @objc dynamic var thumb: String = "" 83 | // @objc dynamic var logoUrl: String = "" 84 | // @objc dynamic var isShow: Bool = false 85 | // @objc dynamic var duration: Int = 700 86 | // @objc dynamic var mode: String = "" 87 | // @objc dynamic var logoPosition: String = "" 88 | // 89 | // override class func primaryKey() -> String? { 90 | // return "id" 91 | // } 92 | //} 93 | -------------------------------------------------------------------------------- /Sources/Modules/Login/LoginViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LoginViewController.swift 3 | // SwiftBilibili 4 | // 5 | // Created by luowen on 2020/11/19. 6 | // Copyright © 2020 luowen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class LoginViewController: BaseViewController { 12 | 13 | override func viewDidLoad() { 14 | super.viewDidLoad() 15 | 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /Sources/Modules/Member/MemberViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MemberViewController.swift 3 | // SwiftBilibili 4 | // 5 | // Created by luowen on 2020/9/4. 6 | // Copyright © 2020 luowen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | final class MemberViewController: BaseViewController { 12 | 13 | override func viewDidLoad() { 14 | super.viewDidLoad() 15 | 16 | // Do any additional setup after loading the view. 17 | } 18 | 19 | /* 20 | // MARK: - Navigation 21 | 22 | // In a storyboard-based application, you will often want to do a little preparation before navigation 23 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 24 | // Get the new view controller using segue.destination. 25 | // Pass the selected object to the new view controller. 26 | } 27 | */ 28 | 29 | } 30 | -------------------------------------------------------------------------------- /Sources/Router/Home/HomeRouter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HomeRouter.swift 3 | // SwiftBilibili 4 | // 5 | // Created by luowen on 2020/11/18. 6 | // Copyright © 2020 luowen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import URLNavigator 11 | 12 | struct HomeRouter { 13 | static var router: Provider! 14 | static func initialize(navigator: Navigator) { 15 | self.router = Provider(navigator: navigator, [HomeRouterLoginPlugin()]) 16 | } 17 | } 18 | 19 | extension HomeRouter { 20 | 21 | /// 打开 22 | /// 23 | /// - Parameters: 24 | /// - url: url 25 | /// - context: context 26 | /// - Returns: true or false 27 | @discardableResult 28 | static func open(_ url: URLConvertible, 29 | completion: ((Bool) -> Void)? = .none) -> Bool { 30 | return router.open(url, completion: completion) 31 | } 32 | 33 | /// 获取视图控制器 34 | /// 35 | /// - Parameters: 36 | /// - url: url 37 | /// - context: context 38 | /// - Returns: 视图控制器 39 | static func viewController(_ url: URLConvertible) -> UIViewController? { 40 | return router.viewController(url) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Sources/Router/Home/HomeRouterLoginPlugin.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HomeRouterLoginPlugin.swift 3 | // SwiftBilibili 4 | // 5 | // Created by luowen on 2020/11/18. 6 | // Copyright © 2020 luowen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class HomeRouterLoginPlugin: Plugin { 12 | 13 | override func prepare(open type: HomeRouterType, completion: @escaping (Bool) -> Void) { 14 | guard type.needLogin else { 15 | completion(true) 16 | return 17 | } 18 | guard !Utils.isLogin else { 19 | completion(true) 20 | return 21 | } 22 | guard let controller = UIViewController.topMost else { 23 | completion(true) 24 | return 25 | } 26 | let loginController = LoginViewController() 27 | controller.present(loginController, animated: true) { 28 | completion(true) 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Sources/Router/Home/HomeRouterOpener.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HomeRouterOpener.swift 3 | // SwiftBilibili 4 | // 5 | // Created by luowen on 2020/11/18. 6 | // Copyright © 2020 luowen. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | extension HomeRouterType { 12 | 13 | func controller(url: URLConvertible, values: [String : Any]) -> Routerable? { 14 | switch self { 15 | case .live: 16 | return HomeLiveViewController() 17 | case .promo: 18 | return HomePromoViewController() 19 | case .bangum: 20 | return HomeBangumiViewController() 21 | case .cinema: 22 | return HomeCinemaViewController() 23 | case .activity: 24 | return HomeActivityViewController() 25 | case .hot: 26 | return HomeHotViewController() 27 | case .optional: 28 | guard let id = values["id"] as? Int else { return nil } 29 | return HomeOptionalViewController(id: id) 30 | case .test: 31 | return nil 32 | } 33 | } 34 | 35 | func handle(url: URLConvertible, values: [String : Any], completion: @escaping (Bool) -> Void) { 36 | log.debug("没有返回控制器") 37 | completion(true) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Sources/Router/Home/HomeRouterType.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HomeRouterType.swift 3 | // SwiftBilibili 4 | // 5 | // Created by luowen on 2020/11/18. 6 | // Copyright © 2020 luowen. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | enum HomeRouterType { 12 | /// 推荐 13 | case promo 14 | /// 直播 15 | case live 16 | /// 番剧 17 | case bangum 18 | /// 热门 19 | case hot 20 | /// 影院 21 | case cinema 22 | /// 活动 23 | case activity 24 | /// 配置 25 | case optional 26 | /// 测试 27 | case test 28 | } 29 | 30 | extension HomeRouterType: RouterTypeable { 31 | 32 | var pattern: String { 33 | switch self { 34 | case .promo: 35 | return "bilibili://pegasus/promo" 36 | case .live: 37 | return "bilibili://live/home" 38 | case .bangum: 39 | return "bilibili://pgc/home" 40 | case .hot: 41 | return "bilibili://pegasus/hottopic" 42 | case .cinema: 43 | return "bilibili://pgc/cinema-tab" 44 | case .activity: 45 | return "bilibili://pgc/activity-tab" 46 | case .optional: 47 | return "bilibili://pegasus/op/" 48 | case .test: 49 | return "bilibili://test" 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Sources/Router/Main/Router.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Router.swift 3 | // SwiftBilibili 4 | // 5 | // Created by luowen on 2020/11/17. 6 | // Copyright © 2020 luowen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import URLNavigator 11 | 12 | struct Router { 13 | static var router: Provider! 14 | static func initialize(navigator: Navigator) { 15 | self.router = Provider(navigator: navigator, []) 16 | } 17 | } 18 | 19 | extension Router { 20 | 21 | /// 打开 22 | /// 23 | /// - Parameters: 24 | /// - url: url 25 | /// - context: context 26 | /// - Returns: true or false 27 | @discardableResult 28 | static func open(_ url: URLConvertible, 29 | completion: ((Bool) -> Void)? = .none) -> Bool { 30 | return router.open(url, completion: completion) 31 | } 32 | 33 | /// 获取视图控制器 34 | /// 35 | /// - Parameters: 36 | /// - url: url 37 | /// - context: context 38 | /// - Returns: 视图控制器 39 | static func viewController(_ url: URLConvertible) -> UIViewController? { 40 | return router.viewController(url) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Sources/Router/Main/RouterOpener.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RouterOpener.swift 3 | // SwiftBilibili 4 | // 5 | // Created by luowen on 2020/11/17. 6 | // Copyright © 2020 luowen. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | extension RouterType { 12 | 13 | func controller(url: URLConvertible, values: [String: Any]) -> Routerable? { 14 | 15 | switch self { 16 | case .http,.https: 17 | guard let url = url.urlValue else { return nil } 18 | return BaseWebViewController(url: url.absoluteString) 19 | case .error: 20 | return nil 21 | } 22 | } 23 | 24 | func handle(url: URLConvertible, values: [String : Any], completion: @escaping (Bool) -> Void) { 25 | log.debug("错误的url: \(url)") 26 | completion(true) 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /Sources/Router/Main/RouterType.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RouterType.swift 3 | // SwiftBilibili 4 | // 5 | // Created by luowen on 2020/11/17. 6 | // Copyright © 2020 luowen. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | enum RouterType { 12 | /// http 13 | case http 14 | /// https 15 | case https 16 | /// 不存在地址 17 | case error 18 | } 19 | 20 | extension RouterType: RouterTypeable { 21 | 22 | var pattern: String { 23 | switch self { 24 | case .http: 25 | return "http://" 26 | case .https: 27 | return "https://" 28 | case .error: 29 | return "bilibili://" 30 | 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Sources/Router/Navigator/Context.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Context.swift 3 | // SwiftBilibili 4 | // 5 | // Created by luowen on 2020/11/17. 6 | // Copyright © 2020 luowen. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct Context { 12 | let callback: (Bool) -> Void 13 | 14 | init(_ completion: @escaping (Bool) -> Void = { _ in }) { 15 | callback = completion 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Sources/Router/Navigator/Plugin.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Plugin.swift 3 | // SwiftBilibili 4 | // 5 | // Created by luowen on 2020/11/13. 6 | // Copyright © 2020 luowen. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | 12 | 13 | open class Plugin: RouterPluginable { 14 | 15 | public init() { 16 | 17 | } 18 | 19 | open func should(open type: T) -> Bool { 20 | return true 21 | } 22 | 23 | open func prepare(open type: T, completion: @escaping (Bool) -> Void) { 24 | completion(true) 25 | } 26 | 27 | open func will(open type: T, controller: Routerable) { 28 | 29 | } 30 | 31 | open func did(open type: T, controller: Routerable) { 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Sources/Services/API/ConfigAPI.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ConfigAPI.swift 3 | // SwiftBilibili 4 | // 5 | // Created by 罗文 on 2020/9/5. 6 | // Copyright © 2020 luowen. All rights reserved. 7 | // 8 | 9 | // swiftlint:disable line_length 10 | 11 | import LWNetwork 12 | import LWExtensionKit 13 | import Alamofire 14 | 15 | /// app中的一些配置API 比如广告 开屏 上传 16 | enum ConfigAPI { 17 | /// 开屏图列表 18 | case splashList 19 | /// 广告列表 20 | case adList 21 | /// 标签栏列表 22 | case tabList 23 | } 24 | 25 | extension ConfigAPI: SugarTargetType { 26 | 27 | var baseURL: URL { 28 | return URL(string: "https://app.bilibili.com")! 29 | } 30 | 31 | var route: Route { 32 | switch self { 33 | case .splashList: 34 | return .get("/x/v2/splash/brand/list") 35 | case .adList: 36 | return .get("/x/v2/splash/list") 37 | case .tabList: 38 | return .get("/x/resource/show/tab") 39 | } 40 | } 41 | 42 | var parameters: LWNetwork.Parameters? { 43 | switch self { 44 | case .splashList: 45 | return ["last_read_at": -1,"network": "wifi","screen_height": 2532,"screen_width": 1170,"sign": "480c2bdb137ed9e0b41dc0ccf4343f7c","ts": 1634623698] 46 | case .adList: 47 | return ["ad_extra":adExtra,"birth":"","height":"2436","width":"1125","sign":"625b697f828703ee8d62f26045bba2e0","build":"10300","ts":"1601015546","statistics":"%7B%22appId%22%3A1%2C%22version%22%3A%226.10.0%22%2C%22abtest%22%3A%22%22%2C%22platform%22%3A1%7D"] 48 | case .tabList: 49 | return ["appver":"10300","filtered":"1","sign":"9ce189481c9014339ee1fbda152bcb03","build":"10300","ts":"1601015545","statistics":"%7B%22appId%22%3A1%2C%22version%22%3A%226.10.0%22%2C%22abtest%22%3A%22%22%2C%22platform%22%3A1%7D"] 50 | } 51 | } 52 | 53 | } 54 | 55 | extension ConfigAPI { 56 | var adExtra: String { 57 | return "F6209EEC4D712ADB7AAF94480A782BC7DA2715C77FE25001F5FB49394B682198E786E46161C40B92AC5EF0BA4C1EB739E3F12AED4E2BF5D32A3FA3EBC23ED21100C43C049922D2E0A0F975D04A29EB11056B6CE74833E7D536C165634438CD9CA1F10C777100E28C088F4D7D8DB0357B234756C288FD4E8CCC7229D84F81614028BCB0BFDFCD5C23C0D46EFA89E36C7A2C9C10475268A264482DB9A230708C86DE8CDD4C68D547A4D5EE48BE8D5BB52E477E46C9100A8C1B09FBA458FD36012905C71CC354884D3BE08E87AB992C739432E35A0ACA8FF89CF58B0ED9A58D01548E335F5A254C1E54308AD8ACB8A0D331691484193653FFC27CD53B80BD1F868E020CDAACAA80B45EF6BBFF41D17FA2F35F395DAB1907FB427103CF7D9DBA0508FD1CF2827FDBB805FE317C3FB59A33444333012BCA1EE34FAFD5F7EDFE3C77448B05B49DCCEE860769E15408631C033325C6F4BE951400EE2D24F2440475B644E0AAFCCB8FD5FF732D8CCEE55A5CB8587A4A6C1F7738F747BD2C9734C162FE3F292850CB6AB3FCE9B835B4A51285F73F55C7410E67A5AEAF20B2B23B6939FA62E9D95588482B2EA2F9BAEC2AFC207FDF5A1088F7CACFB28AFD88E63465A22C56112345541A46B1B2AC62F81A0D202CD3F6C917EC923F724441B9535161F5FB841B635DF87F75C97148885980B7368F76C3A2D08276845F9D82B7753CDBA1977456C642071F7E6D1F0C3C08A0E0720FECE72BC9B4A8633AB593DFB51251169413" 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Sources/Services/API/HomeAPI.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HomeAPI.swift 3 | // SwiftBilibili 4 | // 5 | // Created by luowen on 2020/9/25. 6 | // Copyright © 2020 luowen. All rights reserved. 7 | // 8 | 9 | // swiftlint:disable line_length 10 | 11 | import LWNetwork 12 | 13 | enum HomeAPI { 14 | /// 首页展示数据列表 15 | case feedList 16 | } 17 | 18 | extension HomeAPI: SugarTargetType { 19 | 20 | var baseURL: URL { 21 | return URL(string: "http://app.bilibili.com")! 22 | } 23 | 24 | var route: Route { 25 | switch self { 26 | case .feedList: 27 | return .get("x/v2/feed/index") 28 | } 29 | } 30 | 31 | var parameters: Parameters? { 32 | return ["ad_extra":adExtra,"appver":"10300","build":"10300","autoplay_card":"0","banner_hash":"","device_name":"iPhone%20X","fnver":"0","fourk":"1","network":"wifi","sign":"ecd8c89a378fee736e579b5c4fe24e42","filtered":"1","flush":"0","guidance":"1","https_url_req":"0","fnval":"208","idx":"0","login_event":"1","column":"0","pull":"1","qn":"32","recsys_mode":"0","open_event":"cold","statistics":"%7B%22appId%22%3A1%2C%22version%22%3A%226.10.0%22%2C%22abtest%22%3A%22%22%2C%22platform%22%3A1%7D"] 33 | } 34 | 35 | } 36 | 37 | extension HomeAPI { 38 | var adExtra: String { 39 | return "F6209EEC4D712ADB7AAF94480A782BC7DA2715C77FE25001F5FB49394B682198E786E46161C40B92AC5EF0BA4C1EB739E3F12AED4E2BF5D32A3FA3EBC23ED21100C43C049922D2E0A0F975D04A29EB11056B6CE74833E7D536C165634438CD9CA1F10C777100E28C088F4D7D8DB0357B234756C288FD4E8CCC7229D84F81614028BCB0BFDFCD5C23C0D46EFA89E36C7A2C9C10475268A264482DB9A230708C86DE8CDD4C68D547A4D5EE48BE8D5BB52E477E46C9100A8C1B09FBA458FD36012905C71CC354884D3BE08E87AB992C739432E35A0ACA8FF89CF58B0ED9A58D01548E335F5A254C1E54308AD8ACB8A0D331691484193653FFC27CD53B80BD1F868E020CDAACAA80B45EF6BBFF41D17FA2F35F395DAB1907FB427103CF7D9DBA0508FD1CF2827FDBB805FE317C3FB59A33444333012BCA1EE34FAFD5F7EDFE3C77448B05B49DCCEE860769E15408631C033325C6F4BE951400EE2D24F2440475B644E0AAFCCB8FD5FF732D8CCEE55A5CB8587A4A6C1F7738F747BD2C9734C162FE3F292850CB6AB3FCE9B835B4A51285F73F55C7410E67A5AEAF20B2B23B6939FA62E9D95588482B2EA2F9BAEC2AFC207FDF5A1088F7CACFB28AFD88E63465A22C56112345541A46B1B2AC62F81A0D202CD3F6C917EC923F724441B9535161F5FB841B635DF87F75C97148885980B7368F76C3A2D08276845F9D82B7753CDBA1977456C642071F7E6D1F0C3C08A0E0720FECE72BC9B4A8633AB593DFB51251169413" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Sources/Tools/CGFloatLiteral/CGFloatLiteral.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CGFloatLiteral.swift 3 | // SwiftBilibili 4 | // 5 | // Created by luowen on 2020/9/4. 6 | // Copyright © 2020 luowen. All rights reserved. 7 | // 8 | 9 | import CoreGraphics 10 | 11 | public extension IntegerLiteralType { 12 | var f: CGFloat { 13 | return CGFloat(self) 14 | } 15 | } 16 | 17 | public extension FloatLiteralType { 18 | var f: CGFloat { 19 | return CGFloat(self) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Sources/Tools/ManualLayout/CALayer+ManualLayout.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CALayer+ManualLayout.swift 3 | // ManualLayout 4 | // 5 | // Created by Baris Sencan on 23/02/15. 6 | // Copyright (c) 2015 Baris Sencan. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public extension CALayer { 12 | 13 | // MARK: - Position 14 | var origin: CGPoint { 15 | get { 16 | return frame.origin 17 | } 18 | set { 19 | x = newValue.x 20 | y = newValue.y 21 | } 22 | } 23 | 24 | var x: CGFloat { 25 | get { return frame.x } 26 | set { frame.x = newValue } 27 | } 28 | 29 | var y: CGFloat { 30 | get { return frame.y } 31 | set { frame.y = newValue } 32 | } 33 | 34 | var center: CGPoint { 35 | get { return frame.center } 36 | set { frame.center = newValue } 37 | } 38 | 39 | var centerX: CGFloat { 40 | get { return frame.centerX } 41 | set { frame.centerX = newValue } 42 | } 43 | 44 | var centerY: CGFloat { 45 | get { return frame.centerY } 46 | set { frame.centerY = newValue } 47 | } 48 | 49 | // MARK: - Size 50 | 51 | var size: CGSize { 52 | get { 53 | return frame.size 54 | } 55 | set { 56 | width = newValue.width 57 | height = newValue.height 58 | } 59 | } 60 | 61 | var width: CGFloat { 62 | get { 63 | return frame.size.width 64 | } 65 | set { 66 | frame.size.width = snapToPixel(pointCoordinate: newValue) 67 | } 68 | } 69 | 70 | var height: CGFloat { 71 | get { 72 | return frame.size.height 73 | } 74 | set { 75 | frame.size.height = snapToPixel(pointCoordinate: newValue) 76 | } 77 | } 78 | 79 | // MARK: - Edges 80 | 81 | var top: CGFloat { 82 | get { return frame.top } 83 | set { frame.top = newValue } 84 | } 85 | 86 | var right: CGFloat { 87 | get { return frame.right } 88 | set { frame.right = newValue } 89 | } 90 | 91 | var bottom: CGFloat { 92 | get { return frame.bottom } 93 | set { frame.bottom = newValue } 94 | } 95 | 96 | var left: CGFloat { 97 | get { return frame.left } 98 | set { frame.left = newValue } 99 | } 100 | 101 | // MARK: - Alternative Edges 102 | 103 | var top2: CGFloat { 104 | get { return frame.top2 } 105 | set { frame.top2 = newValue } 106 | } 107 | 108 | var right2: CGFloat { 109 | get { return frame.right2 } 110 | set { frame.right2 = newValue } 111 | } 112 | 113 | var bottom2: CGFloat { 114 | get { return frame.bottom2 } 115 | set { frame.bottom2 = newValue } 116 | } 117 | 118 | var left2: CGFloat { 119 | get { return frame.left2 } 120 | set { frame.left2 = newValue } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /Sources/Tools/ManualLayout/SmartAssign.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SmartAssign.swift 3 | // ManualLayout 4 | // 5 | // Created by Baris Sencan on 26/02/15. 6 | // Copyright (c) 2015 Baris Sencan. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | precedencegroup SmartAssignment { 12 | assignment: true 13 | } 14 | 15 | infix operator =~ : SmartAssignment 16 | 17 | @discardableResult 18 | public func =~ (point: inout CGPoint, pointTuple: (CGFloat, CGFloat)) -> CGPoint { 19 | point = CGPoint(x: pointTuple.0, y: pointTuple.1) 20 | return point 21 | } 22 | 23 | @discardableResult 24 | public func =~ (size: inout CGSize, sizeTuple: (CGFloat, CGFloat)) -> CGSize { 25 | size = CGSize(width: sizeTuple.0, height: sizeTuple.1) 26 | return size 27 | } 28 | 29 | @discardableResult 30 | public func =~ (rect: inout CGRect, rectTuple: (CGFloat, CGFloat, CGFloat, CGFloat)) -> CGRect { 31 | rect = CGRect(x: rectTuple.0, y: rectTuple.1, width: rectTuple.2, height: rectTuple.3) 32 | return rect 33 | } 34 | -------------------------------------------------------------------------------- /Sources/Tools/ManualLayout/UIScrollView+ManualLayout.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIScrollView+ManualLayout.swift 3 | // ManualLayout 4 | // 5 | // Created by Baris Sencan on 06/03/15. 6 | // Copyright (c) 2015 Baris Sencan. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public extension UIScrollView { 12 | 13 | // MARK: - Content Size 14 | 15 | var contentWidth: CGFloat { 16 | get { 17 | return contentSize.width 18 | } 19 | set { 20 | contentSize.width = snapToPixel(pointCoordinate: newValue) 21 | } 22 | } 23 | 24 | var contentHeight: CGFloat { 25 | get { 26 | return contentSize.height 27 | } 28 | set { 29 | contentSize.height = snapToPixel(pointCoordinate: newValue) 30 | } 31 | } 32 | 33 | // MARK: - Content Edges (For Convenience) 34 | 35 | var contentTop: CGFloat { 36 | return 0 37 | } 38 | 39 | var contentLeft: CGFloat { 40 | return 0 41 | } 42 | 43 | var contentBottom: CGFloat { 44 | get { 45 | return contentHeight 46 | } 47 | set { 48 | contentHeight = newValue 49 | } 50 | } 51 | 52 | var contentRight: CGFloat { 53 | get { 54 | return contentWidth 55 | } 56 | set { 57 | contentWidth = newValue 58 | } 59 | } 60 | 61 | // MARK: - Viewport Edges 62 | 63 | var viewportTop: CGFloat { 64 | get { 65 | return contentOffset.y 66 | } 67 | set { 68 | contentOffset.y = snapToPixel(pointCoordinate: newValue) 69 | } 70 | } 71 | 72 | var viewportLeft: CGFloat { 73 | get { 74 | return contentOffset.x 75 | } 76 | set { 77 | contentOffset.x = snapToPixel(pointCoordinate: newValue) 78 | } 79 | } 80 | 81 | var viewportBottom: CGFloat { 82 | get { 83 | return contentOffset.y + height 84 | } 85 | set { 86 | contentOffset.y = snapToPixel(pointCoordinate: newValue - height) 87 | } 88 | } 89 | 90 | var viewportRight: CGFloat { 91 | get { 92 | return contentOffset.x + width 93 | } 94 | set { 95 | contentOffset.x = snapToPixel(pointCoordinate: newValue - width) 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /Sources/Tools/ManualLayout/UIViewController+ManualLayout.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIViewController+ManualLayout.swift 3 | // ManualLayout 4 | // 5 | // Created by Baris Sencan on 25/02/15. 6 | // Copyright (c) 2015 Baris Sencan. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public extension UIViewController { 12 | 13 | var bounds: CGRect { 14 | return view.bounds 15 | } 16 | 17 | // MARK: - Center 18 | 19 | var center: CGPoint { 20 | return view.center 21 | } 22 | 23 | var centerX: CGFloat { 24 | return view.centerX 25 | } 26 | 27 | var centerY: CGFloat { 28 | return view.centerY 29 | } 30 | 31 | // MARK: - Size 32 | 33 | var size: CGSize { 34 | return view.size 35 | } 36 | 37 | var width: CGFloat { 38 | return view.width 39 | } 40 | 41 | var height: CGFloat { 42 | return view.height 43 | } 44 | 45 | // MARK: - Edges 46 | 47 | var top: CGFloat { 48 | return 0 49 | } 50 | 51 | var right: CGFloat { 52 | return view.width 53 | } 54 | 55 | var bottom: CGFloat { 56 | return view.height 57 | } 58 | 59 | var left: CGFloat { 60 | return 0 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Sources/Tools/ObjectMapperTransform/HexColorTransform.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HexColorTransform.swift 3 | // SwiftBilibili 4 | // 5 | // Created by luowen on 2020/9/4. 6 | // Copyright © 2020 luowen. All rights reserved. 7 | // 8 | 9 | import ObjectMapper 10 | 11 | final class HexColorTransform: TransformType { 12 | 13 | typealias Object = UIColor 14 | typealias JSON = String 15 | 16 | func transformToJSON(_ value: UIColor?) -> String? { 17 | return nil 18 | } 19 | 20 | func transformFromJSON(_ value: Any?) -> UIColor? { 21 | 22 | guard let hex = value as? String else { 23 | return nil 24 | } 25 | let color = UIColor(hex) 26 | return color 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /Sources/Tools/Protocol/AssociatedObjectStore.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AssociatedObjectStore.swift 3 | // SwiftBilibili 4 | // 5 | // Created by luowen on 2020/11/24. 6 | // Copyright © 2020 luowen. All rights reserved. 7 | // 8 | 9 | import ObjectiveC 10 | 11 | public protocol AssociatedObjectStore { } 12 | 13 | extension AssociatedObjectStore { 14 | func associatedObject(forKey key: UnsafeRawPointer) -> T? { 15 | return objc_getAssociatedObject(self, key) as AnyObject as? T 16 | } 17 | 18 | func associatedObject(forKey key: UnsafeRawPointer, default: @autoclosure () -> T, ploicy: objc_AssociationPolicy = .OBJC_ASSOCIATION_RETAIN_NONATOMIC) -> T { 19 | if let object: T = self.associatedObject(forKey: key) { 20 | return object 21 | } 22 | let object = `default`() 23 | self.setAssociatedObject(object, forKey: key, ploicy: ploicy) 24 | return object 25 | } 26 | 27 | func setAssociatedObject(_ object: T?, forKey key: UnsafeRawPointer, ploicy: objc_AssociationPolicy = .OBJC_ASSOCIATION_RETAIN_NONATOMIC) { 28 | objc_setAssociatedObject(self, key, object, ploicy) 29 | } 30 | } 31 | 32 | class WeakWrapper: NSObject { 33 | weak var obj: NSObject? 34 | } 35 | -------------------------------------------------------------------------------- /Sources/Tools/Protocol/Base.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Base.swift 3 | // SwiftBilibili 4 | // 5 | // Created by luowen on 2020/11/24. 6 | // Copyright © 2020 luowen. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public final class LWProtocolNameSpace { 12 | public let base: Base 13 | public init(_ base: Base) { 14 | self.base = base 15 | } 16 | } 17 | 18 | public protocol LWProtocolCompatible { 19 | associatedtype LWProtocolCompatibleType 20 | var lw: LWProtocolCompatibleType { get } 21 | } 22 | 23 | public extension LWProtocolCompatible { 24 | var lw: LWProtocolNameSpace { 25 | LWProtocolNameSpace(self) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Sources/Tools/Protocol/Nibloadable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Nibloadable.swift 3 | // SwiftBilibili 4 | // 5 | // Created by luowen on 2020/11/24. 6 | // Copyright © 2020 luowen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public protocol Nibloadable { 12 | static var nibIdentifier: String { get } 13 | } 14 | 15 | extension Nibloadable { 16 | static var nib: UINib { 17 | return UINib(nibName: nibIdentifier, bundle: nil) 18 | } 19 | } 20 | 21 | extension Nibloadable where Self: UIView { 22 | public static func loadFromNib(_ bundle: Bundle? = nil) -> Self { 23 | guard let view = UINib(nibName: nibIdentifier, bundle: bundle).instantiate(withOwner: nil, options: nil).first as? Self else { 24 | fatalError("Couldn't find nib file for \(String(describing: Self.self))") 25 | } 26 | return view 27 | } 28 | } 29 | 30 | extension Nibloadable where Self: UIViewController { 31 | public static func loadFromNib(_ bundle: Bundle? = nil) -> Self { 32 | return Self(nibName: nibIdentifier, bundle: bundle) 33 | } 34 | } 35 | 36 | extension Nibloadable where Self: UITableView { 37 | public static func loadFromNib(_ bundle: Bundle? = nil) -> Self { 38 | guard let tableView = UINib(nibName: nibIdentifier, bundle: bundle).instantiate(withOwner: nil, options: nil).first as? Self else { 39 | fatalError("Couldn't find nib file for \(String(describing: Self.self))") 40 | } 41 | return tableView 42 | } 43 | } 44 | 45 | extension Nibloadable where Self: UITableViewController { 46 | public static func loadFromNib(_ bundle: Bundle? = nil) -> Self { 47 | return Self(nibName: nibIdentifier, bundle: bundle) 48 | } 49 | } 50 | 51 | // MARK: 遵守协议 52 | extension UIView: Nibloadable { 53 | public static var nibIdentifier: String { 54 | return String(describing: self) 55 | } 56 | } 57 | 58 | extension UIViewController: Nibloadable { 59 | public static var nibIdentifier: String { 60 | return String(describing: self) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Sources/Tools/Theme/Theme.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Theme.swift 3 | // SwiftBilibili 4 | // 5 | // Created by luowen on 2020/9/4. 6 | // Copyright © 2020 luowen. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | import SwiftyUserDefaults 12 | import RxSwift 13 | import RxTheme 14 | 15 | enum ThemeStyle: String,DefaultsSerializable,ThemeProvider { 16 | case light = "简洁白" 17 | case dark = "夜间模式" 18 | case pink = "少女粉" 19 | 20 | var associatedObject: Theme { 21 | switch self { 22 | case .light: return LightTheme() 23 | case .dark: return DarkTheme() 24 | case .pink: return PinkTheme() 25 | } 26 | } 27 | } 28 | 29 | protocol Theme { 30 | var mainColorModel: ThemeMainColorModel { get } 31 | var isDark: Bool { get } 32 | } 33 | 34 | struct LightTheme: Theme { 35 | 36 | var mainColorModel: ThemeMainColorModel = ThemeManager.shared.lightStyleModel.colors 37 | var isDark: Bool = ThemeManager.shared.lightStyleModel.isDark 38 | 39 | } 40 | 41 | struct DarkTheme: Theme { 42 | 43 | var mainColorModel: ThemeMainColorModel = ThemeManager.shared.darkStyleModel.colors 44 | var isDark: Bool = ThemeManager.shared.darkStyleModel.isDark 45 | } 46 | 47 | struct PinkTheme: Theme { 48 | 49 | var mainColorModel: ThemeMainColorModel = ThemeManager.shared.pinkStyleModel.colors 50 | var isDark: Bool = ThemeManager.shared.pinkStyleModel.isDark 51 | } 52 | 53 | let themeService = ThemeStyle.service(initial: UserDefaultsManager.theme.style) 54 | func themed(_ mapper: @escaping ((Theme) -> T)) -> Observable { 55 | return themeService.attrStream(mapper) 56 | } 57 | -------------------------------------------------------------------------------- /Sources/Tools/Theme/ThemeManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ThemeManager.swift 3 | // SwiftBilibili 4 | // 5 | // Created by luowen on 2020/9/4. 6 | // Copyright © 2020 luowen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ThemeManager { 12 | 13 | static var shared = ThemeManager() 14 | 15 | var mainStyleModels: [ThemeMainStyleModel] = [] 16 | 17 | var currentStyleModel: ThemeMainStyleModel { 18 | switch UserDefaultsManager.theme.style { 19 | case .dark: 20 | return darkStyleModel 21 | case .light: 22 | return lightStyleModel 23 | case .pink: 24 | return pinkStyleModel 25 | } 26 | } 27 | 28 | var lightStyleModel: ThemeMainStyleModel { 29 | if _lightStyleModel == nil { 30 | _lightStyleModel = mainStyleModels.filter {$0.style == .light}.first! 31 | } 32 | return _lightStyleModel! 33 | } 34 | 35 | var darkStyleModel: ThemeMainStyleModel { 36 | if _darkStyleModel == nil { 37 | _darkStyleModel = mainStyleModels.filter {$0.style == .dark}.first! 38 | } 39 | return _darkStyleModel! 40 | } 41 | 42 | var pinkStyleModel: ThemeMainStyleModel { 43 | if _pinkStyleModel == nil { 44 | _pinkStyleModel = mainStyleModels.filter {$0.style == .pink}.first! 45 | } 46 | return _pinkStyleModel! 47 | } 48 | 49 | private var _lightStyleModel: ThemeMainStyleModel? 50 | private var _darkStyleModel: ThemeMainStyleModel? 51 | private var _pinkStyleModel: ThemeMainStyleModel? 52 | 53 | } 54 | -------------------------------------------------------------------------------- /Sources/Tools/Timer/SwiftCountDownTimer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftCountDownTimer.swift 3 | // SwiftBilibili 4 | // 5 | // Created by luowen on 2020/10/26. 6 | // Copyright © 2020 luowen. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public class SwiftCountDownTimer { 12 | 13 | private let internalTimer: SwiftTimer 14 | 15 | private var leftTimes: Int 16 | 17 | private let originalTimes: Int 18 | 19 | private let handler: (SwiftCountDownTimer, _ leftTimes: Int) -> Void 20 | 21 | /// 初始化倒计时 22 | /// - Parameters: 23 | /// - interval: 间隔 24 | /// - times: 剩余时间 25 | /// - queue: 队列 26 | /// - handler: 回调 27 | public init(interval: DispatchTimeInterval, 28 | times: Int, 29 | queue: DispatchQueue = .main, 30 | handler: @escaping (SwiftCountDownTimer, _ leftTimes: Int) -> Void ) { 31 | 32 | self.leftTimes = times 33 | self.originalTimes = times 34 | self.handler = handler 35 | self.internalTimer = SwiftTimer.repeaticTimer(interval: interval, queue: queue, handler: { _ in 36 | }) 37 | self.internalTimer.rescheduleHandler { [weak self] swiftTimer in 38 | guard let self = self else { 39 | return 40 | } 41 | if self.leftTimes > 0 { 42 | self.leftTimes -= 1 43 | self.handler(self, self.leftTimes) 44 | } else { 45 | swiftTimer.suspend() 46 | } 47 | } 48 | } 49 | 50 | public func start() { 51 | self.internalTimer.start() 52 | } 53 | 54 | public func suspend() { 55 | self.internalTimer.suspend() 56 | } 57 | 58 | public func reCountDown() { 59 | self.leftTimes = self.originalTimes 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /Sources/Utils/LaunchAdManager/LaunchAdManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LaunchAdManager.swift 3 | // SwiftBilibili 4 | // 5 | // Created by luowen on 2020/9/28. 6 | // Copyright © 2020 luowen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import RxSwift 11 | 12 | class LaunchAdManager { 13 | 14 | static let `default` = LaunchAdManager() 15 | 16 | var disposeBag = DisposeBag() 17 | 18 | var enterForegroundProtocol: NSObjectProtocol? 19 | 20 | init() { 21 | enterForegroundProtocol = NotificationCenter.default.addObserver(forName: UIApplication.willEnterForegroundNotification, object: nil, queue: OperationQueue.main) { (_) in 22 | LaunchAdManager.default.display() 23 | LaunchAdManager.default.loadSplashInfo() 24 | } 25 | } 26 | 27 | deinit { 28 | enterForegroundProtocol = nil 29 | } 30 | 31 | func display() { 32 | // 显示广告 33 | let showTime = UserDefaultsManager.splash.adShowTime 34 | let minInterval = UserDefaultsManager.splash.adMinInterval 35 | // if let cacheAdItem = AdCacheManager.default.cachedShowItem(), 36 | // Utils.currentAppTime() - showTime >= minInterval { 37 | // UserDefaultsManager.splash.adShowTime = Utils.currentAppTime() 38 | // LaunchAd.display(with: cacheAdItem, delegate: self) 39 | // } 40 | // 加载广告 41 | let loadTime = UserDefaultsManager.splash.adLoadTime 42 | let pullInterval = UserDefaultsManager.splash.adPullInterval 43 | if Utils.currentAppTime() - loadTime >= pullInterval { 44 | // ConfigAPI.adList.request() 45 | // .mapObject(AdInfoModel.self) 46 | // .subscribe(onSuccess: { (adInfo) in 47 | // UserDefaultsManager.splash.adPullInterval = adInfo.pullInterval 48 | // UserDefaultsManager.splash.adMinInterval = adInfo.minInterval 49 | // UserDefaultsManager.splash.adLoadTime = Utils.currentAppTime() 50 | // try? AdCacheManager.default.storeAdData(adInfo) 51 | // }) 52 | // .disposed(by: disposeBag) 53 | } 54 | } 55 | 56 | func loadSplashInfo() { 57 | 58 | let loadTime = UserDefaultsManager.splash.splashLoadTime 59 | let pullInterval = UserDefaultsManager.splash.splashPullInterval 60 | 61 | if Utils.currentAppTime() - loadTime >= pullInterval { 62 | // ConfigAPI.splashList 63 | // .request() 64 | // .mapObject(SplashInfoModel.self) 65 | // .subscribe(onSuccess: { (splashInfo) in 66 | // UserDefaultsManager.splash.splashLoadTime = Utils.currentAppTime() 67 | // UserDefaultsManager.splash.splashPullInterval = splashInfo.pullInterval 68 | // //try? SplashCacheManager.default.storeSplashData(splashInfo) 69 | // }) 70 | // .disposed(by: disposeBag) 71 | } 72 | } 73 | } 74 | 75 | extension LaunchAdManager: LaunchAdDelegate { 76 | 77 | } 78 | -------------------------------------------------------------------------------- /Sources/Utils/PermissionManager/PermissionManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PermissionManager.swift 3 | // SwiftBilibili 4 | // 5 | // Created by luowen on 2020/11/24. 6 | // Copyright © 2020 luowen. All rights reserved. 7 | // 8 | 9 | import Photos 10 | import AVFoundation 11 | import UserNotifications 12 | 13 | public struct PermissionManager { 14 | /// 获取相册权限 15 | static public func authorizePhotoWith(comletion: @escaping (Bool) -> Void ) { 16 | let granted = PHPhotoLibrary.authorizationStatus() 17 | switch granted { 18 | case PHAuthorizationStatus.authorized: 19 | comletion(true) 20 | case PHAuthorizationStatus.denied,PHAuthorizationStatus.restricted: 21 | comletion(false) 22 | case PHAuthorizationStatus.notDetermined: 23 | PHPhotoLibrary.requestAuthorization({ (status) in 24 | DispatchQueue.main.async { 25 | comletion(status == PHAuthorizationStatus.authorized ? true:false) 26 | } 27 | }) 28 | default: 29 | comletion(false) 30 | } 31 | } 32 | /// 获取相机权限 33 | static public func authorizeCameraWith(comletion: @escaping (Bool) -> Void) { 34 | let granted = AVCaptureDevice.authorizationStatus(for: AVMediaType.video) 35 | switch granted { 36 | case .authorized: 37 | comletion(true) 38 | case .denied: 39 | comletion(false) 40 | case .restricted: 41 | comletion(false) 42 | case .notDetermined: 43 | AVCaptureDevice.requestAccess(for: AVMediaType.video, completionHandler: { (granted) in 44 | DispatchQueue.main.async { 45 | comletion(granted) 46 | } 47 | }) 48 | default: 49 | comletion(false) 50 | } 51 | } 52 | /// 获取推送权限 53 | static public func authorizeNotificationWith(completion: @escaping (Bool) -> Void) { 54 | UNUserNotificationCenter.current().getNotificationSettings { (setting) in 55 | let granted = setting.authorizationStatus == .authorized 56 | completion(granted) 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Sources/Utils/UserDefaultsManager/DefaultsKeys+UserDefaults.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DefaultsKeys+UserDefaults.swift 3 | // SwiftBilibili 4 | // 5 | // Created by luowen on 2020/9/4. 6 | // Copyright © 2020 luowen. All rights reserved. 7 | // 8 | 9 | import SwiftyUserDefaults 10 | 11 | // MARK: Theme 12 | extension DefaultsKeys { 13 | var themeStyle: DefaultsKey { .init("themeStyle", defaultValue: .light) } 14 | } 15 | 16 | // MARK: Splash 17 | extension DefaultsKeys { 18 | var agreePolicy: DefaultsKey { .init("agreePolicy", defaultValue: false) } 19 | 20 | var splashLoadTime: DefaultsKey { .init("splashLoadTime", defaultValue: 0) } 21 | var splashPullInterval: DefaultsKey { .init("splashPullInterval", defaultValue: 0) } 22 | 23 | var adShowTime: DefaultsKey { .init("adShowTime", defaultValue: 0) } 24 | var adLoadTime: DefaultsKey { .init("adLoadTime", defaultValue: 0) } 25 | var adPullInterval: DefaultsKey { .init("adPullInterval", defaultValue: 0) } 26 | var adMinInterval: DefaultsKey { .init("adMinInterval", defaultValue: 0) } 27 | } 28 | 29 | // MARK: - App 30 | extension DefaultsKeys { 31 | var activityTabId: DefaultsKey { .init("activityTabId", defaultValue: "") } 32 | var activityTabIsClicked: DefaultsKey { .init(UserDefaultsManager.activity.activityTabId, defaultValue: false) } 33 | } 34 | 35 | // MARK: - User 36 | extension DefaultsKeys { 37 | var token: DefaultsKey { .init("token", defaultValue: "") } 38 | } 39 | -------------------------------------------------------------------------------- /Sources/Utils/UserDefaultsManager/UserDefaultsManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UserDefaultsManager.swift 3 | // SwiftBilibili 4 | // 5 | // Created by luowen on 2020/9/4. 6 | // Copyright © 2020 luowen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import SwiftyUserDefaults 11 | 12 | struct UserDefaultsManager { 13 | 14 | struct Theme { 15 | @SwiftyUserDefault(keyPath: \.themeStyle) 16 | var style: ThemeStyle 17 | } 18 | 19 | struct Splash { 20 | /// 是否同意隐私协议 21 | @SwiftyUserDefault(keyPath: \.agreePolicy) 22 | var agreePolicy: Bool 23 | /// 广告加载的时差 s作为单位 24 | @SwiftyUserDefault(keyPath: \.adPullInterval) 25 | var adPullInterval: Double 26 | /// 广告显示的时差 s作为单位 27 | @SwiftyUserDefault(keyPath: \.adMinInterval) 28 | var adMinInterval: Double 29 | /// 广告加载的时间 s作为单位 30 | @SwiftyUserDefault(keyPath: \.adLoadTime) 31 | var adLoadTime: Double 32 | /// 广告展示的时间 s作为单位 33 | @SwiftyUserDefault(keyPath: \.adShowTime) 34 | var adShowTime: Double 35 | 36 | /// 开屏加载的时差 s作为单位 37 | @SwiftyUserDefault(keyPath: \.splashPullInterval) 38 | var splashPullInterval: Double 39 | /// 开屏加载的时间 s作为单位 40 | @SwiftyUserDefault(keyPath: \.splashLoadTime) 41 | var splashLoadTime: Double 42 | 43 | } 44 | 45 | struct Activity { 46 | /// 首页活动标签Id 47 | @SwiftyUserDefault(keyPath: \.activityTabId) 48 | var activityTabId: String 49 | } 50 | 51 | struct App { 52 | /// 首页活动标签是否点击 53 | @SwiftyUserDefault(keyPath: \.activityTabIsClicked) 54 | var activityTabIsClicked: Bool 55 | } 56 | 57 | struct User { 58 | @SwiftyUserDefault(keyPath: \.token) 59 | var token: String 60 | } 61 | 62 | static var theme = Theme() 63 | static var splash = Splash() 64 | static var activity = Activity() 65 | static var app = App() 66 | static var user = User() 67 | } 68 | -------------------------------------------------------------------------------- /Sources/Utils/Utils.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Utils.swift 3 | // SwiftBilibili 4 | // 5 | // Created by luowen on 2020/9/29. 6 | // Copyright © 2020 luowen. All rights reserved. 7 | // 8 | import UIKit 9 | import SwiftDate 10 | 11 | struct Utils { 12 | /// 当前app时间 13 | static func currentAppTime() -> TimeInterval { 14 | return Date().timeIntervalSince1970 15 | } 16 | /// 是否登录 17 | static var isLogin: Bool { 18 | return !UserDefaultsManager.user.token.isEmpty 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Supporting Files/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | NSAppTransportSecurity 24 | 25 | NSAllowsArbitraryLoads 26 | 27 | 28 | UIBackgroundModes 29 | 30 | UILaunchStoryboardName 31 | LaunchScreen 32 | UIRequiredDeviceCapabilities 33 | 34 | armv7 35 | 36 | UIStatusBarHidden 37 | 38 | UISupportedInterfaceOrientations 39 | 40 | UIInterfaceOrientationPortrait 41 | 42 | UISupportedInterfaceOrientations~ipad 43 | 44 | UIInterfaceOrientationPortrait 45 | UIInterfaceOrientationPortraitUpsideDown 46 | UIInterfaceOrientationLandscapeLeft 47 | UIInterfaceOrientationLandscapeRight 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /SwiftBilibili.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /SwiftBilibili.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /SwiftBilibili.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /SwiftBilibili.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /SwiftBilibili.xcworkspace/xcuserdata/fd.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 9 | 17 | 18 | 19 | 20 | 21 | --------------------------------------------------------------------------------