├── LocalLib
├── LWNetwork
│ ├── README.md
│ ├── Classes
│ │ ├── Cache
│ │ │ ├── Single+Cache.swift
│ │ │ ├── Observable+Cache.swift
│ │ │ ├── CachingKey+Moya.swift
│ │ │ ├── Expiry.swift
│ │ │ ├── Storable.swift
│ │ │ ├── OnCache.swift
│ │ │ ├── Expirable.swift
│ │ │ └── SugarTargetType+Cache.swift
│ │ ├── MoyaSugar
│ │ │ ├── SugarTargetType.swift
│ │ │ ├── MultiSugarTarget.swift
│ │ │ ├── Route.swift
│ │ │ ├── Parameters.swift
│ │ │ └── MoyaSugarProvider.swift
│ │ └── Core
│ │ │ ├── Configuration.swift
│ │ │ ├── ActivityIndictor.swift
│ │ │ ├── NetworkError.swift
│ │ │ ├── Network.swift
│ │ │ ├── ObservableType+ObjectMapper.swift
│ │ │ ├── NetworkReachabilityManager.swift
│ │ │ └── NetworkLoggerPlugin.swift
│ ├── LICENSE
│ └── LWNetwork.podspec
├── BBNetworkEx
│ ├── README.md
│ ├── Assets
│ │ └── image.xcassets
│ │ │ ├── Contents.json
│ │ │ ├── bblive_2233_loading_error_128x94_@2x.png
│ │ │ ├── bblive_2233_loading_error_128x94_@3x.png
│ │ │ ├── loading_1.imageset
│ │ │ ├── loading_1_280x158_@2x.png
│ │ │ ├── loading_1_280x158_@3x.png
│ │ │ └── Contents.json
│ │ │ ├── loading_2.imageset
│ │ │ ├── loading_2_280x158_@2x.png
│ │ │ ├── loading_2_280x158_@3x.png
│ │ │ └── Contents.json
│ │ │ ├── loading_3.imageset
│ │ │ ├── loading_3_280x158_@2x.png
│ │ │ ├── loading_3_280x158_@3x.png
│ │ │ └── Contents.json
│ │ │ ├── bblivebc_battle_pk_panel_load_error_184x140_@2x.png
│ │ │ ├── bblivebc_battle_pk_panel_load_error_184x140_@3x.png
│ │ │ ├── load_error_1.imageset
│ │ │ ├── bblive_2233_loading_error_128x94_@2x.png
│ │ │ ├── bblive_2233_loading_error_128x94_@3x.png
│ │ │ └── Contents.json
│ │ │ └── load_error_2.imageset
│ │ │ ├── bblivebc_battle_pk_panel_load_error_184x140_@2x.png
│ │ │ ├── bblivebc_battle_pk_panel_load_error_184x140_@3x.png
│ │ │ └── Contents.json
│ ├── Classes
│ │ ├── ResourceLoader.swift
│ │ ├── CommonParameters.swift
│ │ ├── Network+Cache.swift
│ │ └── LoadingView.swift
│ ├── LICENSE
│ └── BBNetworkEx.podspec
└── LWExtensionKit
│ ├── README.md
│ ├── Classes
│ ├── Foundation
│ │ └── String
│ │ │ └── String+Extension.swift
│ ├── UIKit
│ │ ├── UIDevice
│ │ │ └── UIDevice+Extension.swift
│ │ └── UIColor
│ │ │ └── UIColor+Extension.swift
│ └── Base
│ │ └── ExtensionCompatible.swift
│ ├── LICENSE
│ └── LWExtensionKit.podspec
├── Resource
├── Assets.xcassets
│ ├── Contents.json
│ ├── Common
│ │ ├── Contents.json
│ │ ├── column_back_white_8x14_.imageset
│ │ │ ├── column_back_white_8x14_@2x.png
│ │ │ ├── column_back_white_8x14_@3x.png
│ │ │ └── Contents.json
│ │ └── arrow_right_white_24x24_.imageset
│ │ │ ├── arrow_right_white_24x24_@2x.png
│ │ │ ├── arrow_right_white_24x24_@3x.png
│ │ │ └── Contents.json
│ ├── Empty
│ │ ├── Contents.json
│ │ └── empty_coupon.imageset
│ │ │ ├── empty_coupon@2x.png
│ │ │ ├── empty_coupon@3x.png
│ │ │ └── Contents.json
│ ├── Home
│ │ ├── Contents.json
│ │ ├── top_btn_linkim_home_icon_22x22_.imageset
│ │ │ ├── top_btn_linkim_home_icon_22x22_@2x.png
│ │ │ ├── top_btn_linkim_home_icon_22x22_@3x.png
│ │ │ └── Contents.json
│ │ └── top_btn_game_centerhome_icon_22x22_.imageset
│ │ │ ├── top_btn_game_centerhome_icon_22x22_@2x.png
│ │ │ ├── top_btn_game_centerhome_icon_22x22_@3x.png
│ │ │ └── Contents.json
│ ├── Launch
│ │ ├── Contents.json
│ │ ├── launcher_logo_2020.imageset
│ │ │ ├── launcher_logo_2020@2x.png
│ │ │ ├── launcher_logo_2020@3x.png
│ │ │ └── Contents.json
│ │ ├── launcher_image_2020_0709.imageset
│ │ │ ├── launcher_image_2020_0709@2x.png
│ │ │ ├── launcher_image_2020_0709@3x.png
│ │ │ └── Contents.json
│ │ └── player_shadow_logo_40x17_.imageset
│ │ │ ├── player_shadow_logo_40x17_@2x.png
│ │ │ ├── player_shadow_logo_40x17_@3x.png
│ │ │ └── Contents.json
│ ├── Loading
│ │ ├── Contents.json
│ │ ├── loading_1_280x158_.imageset
│ │ │ ├── loading_1_280x158_@2x.png
│ │ │ ├── loading_1_280x158_@3x.png
│ │ │ └── Contents.json
│ │ ├── loading_2_280x158_.imageset
│ │ │ ├── loading_2_280x158_@2x.png
│ │ │ ├── loading_2_280x158_@3x.png
│ │ │ └── Contents.json
│ │ ├── loading_3_280x158_.imageset
│ │ │ ├── loading_3_280x158_@2x.png
│ │ │ ├── loading_3_280x158_@3x.png
│ │ │ └── Contents.json
│ │ ├── pull_loading_1_60x60_.imageset
│ │ │ ├── pull_loading_1_60x60_@2x.png
│ │ │ ├── pull_loading_1_60x60_@3x.png
│ │ │ └── Contents.json
│ │ ├── pull_loading_2_60x60_.imageset
│ │ │ ├── pull_loading_2_60x60_@2x.png
│ │ │ ├── pull_loading_2_60x60_@3x.png
│ │ │ └── Contents.json
│ │ ├── pull_loading_3_60x60_.imageset
│ │ │ ├── pull_loading_3_60x60_@2x.png
│ │ │ ├── pull_loading_3_60x60_@3x.png
│ │ │ └── Contents.json
│ │ └── pull_loading_4_60x60_.imageset
│ │ │ ├── pull_loading_4_60x60_@2x.png
│ │ │ ├── pull_loading_4_60x60_@3x.png
│ │ │ └── Contents.json
│ ├── TabBar
│ │ ├── Contents.json
│ │ ├── bottom_tabbar_mainhome_normal_22x22_.imageset
│ │ │ ├── bottom_tabbar_mainhome_normal_22x22_@2x.png
│ │ │ ├── bottom_tabbar_mainhome_normal_22x22_@3x.png
│ │ │ └── Contents.json
│ │ ├── bottom_tabbar_mallhome_normal_22x22_.imageset
│ │ │ ├── bottom_tabbar_mallhome_normal_22x22_@2x.png
│ │ │ ├── bottom_tabbar_mallhome_normal_22x22_@3x.png
│ │ │ └── Contents.json
│ │ ├── bottom_tabbar_mainhome_selected_22x22_.imageset
│ │ │ ├── bottom_tabbar_mainhome_selected_22x22_@2x.png
│ │ │ ├── bottom_tabbar_mainhome_selected_22x22_@3x.png
│ │ │ └── Contents.json
│ │ ├── bottom_tabbar_mallhome_selected_22x22_.imageset
│ │ │ ├── bottom_tabbar_mallhome_selected_22x22_@2x.png
│ │ │ ├── bottom_tabbar_mallhome_selected_22x22_@3x.png
│ │ │ └── Contents.json
│ │ ├── bottom_tabbar_user_center_normal_22x22_.imageset
│ │ │ ├── bottom_tabbar_user_center_normal_22x22_@2x.png
│ │ │ ├── bottom_tabbar_user_center_normal_22x22_@3x.png
│ │ │ └── Contents.json
│ │ ├── bottom_tabbar_followinghome_normal_22x22_.imageset
│ │ │ ├── bottom_tabbar_followinghome_normal_22x22_@2x.png
│ │ │ ├── bottom_tabbar_followinghome_normal_22x22_@3x.png
│ │ │ └── Contents.json
│ │ ├── bottom_tabbar_user_center_selected_22x22_.imageset
│ │ │ ├── bottom_tabbar_user_center_selected_22x22_@2x.png
│ │ │ ├── bottom_tabbar_user_center_selected_22x22_@3x.png
│ │ │ └── Contents.json
│ │ ├── bottom_tabbar_pegasuschannel_normal_22x22_.imageset
│ │ │ ├── bottom_tabbar_pegasuschannel_normal_22x22_@2x.png
│ │ │ ├── bottom_tabbar_pegasuschannel_normal_22x22_@3x.png
│ │ │ └── Contents.json
│ │ ├── bottom_tabbar_followinghome_selected_22x22_.imageset
│ │ │ ├── bottom_tabbar_followinghome_selected_22x22_@2x.png
│ │ │ ├── bottom_tabbar_followinghome_selected_22x22_@3x.png
│ │ │ └── Contents.json
│ │ └── bottom_tabbar_pegasuschannel_selected_22x22_.imageset
│ │ │ ├── bottom_tabbar_pegasuschannel_selected_22x22_@2x.png
│ │ │ ├── bottom_tabbar_pegasuschannel_selected_22x22_@3x.png
│ │ │ └── Contents.json
│ ├── NetError
│ │ ├── Contents.json
│ │ ├── bblive_2233_loading_error_128x94_.imageset
│ │ │ ├── bblive_2233_loading_error_128x94_@2x.png
│ │ │ ├── bblive_2233_loading_error_128x94_@3x.png
│ │ │ └── Contents.json
│ │ └── bblivebc_battle_pk_panel_load_error_184x140_.imageset
│ │ │ ├── bblivebc_battle_pk_panel_load_error_184x140_@2x.png
│ │ │ ├── bblivebc_battle_pk_panel_load_error_184x140_@3x.png
│ │ │ └── Contents.json
│ └── AppIcon.appiconset
│ │ ├── AppIcon29x29@2x.png
│ │ ├── AppIcon29x29@3x.png
│ │ ├── AppIcon40x40@2x.png
│ │ ├── AppIcon40x40@3x.png
│ │ ├── AppIcon60x60@2x.png
│ │ ├── AppIcon60x60@3x.png
│ │ ├── AppIcon29x29~ipad.png
│ │ ├── AppIcon40x40~ipad.png
│ │ ├── AppIcon76x76~ipad.png
│ │ ├── AppIcon29x29@2x~ipad.png
│ │ ├── AppIcon40x40@2x~ipad.png
│ │ ├── AppIcon76x76@2x~ipad.png
│ │ ├── AppIcon83.5x83.5@2x~ipad.png
│ │ └── Contents.json
├── Image
│ ├── launcher_logo_2020@2x.png
│ └── launcher_logo_2020@3x.png
└── Base.lproj
│ └── LaunchScreen.storyboard
├── Sources
├── Modules
│ ├── LaunchModule
│ │ ├── LaunchImages.xcassets
│ │ │ ├── Contents.json
│ │ │ ├── logo.imageset
│ │ │ │ ├── launcher_logo_2020@2x.png
│ │ │ │ ├── launcher_logo_2020@3x.png
│ │ │ │ └── Contents.json
│ │ │ └── default_splash.imageset
│ │ │ │ ├── launcher_image_2020_0709@2x.png
│ │ │ │ ├── launcher_image_2020_0709@3x.png
│ │ │ │ └── Contents.json
│ │ ├── Cache
│ │ │ └── LaunchUserDefaults.swift
│ │ ├── API
│ │ │ └── LaunchAPI.swift
│ │ └── Models
│ │ │ └── SplashInfoModel.swift
│ ├── Login
│ │ └── LoginViewController.swift
│ ├── Home
│ │ ├── Custom
│ │ │ └── SegmentedDataSource
│ │ │ │ ├── BBSegmentedTitleIOrImagetemModel.swift
│ │ │ │ └── BBSegmentedTitleOrImageDataSource.swift
│ │ ├── Controllers
│ │ │ ├── HomeLiveViewController.swift
│ │ │ ├── HomeCinemaViewController.swift
│ │ │ ├── HomeActivityViewController.swift
│ │ │ ├── HomeBangumiViewController.swift
│ │ │ ├── HomeHotViewController.swift
│ │ │ ├── HomeOptionalViewController.swift
│ │ │ └── HomePromoViewController.swift
│ │ └── Models
│ │ │ └── HomeTabModel.swift
│ ├── Channel
│ │ └── ChannelViewController.swift
│ ├── Member
│ │ └── MemberViewController.swift
│ └── Dynamic
│ │ └── DynamicViewController.swift
├── Router
│ ├── Navigator
│ │ ├── Context.swift
│ │ └── Plugin.swift
│ ├── Main
│ │ ├── RouterType.swift
│ │ ├── RouterOpener.swift
│ │ └── Router.swift
│ └── Home
│ │ ├── HomeRouterLoginPlugin.swift
│ │ ├── HomeRouter.swift
│ │ ├── HomeRouterOpener.swift
│ │ └── HomeRouterType.swift
├── Tools
│ ├── CGFloatLiteral
│ │ └── CGFloatLiteral.swift
│ ├── Protocol
│ │ ├── Base.swift
│ │ ├── AssociatedObjectStore.swift
│ │ └── Nibloadable.swift
│ ├── ObjectMapperTransform
│ │ └── HexColorTransform.swift
│ ├── ManualLayout
│ │ ├── SmartAssign.swift
│ │ ├── UIViewController+ManualLayout.swift
│ │ ├── UIScrollView+ManualLayout.swift
│ │ ├── CALayer+ManualLayout.swift
│ │ └── UIView+ManualLayout.swift
│ ├── Theme
│ │ ├── ThemeManager.swift
│ │ └── Theme.swift
│ └── Timer
│ │ └── SwiftCountDownTimer.swift
├── Const
│ ├── Const.swift
│ ├── Screen
│ │ └── Screen.swift
│ ├── Font
│ │ └── Font.swift
│ └── Image
│ │ └── Image.swift
├── Utils
│ ├── Utils.swift
│ ├── UserDefaultsManager
│ │ ├── DefaultsKeys+UserDefaults.swift
│ │ └── UserDefaultsManager.swift
│ ├── PermissionManager
│ │ └── PermissionManager.swift
│ └── LaunchAdManager
│ │ └── LaunchAdManager.swift
├── Components
│ ├── Toast
│ │ └── Toast.swift
│ └── LaunchAd
│ │ ├── LaunchAdViewController.swift
│ │ ├── LaunchAdTitleView.swift
│ │ └── LaunchAdVideoView.swift
├── Base
│ ├── BaseView.swift
│ ├── BaseNavigationController.swift
│ ├── BaseTabBarController.swift
│ └── BaseViewController.swift
├── Extensions
│ ├── UIKit
│ │ ├── UIView+Extension.swift
│ │ ├── UIImageView+Kingfisher.swift
│ │ ├── UIButton+Kingfisher.swift
│ │ ├── UIViewController+PresentModal.swift
│ │ └── UIColor+Hex.swift
│ └── Rx
│ │ └── MapAt.swift
├── AppDelegate.swift
└── Services
│ └── API
│ ├── HomeAPI.swift
│ └── ConfigAPI.swift
├── SwiftBilibili.xcodeproj
└── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ └── IDEWorkspaceChecks.plist
├── SwiftBilibili.xcworkspace
├── contents.xcworkspacedata
├── xcshareddata
│ └── IDEWorkspaceChecks.plist
└── xcuserdata
│ └── fd.xcuserdatad
│ └── xcdebugger
│ └── Breakpoints_v2.xcbkptlist
├── .gitignore
├── LICENSE
├── .swiftlint.yml
├── Supporting Files
└── Info.plist
└── Podfile
/LocalLib/LWNetwork/README.md:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/LocalLib/BBNetworkEx/README.md:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/LocalLib/LWExtensionKit/README.md:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/Resource/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Resource/Assets.xcassets/Common/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/Home/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Resource/Assets.xcassets/Launch/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Resource/Assets.xcassets/Loading/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Resource/Assets.xcassets/TabBar/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Resource/Assets.xcassets/NetError/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/LocalLib/BBNetworkEx/Assets/image.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Resource/Image/launcher_logo_2020@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowen1991/SwiftBilibili/HEAD/Resource/Image/launcher_logo_2020@2x.png
--------------------------------------------------------------------------------
/Resource/Image/launcher_logo_2020@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowen1991/SwiftBilibili/HEAD/Resource/Image/launcher_logo_2020@3x.png
--------------------------------------------------------------------------------
/Sources/Modules/LaunchModule/LaunchImages.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Resource/Assets.xcassets/AppIcon.appiconset/AppIcon29x29@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowen1991/SwiftBilibili/HEAD/Resource/Assets.xcassets/AppIcon.appiconset/AppIcon29x29@2x.png
--------------------------------------------------------------------------------
/Resource/Assets.xcassets/AppIcon.appiconset/AppIcon29x29@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowen1991/SwiftBilibili/HEAD/Resource/Assets.xcassets/AppIcon.appiconset/AppIcon29x29@3x.png
--------------------------------------------------------------------------------
/Resource/Assets.xcassets/AppIcon.appiconset/AppIcon40x40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowen1991/SwiftBilibili/HEAD/Resource/Assets.xcassets/AppIcon.appiconset/AppIcon40x40@2x.png
--------------------------------------------------------------------------------
/Resource/Assets.xcassets/AppIcon.appiconset/AppIcon40x40@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowen1991/SwiftBilibili/HEAD/Resource/Assets.xcassets/AppIcon.appiconset/AppIcon40x40@3x.png
--------------------------------------------------------------------------------
/Resource/Assets.xcassets/AppIcon.appiconset/AppIcon60x60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowen1991/SwiftBilibili/HEAD/Resource/Assets.xcassets/AppIcon.appiconset/AppIcon60x60@2x.png
--------------------------------------------------------------------------------
/Resource/Assets.xcassets/AppIcon.appiconset/AppIcon60x60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowen1991/SwiftBilibili/HEAD/Resource/Assets.xcassets/AppIcon.appiconset/AppIcon60x60@3x.png
--------------------------------------------------------------------------------
/Resource/Assets.xcassets/AppIcon.appiconset/AppIcon29x29~ipad.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowen1991/SwiftBilibili/HEAD/Resource/Assets.xcassets/AppIcon.appiconset/AppIcon29x29~ipad.png
--------------------------------------------------------------------------------
/Resource/Assets.xcassets/AppIcon.appiconset/AppIcon40x40~ipad.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowen1991/SwiftBilibili/HEAD/Resource/Assets.xcassets/AppIcon.appiconset/AppIcon40x40~ipad.png
--------------------------------------------------------------------------------
/Resource/Assets.xcassets/AppIcon.appiconset/AppIcon76x76~ipad.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowen1991/SwiftBilibili/HEAD/Resource/Assets.xcassets/AppIcon.appiconset/AppIcon76x76~ipad.png
--------------------------------------------------------------------------------
/Resource/Assets.xcassets/AppIcon.appiconset/AppIcon29x29@2x~ipad.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowen1991/SwiftBilibili/HEAD/Resource/Assets.xcassets/AppIcon.appiconset/AppIcon29x29@2x~ipad.png
--------------------------------------------------------------------------------
/Resource/Assets.xcassets/AppIcon.appiconset/AppIcon40x40@2x~ipad.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowen1991/SwiftBilibili/HEAD/Resource/Assets.xcassets/AppIcon.appiconset/AppIcon40x40@2x~ipad.png
--------------------------------------------------------------------------------
/Resource/Assets.xcassets/AppIcon.appiconset/AppIcon76x76@2x~ipad.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowen1991/SwiftBilibili/HEAD/Resource/Assets.xcassets/AppIcon.appiconset/AppIcon76x76@2x~ipad.png
--------------------------------------------------------------------------------
/Resource/Assets.xcassets/AppIcon.appiconset/AppIcon83.5x83.5@2x~ipad.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowen1991/SwiftBilibili/HEAD/Resource/Assets.xcassets/AppIcon.appiconset/AppIcon83.5x83.5@2x~ipad.png
--------------------------------------------------------------------------------
/Resource/Assets.xcassets/Empty/empty_coupon.imageset/empty_coupon@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowen1991/SwiftBilibili/HEAD/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/HEAD/Resource/Assets.xcassets/Empty/empty_coupon.imageset/empty_coupon@3x.png
--------------------------------------------------------------------------------
/LocalLib/BBNetworkEx/Assets/image.xcassets/bblive_2233_loading_error_128x94_@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowen1991/SwiftBilibili/HEAD/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/HEAD/LocalLib/BBNetworkEx/Assets/image.xcassets/bblive_2233_loading_error_128x94_@3x.png
--------------------------------------------------------------------------------
/Resource/Assets.xcassets/Launch/launcher_logo_2020.imageset/launcher_logo_2020@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowen1991/SwiftBilibili/HEAD/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/HEAD/Resource/Assets.xcassets/Launch/launcher_logo_2020.imageset/launcher_logo_2020@3x.png
--------------------------------------------------------------------------------
/Resource/Assets.xcassets/Loading/loading_1_280x158_.imageset/loading_1_280x158_@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowen1991/SwiftBilibili/HEAD/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/HEAD/Resource/Assets.xcassets/Loading/loading_1_280x158_.imageset/loading_1_280x158_@3x.png
--------------------------------------------------------------------------------
/Resource/Assets.xcassets/Loading/loading_2_280x158_.imageset/loading_2_280x158_@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowen1991/SwiftBilibili/HEAD/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/HEAD/Resource/Assets.xcassets/Loading/loading_2_280x158_.imageset/loading_2_280x158_@3x.png
--------------------------------------------------------------------------------
/Resource/Assets.xcassets/Loading/loading_3_280x158_.imageset/loading_3_280x158_@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowen1991/SwiftBilibili/HEAD/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/HEAD/Resource/Assets.xcassets/Loading/loading_3_280x158_.imageset/loading_3_280x158_@3x.png
--------------------------------------------------------------------------------
/LocalLib/BBNetworkEx/Assets/image.xcassets/loading_1.imageset/loading_1_280x158_@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowen1991/SwiftBilibili/HEAD/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/HEAD/LocalLib/BBNetworkEx/Assets/image.xcassets/loading_1.imageset/loading_1_280x158_@3x.png
--------------------------------------------------------------------------------
/LocalLib/BBNetworkEx/Assets/image.xcassets/loading_2.imageset/loading_2_280x158_@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowen1991/SwiftBilibili/HEAD/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/HEAD/LocalLib/BBNetworkEx/Assets/image.xcassets/loading_2.imageset/loading_2_280x158_@3x.png
--------------------------------------------------------------------------------
/LocalLib/BBNetworkEx/Assets/image.xcassets/loading_3.imageset/loading_3_280x158_@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowen1991/SwiftBilibili/HEAD/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/HEAD/LocalLib/BBNetworkEx/Assets/image.xcassets/loading_3.imageset/loading_3_280x158_@3x.png
--------------------------------------------------------------------------------
/Sources/Modules/LaunchModule/LaunchImages.xcassets/logo.imageset/launcher_logo_2020@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowen1991/SwiftBilibili/HEAD/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/HEAD/Sources/Modules/LaunchModule/LaunchImages.xcassets/logo.imageset/launcher_logo_2020@3x.png
--------------------------------------------------------------------------------
/LocalLib/BBNetworkEx/Assets/image.xcassets/bblivebc_battle_pk_panel_load_error_184x140_@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowen1991/SwiftBilibili/HEAD/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/HEAD/LocalLib/BBNetworkEx/Assets/image.xcassets/bblivebc_battle_pk_panel_load_error_184x140_@3x.png
--------------------------------------------------------------------------------
/Resource/Assets.xcassets/Loading/pull_loading_1_60x60_.imageset/pull_loading_1_60x60_@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowen1991/SwiftBilibili/HEAD/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/HEAD/Resource/Assets.xcassets/Loading/pull_loading_1_60x60_.imageset/pull_loading_1_60x60_@3x.png
--------------------------------------------------------------------------------
/Resource/Assets.xcassets/Loading/pull_loading_2_60x60_.imageset/pull_loading_2_60x60_@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowen1991/SwiftBilibili/HEAD/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/HEAD/Resource/Assets.xcassets/Loading/pull_loading_2_60x60_.imageset/pull_loading_2_60x60_@3x.png
--------------------------------------------------------------------------------
/Resource/Assets.xcassets/Loading/pull_loading_3_60x60_.imageset/pull_loading_3_60x60_@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowen1991/SwiftBilibili/HEAD/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/HEAD/Resource/Assets.xcassets/Loading/pull_loading_3_60x60_.imageset/pull_loading_3_60x60_@3x.png
--------------------------------------------------------------------------------
/Resource/Assets.xcassets/Loading/pull_loading_4_60x60_.imageset/pull_loading_4_60x60_@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowen1991/SwiftBilibili/HEAD/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/HEAD/Resource/Assets.xcassets/Loading/pull_loading_4_60x60_.imageset/pull_loading_4_60x60_@3x.png
--------------------------------------------------------------------------------
/Resource/Assets.xcassets/Common/column_back_white_8x14_.imageset/column_back_white_8x14_@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowen1991/SwiftBilibili/HEAD/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/HEAD/Resource/Assets.xcassets/Common/column_back_white_8x14_.imageset/column_back_white_8x14_@3x.png
--------------------------------------------------------------------------------
/Resource/Assets.xcassets/Common/arrow_right_white_24x24_.imageset/arrow_right_white_24x24_@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowen1991/SwiftBilibili/HEAD/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/HEAD/Resource/Assets.xcassets/Common/arrow_right_white_24x24_.imageset/arrow_right_white_24x24_@3x.png
--------------------------------------------------------------------------------
/Resource/Assets.xcassets/Launch/launcher_image_2020_0709.imageset/launcher_image_2020_0709@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowen1991/SwiftBilibili/HEAD/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/HEAD/Resource/Assets.xcassets/Launch/launcher_image_2020_0709.imageset/launcher_image_2020_0709@3x.png
--------------------------------------------------------------------------------
/Resource/Assets.xcassets/Launch/player_shadow_logo_40x17_.imageset/player_shadow_logo_40x17_@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowen1991/SwiftBilibili/HEAD/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/HEAD/Resource/Assets.xcassets/Launch/player_shadow_logo_40x17_.imageset/player_shadow_logo_40x17_@3x.png
--------------------------------------------------------------------------------
/SwiftBilibili.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/LocalLib/BBNetworkEx/Assets/image.xcassets/load_error_1.imageset/bblive_2233_loading_error_128x94_@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowen1991/SwiftBilibili/HEAD/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/HEAD/LocalLib/BBNetworkEx/Assets/image.xcassets/load_error_1.imageset/bblive_2233_loading_error_128x94_@3x.png
--------------------------------------------------------------------------------
/Sources/Modules/LaunchModule/LaunchImages.xcassets/default_splash.imageset/launcher_image_2020_0709@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowen1991/SwiftBilibili/HEAD/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/HEAD/Sources/Modules/LaunchModule/LaunchImages.xcassets/default_splash.imageset/launcher_image_2020_0709@3x.png
--------------------------------------------------------------------------------
/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/HEAD/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/HEAD/Resource/Assets.xcassets/Home/top_btn_linkim_home_icon_22x22_.imageset/top_btn_linkim_home_icon_22x22_@3x.png
--------------------------------------------------------------------------------
/LocalLib/BBNetworkEx/Assets/image.xcassets/load_error_2.imageset/bblivebc_battle_pk_panel_load_error_184x140_@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowen1991/SwiftBilibili/HEAD/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/HEAD/LocalLib/BBNetworkEx/Assets/image.xcassets/load_error_2.imageset/bblivebc_battle_pk_panel_load_error_184x140_@3x.png
--------------------------------------------------------------------------------
/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/HEAD/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/HEAD/Resource/Assets.xcassets/Home/top_btn_game_centerhome_icon_22x22_.imageset/top_btn_game_centerhome_icon_22x22_@3x.png
--------------------------------------------------------------------------------
/Resource/Assets.xcassets/NetError/bblive_2233_loading_error_128x94_.imageset/bblive_2233_loading_error_128x94_@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowen1991/SwiftBilibili/HEAD/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/HEAD/Resource/Assets.xcassets/NetError/bblive_2233_loading_error_128x94_.imageset/bblive_2233_loading_error_128x94_@3x.png
--------------------------------------------------------------------------------
/Resource/Assets.xcassets/TabBar/bottom_tabbar_mainhome_normal_22x22_.imageset/bottom_tabbar_mainhome_normal_22x22_@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowen1991/SwiftBilibili/HEAD/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/HEAD/Resource/Assets.xcassets/TabBar/bottom_tabbar_mainhome_normal_22x22_.imageset/bottom_tabbar_mainhome_normal_22x22_@3x.png
--------------------------------------------------------------------------------
/Resource/Assets.xcassets/TabBar/bottom_tabbar_mallhome_normal_22x22_.imageset/bottom_tabbar_mallhome_normal_22x22_@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowen1991/SwiftBilibili/HEAD/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/HEAD/Resource/Assets.xcassets/TabBar/bottom_tabbar_mallhome_normal_22x22_.imageset/bottom_tabbar_mallhome_normal_22x22_@3x.png
--------------------------------------------------------------------------------
/Resource/Assets.xcassets/TabBar/bottom_tabbar_mainhome_selected_22x22_.imageset/bottom_tabbar_mainhome_selected_22x22_@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowen1991/SwiftBilibili/HEAD/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/HEAD/Resource/Assets.xcassets/TabBar/bottom_tabbar_mainhome_selected_22x22_.imageset/bottom_tabbar_mainhome_selected_22x22_@3x.png
--------------------------------------------------------------------------------
/Resource/Assets.xcassets/TabBar/bottom_tabbar_mallhome_selected_22x22_.imageset/bottom_tabbar_mallhome_selected_22x22_@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowen1991/SwiftBilibili/HEAD/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/HEAD/Resource/Assets.xcassets/TabBar/bottom_tabbar_mallhome_selected_22x22_.imageset/bottom_tabbar_mallhome_selected_22x22_@3x.png
--------------------------------------------------------------------------------
/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/HEAD/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/HEAD/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_followinghome_normal_22x22_.imageset/bottom_tabbar_followinghome_normal_22x22_@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowen1991/SwiftBilibili/HEAD/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/HEAD/Resource/Assets.xcassets/TabBar/bottom_tabbar_followinghome_normal_22x22_.imageset/bottom_tabbar_followinghome_normal_22x22_@3x.png
--------------------------------------------------------------------------------
/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/HEAD/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/HEAD/Resource/Assets.xcassets/TabBar/bottom_tabbar_user_center_selected_22x22_.imageset/bottom_tabbar_user_center_selected_22x22_@3x.png
--------------------------------------------------------------------------------
/Resource/Assets.xcassets/TabBar/bottom_tabbar_pegasuschannel_normal_22x22_.imageset/bottom_tabbar_pegasuschannel_normal_22x22_@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowen1991/SwiftBilibili/HEAD/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/HEAD/Resource/Assets.xcassets/TabBar/bottom_tabbar_pegasuschannel_normal_22x22_.imageset/bottom_tabbar_pegasuschannel_normal_22x22_@3x.png
--------------------------------------------------------------------------------
/Resource/Assets.xcassets/TabBar/bottom_tabbar_followinghome_selected_22x22_.imageset/bottom_tabbar_followinghome_selected_22x22_@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowen1991/SwiftBilibili/HEAD/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/HEAD/Resource/Assets.xcassets/TabBar/bottom_tabbar_followinghome_selected_22x22_.imageset/bottom_tabbar_followinghome_selected_22x22_@3x.png
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/HEAD/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/HEAD/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/bottom_tabbar_pegasuschannel_selected_22x22_.imageset/bottom_tabbar_pegasuschannel_selected_22x22_@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luowen1991/SwiftBilibili/HEAD/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/HEAD/Resource/Assets.xcassets/TabBar/bottom_tabbar_pegasuschannel_selected_22x22_.imageset/bottom_tabbar_pegasuschannel_selected_22x22_@3x.png
--------------------------------------------------------------------------------
/SwiftBilibili.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/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/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/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/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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_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_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 |
--------------------------------------------------------------------------------
/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/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_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_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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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_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_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_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/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/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/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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_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_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_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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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_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_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_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_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/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/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 |
--------------------------------------------------------------------------------
/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/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/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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/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/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/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/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/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/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 |
--------------------------------------------------------------------------------
/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..
2 |
6 |
7 |
9 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/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/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/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/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/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/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/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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/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/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/Sources/Extensions/UIKit/UIImageView+Kingfisher.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIImageView+Kingfisher.swift
3 | // SwiftBilibili
4 | //
5 | // Created by luowen on 2020/9/11.
6 | // Copyright © 2020 luowen. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import Kingfisher
11 | import RxSwift
12 | import RxCocoa
13 |
14 | extension UIImageView {
15 | @discardableResult
16 | func setImage(
17 | with resource: URL?,
18 | placeholder: UIImage? = nil,
19 | completionHandler: ((Result) -> 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 |
--------------------------------------------------------------------------------
/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/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/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.
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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/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/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/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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/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/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[..
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 |
--------------------------------------------------------------------------------
/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/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/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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/LWExtensionKit/Classes/UIKit/UIColor/UIColor+Extension.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIColor+Extension.swift
3 | // LWExtensionKit
4 | //
5 | // Created by luowen on 2021/10/14.
6 | //
7 |
8 | import UIKit
9 |
10 | public extension UIColor {
11 |
12 | convenience init(hex: String) {
13 |
14 | var red: CGFloat = 0
15 | var green: CGFloat = 0
16 | var blue: CGFloat = 0
17 | var alpha: CGFloat = 1
18 | var hex = hex
19 |
20 | if hex.hasPrefix("#") {
21 | hex = String(hex.dropFirst())
22 | }
23 |
24 | let scanner = Scanner(string: hex)
25 | var hexValue: CUnsignedLongLong = 0
26 | if scanner.scanHexInt64(&hexValue) {
27 | switch hex.count {
28 | case 3:
29 | red = CGFloat((hexValue & 0xF00) >> 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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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/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/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/Sources/Tools/ManualLayout/UIView+ManualLayout.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIView+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 UIView {
12 |
13 | // MARK: - Position
14 |
15 | var origin: CGPoint {
16 | get { return layer.origin }
17 | set { layer.origin = newValue }
18 | }
19 |
20 | var x: CGFloat {
21 | get { return layer.x }
22 | set { layer.x = newValue }
23 | }
24 |
25 | var y: CGFloat {
26 | get { return layer.y }
27 | set { layer.y = newValue }
28 | }
29 |
30 | var centerX: CGFloat {
31 | get { return layer.centerX }
32 | set { layer.centerX = newValue }
33 | }
34 |
35 | var centerY: CGFloat {
36 | get { return layer.centerY }
37 | set { layer.centerY = newValue }
38 | }
39 |
40 | // MARK: - Size
41 |
42 | var size: CGSize {
43 | get { return layer.size }
44 | set { layer.size = newValue }
45 | }
46 |
47 | var width: CGFloat {
48 | get { return layer.width }
49 | set { layer.width = newValue }
50 | }
51 |
52 | var height: CGFloat {
53 | get { return layer.height }
54 | set { layer.height = newValue }
55 | }
56 |
57 | // MARK: - Edges
58 |
59 | var top: CGFloat {
60 | get { return layer.top }
61 | set { layer.top = newValue }
62 | }
63 |
64 | var right: CGFloat {
65 | get { return layer.right }
66 | set { layer.right = newValue }
67 | }
68 |
69 | var bottom: CGFloat {
70 | get { return layer.bottom }
71 | set { layer.bottom = newValue }
72 | }
73 |
74 | var left: CGFloat {
75 | get { return layer.left }
76 | set { layer.left = newValue }
77 | }
78 |
79 | // MARK: - Alternative Edges
80 |
81 | var top2: CGFloat {
82 | get { return layer.top2 }
83 | set { layer.top2 = newValue }
84 | }
85 |
86 | var right2: CGFloat {
87 | get { return layer.right2 }
88 | set { layer.right2 = newValue }
89 | }
90 |
91 | var bottom2: CGFloat {
92 | get { return layer.bottom2 }
93 | set { layer.bottom2 = newValue }
94 | }
95 |
96 | var left2: CGFloat {
97 | get { return layer.left2 }
98 | set { layer.left2 = newValue }
99 | }
100 |
101 | // MARK: - Automatic Sizing
102 |
103 | @discardableResult
104 | func sizeToFit(_ width: CGFloat, _ height: CGFloat) -> CGSize {
105 | return sizeToFit(CGSize(width: width, height: height))
106 | }
107 |
108 | @discardableResult
109 | func sizeToFit(_ constrainedSize: CGSize) -> CGSize {
110 | var newSize = sizeThatFits(constrainedSize)
111 | newSize.width = min(newSize.width, constrainedSize.width)
112 | newSize.height = min(newSize.height, constrainedSize.height)
113 | size = newSize
114 | return newSize
115 | }
116 | }
117 |
--------------------------------------------------------------------------------