杨锴
4 天以前 ac84f81ca2311300b431c1bfb9f71253b59073f2
修改部分
52个文件已修改
8个文件已删除
1,791个文件已添加
260254 ■■■■■ 已修改文件
DolphinEnglishLearnStudent.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DolphinEnglishLearnStudent.xcodeproj/xcshareddata/xcschemes/DolphinEnglishLearnStudent.xcscheme 78 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DolphinEnglishLearnStudent.xcworkspace/contents.xcworkspacedata 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DolphinEnglishLearnStudent.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DolphinEnglishLearnStudent/AppDelegate.swift 54 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DolphinEnglishLearnStudent/Assets.xcassets/Bg/bg_abc.imageset/Contents.json 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DolphinEnglishLearnStudent/Assets.xcassets/Bg/bg_card.imageset/Contents.json 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DolphinEnglishLearnStudent/Assets.xcassets/Bg/bg_login.imageset/Contents.json 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DolphinEnglishLearnStudent/Assets.xcassets/Bg/bg_logo.imageset/Contents.json 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DolphinEnglishLearnStudent/Assets.xcassets/Btn/btn_add_un.imageset/Contents.json 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DolphinEnglishLearnStudent/Assets.xcassets/Btn/btn_radio_cir.imageset/Contents.json 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DolphinEnglishLearnStudent/Assets.xcassets/Btn/btn_radio_u_cir.imageset/Contents.json 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DolphinEnglishLearnStudent/Assets.xcassets/Btn/btn_refresh.imageset/Contents.json 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DolphinEnglishLearnStudent/Assets.xcassets/Btn/placeH.imageset/Contents.json 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DolphinEnglishLearnStudent/Assets.xcassets/Icon/icon_answer.imageset/Contents.json 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DolphinEnglishLearnStudent/Assets.xcassets/Icon/icon_answer.imageset/icon_answer.png 补丁 | 查看 | 原始文档 | blame | 历史
DolphinEnglishLearnStudent/Assets.xcassets/Icon/icon_answer.imageset/icon_answer@2x.png 补丁 | 查看 | 原始文档 | blame | 历史
DolphinEnglishLearnStudent/Assets.xcassets/Icon/icon_answer.imageset/zuiba-xuanzhong@2x.png 补丁 | 查看 | 原始文档 | blame | 历史
DolphinEnglishLearnStudent/Assets.xcassets/Icon/icon_answer.imageset/zuiba-xuanzhong@3x.png 补丁 | 查看 | 原始文档 | blame | 历史
DolphinEnglishLearnStudent/Assets.xcassets/Icon/icon_play.imageset/Contents.json 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DolphinEnglishLearnStudent/Assets.xcassets/Icon/icon_play.imageset/icon_play.png 补丁 | 查看 | 原始文档 | blame | 历史
DolphinEnglishLearnStudent/Assets.xcassets/Icon/icon_play.imageset/icon_play@2x.png 补丁 | 查看 | 原始文档 | blame | 历史
DolphinEnglishLearnStudent/Assets.xcassets/Icon/icon_play.imageset/play@2x.png 补丁 | 查看 | 原始文档 | blame | 历史
DolphinEnglishLearnStudent/Assets.xcassets/Icon/icon_play.imageset/play@3x.png 补丁 | 查看 | 原始文档 | blame | 历史
DolphinEnglishLearnStudent/Assets.xcassets/Icon/icon_play_1.imageset/Contents.json 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DolphinEnglishLearnStudent/Assets.xcassets/Icon/icon_play_1.imageset/Group 4@2x.png 补丁 | 查看 | 原始文档 | blame | 历史
DolphinEnglishLearnStudent/Assets.xcassets/Icon/icon_play_1.imageset/Group 4@3x.png 补丁 | 查看 | 原始文档 | blame | 历史
DolphinEnglishLearnStudent/Assets.xcassets/Icon/icon_play_1.imageset/icon_play_1.png 补丁 | 查看 | 原始文档 | blame | 历史
DolphinEnglishLearnStudent/Assets.xcassets/Icon/icon_play_1.imageset/icon_play_1@2x.png 补丁 | 查看 | 原始文档 | blame | 历史
DolphinEnglishLearnStudent/Assets.xcassets/Icon/icon_playing.imageset/Contents.json 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DolphinEnglishLearnStudent/Assets.xcassets/Icon/icon_poker.imageset/Contents.json 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DolphinEnglishLearnStudent/Assets.xcassets/Icon/icon_question.imageset/Contents.json 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DolphinEnglishLearnStudent/Assets.xcassets/Icon/icon_question.imageset/icon_question.png 补丁 | 查看 | 原始文档 | blame | 历史
DolphinEnglishLearnStudent/Assets.xcassets/Icon/icon_question.imageset/icon_question@2x.png 补丁 | 查看 | 原始文档 | blame | 历史
DolphinEnglishLearnStudent/Assets.xcassets/Icon/icon_question.imageset/wenhao (1)@2x.png 补丁 | 查看 | 原始文档 | blame | 历史
DolphinEnglishLearnStudent/Assets.xcassets/Icon/icon_question.imageset/wenhao (1)@3x.png 补丁 | 查看 | 原始文档 | blame | 历史
DolphinEnglishLearnStudent/Assets.xcassets/Icon/icon_share.imageset/Contents.json 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DolphinEnglishLearnStudent/Assets.xcassets/Icon/icon_success.imageset/Contents.json 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DolphinEnglishLearnStudent/Assets.xcassets/Icon/icon_success_small.imageset/Contents.json 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DolphinEnglishLearnStudent/Assets.xcassets/Icon/icon_vip.imageset/Contents.json 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DolphinEnglishLearnStudent/Assets.xcassets/Icon/icon_waring.imageset/Contents.json 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DolphinEnglishLearnStudent/Assets.xcassets/Icon/icon_waring_small.imageset/Contents.json 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DolphinEnglishLearnStudent/Assets.xcassets/Icon/share_wx.imageset/Contents.json 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DolphinEnglishLearnStudent/Assets.xcassets/Icon/share_wxFri.imageset/Contents.json 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DolphinEnglishLearnStudent/Base/BaseVC.swift 174 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DolphinEnglishLearnStudent/Config/Config.swift 136 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DolphinEnglishLearnStudent/Models/CommonModel.swift 663 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DolphinEnglishLearnStudent/Moudle/Home/HomeListenFight_lesson_1_VC.swift 659 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DolphinEnglishLearnStudent/Moudle/Home/Listen/CCell/ListenFight_lesson_1_CCell.swift 88 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DolphinEnglishLearnStudent/Moudle/Home/Listen/CCell/ListenFight_lesson_1_CCell.xib 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DolphinEnglishLearnStudent/Moudle/Home/Listen/CCell/ListenFight_lesson_3_CCell.swift 88 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DolphinEnglishLearnStudent/Moudle/Home/Listen/CCell/ListenFight_lesson_3_CCell.xib 63 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DolphinEnglishLearnStudent/Moudle/Home/Listen/CCell/ListenFight_lesson_4_CCell.swift 24 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DolphinEnglishLearnStudent/Moudle/Home/Listen/CCell/ListenFight_lesson_4_CCell.xib 82 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DolphinEnglishLearnStudent/Moudle/Home/Listen/TCell/HomeListen_item_TCell.swift 40 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DolphinEnglishLearnStudent/Moudle/Home/Listen/VC/HomeListenFightVC.swift 1512 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DolphinEnglishLearnStudent/Moudle/Home/Listen/VC/HomeListenFight_lesson_2_VC.swift 776 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DolphinEnglishLearnStudent/Moudle/Home/Listen/VC/HomeListenFight_lesson_3_VC.swift 818 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DolphinEnglishLearnStudent/Moudle/Home/Listen/VC/HomeListenFight_lesson_4_VC.swift 901 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DolphinEnglishLearnStudent/Moudle/Home/Listen/VC/HomeListenFight_lesson_5_VC.swift 578 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DolphinEnglishLearnStudent/Moudle/Home/Listen/VC/HomeListenGame_1_VC.swift 628 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DolphinEnglishLearnStudent/Moudle/Home/Listen/VC/HomeListenSubVC.swift 588 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DolphinEnglishLearnStudent/Moudle/Home/Listen/VC/HomeListenVC.swift 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DolphinEnglishLearnStudent/Moudle/Home/Listen/View/Lesson_4_AnswerView.swift 23 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DolphinEnglishLearnStudent/Moudle/Home/Listen/View/Lesson_4_AnswerView.xib 73 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DolphinEnglishLearnStudent/Moudle/Home/VC/HomeStudyCompleteVC.swift 288 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DolphinEnglishLearnStudent/Moudle/Me/TCell/GoodsItemTCell.swift 168 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DolphinEnglishLearnStudent/Other/UIView/StudyHandleView.swift 20 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DolphinEnglishLearnStudent/Other/UIView/StudyHandleView.xib 17 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DolphinEnglishLearnStudent/Other/UIView/VoiceHandleView.swift 173 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DolphinEnglishLearnStudent/Services/NetworkRequest.swift 384 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DolphinEnglishLearnStudent/Services/Services.swift 768 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Podfile.lock 477 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Alamofire/LICENSE 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Alamofire/README.md 256 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Alamofire/Source/AFError.swift 874 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Alamofire/Source/Alamofire.swift 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Alamofire/Source/AlamofireExtended.swift 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Alamofire/Source/AuthenticationInterceptor.swift 402 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Alamofire/Source/CachedResponseHandler.swift 107 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Alamofire/Source/Combine.swift 652 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Alamofire/Source/Concurrency.swift 832 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Alamofire/Source/DispatchQueue+Alamofire.swift 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Alamofire/Source/EventMonitor.swift 907 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Alamofire/Source/HTTPHeaders.swift 452 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Alamofire/Source/HTTPMethod.swift 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Alamofire/Source/MultipartFormData.swift 601 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Alamofire/Source/MultipartUpload.swift 89 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Alamofire/Source/NetworkReachabilityManager.swift 292 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Alamofire/Source/Notifications.swift 115 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Alamofire/Source/OperationQueue+Alamofire.swift 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Alamofire/Source/ParameterEncoder.swift 213 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Alamofire/Source/ParameterEncoding.swift 346 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Alamofire/Source/Protected.swift 168 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Alamofire/Source/RedirectHandler.swift 111 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Alamofire/Source/Request.swift 2066 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Alamofire/Source/RequestCompression.swift 153 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Alamofire/Source/RequestInterceptor.swift 351 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Alamofire/Source/RequestTaskMap.swift 149 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Alamofire/Source/Response.swift 453 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Alamofire/Source/ResponseSerialization.swift 1270 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Alamofire/Source/Result+Alamofire.swift 120 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Alamofire/Source/RetryPolicy.swift 430 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Alamofire/Source/ServerTrustEvaluation.swift 772 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Alamofire/Source/Session.swift 1264 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Alamofire/Source/SessionDelegate.swift 355 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Alamofire/Source/StringEncoding+Alamofire.swift 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Alamofire/Source/URLConvertible+URLRequestConvertible.swift 105 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Alamofire/Source/URLEncodedFormEncoder.swift 1151 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Alamofire/Source/URLRequest+Alamofire.swift 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Alamofire/Source/URLSessionConfiguration+Alamofire.swift 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Alamofire/Source/Validation.swift 302 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/LICENSE 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/README.md 681 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/AEAD/AEAD.swift 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/AEAD/AEADChaCha20Poly1305.swift 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/AEAD/AEADXChaCha20Poly1305.swift 83 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/AES.Cryptors.swift 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/AES.swift 556 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/ASN1/ASN1.swift 87 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/ASN1/ASN1Decoder.swift 107 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/ASN1/ASN1Encoder.swift 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/ASN1/ASN1Scanner.swift 123 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/Array+Extension.swift 155 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/Authenticator.swift 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/BatchedCollection.swift 81 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/Bit.swift 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/BlockCipher.swift 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/BlockDecryptor.swift 98 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/BlockEncryptor.swift 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/BlockMode/BlockMode.swift 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/BlockMode/BlockModeOptions.swift 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/BlockMode/CBC.swift 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/BlockMode/CCM.swift 367 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/BlockMode/CFB.swift 102 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/BlockMode/CTR.swift 138 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/BlockMode/CipherModeWorker.swift 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/BlockMode/ECB.swift 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/BlockMode/GCM.swift 374 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/BlockMode/OCB.swift 398 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/BlockMode/OFB.swift 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/BlockMode/PCBC.swift 74 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/Blowfish.swift 545 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/CBCMAC.swift 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/CMAC.swift 106 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/CS_BigInt/Addition.swift 126 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/CS_BigInt/BigInt.swift 78 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/CS_BigInt/BigUInt.swift 389 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/CS_BigInt/Bitwise Ops.swift 121 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/CS_BigInt/CS.swift 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/CS_BigInt/Codable.swift 157 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/CS_BigInt/Comparable.swift 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/CS_BigInt/Data Conversion.swift 179 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/CS_BigInt/Division.swift 375 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/CS_BigInt/Exponentiation.swift 119 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/CS_BigInt/Floating Point Conversion.swift 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/CS_BigInt/GCD.swift 80 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/CS_BigInt/Hashable.swift 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/CS_BigInt/Integer Conversion.swift 89 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/CS_BigInt/Multiplication.swift 165 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/CS_BigInt/Prime Test.swift 153 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/CS_BigInt/Random.swift 101 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/CS_BigInt/Shifts.swift 211 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/CS_BigInt/Square Root.swift 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/CS_BigInt/Strideable.swift 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/CS_BigInt/String Conversion.swift 240 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/CS_BigInt/Subtraction.swift 169 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/CS_BigInt/Words and Bits.swift 202 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/ChaCha20.swift 351 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/Checksum.swift 208 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/Cipher.swift 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/Collection+Extension.swift 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/CompactMap.swift 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/Cryptor.swift 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/Cryptors.swift 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/Digest.swift 78 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/DigestType.swift 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/Foundation/AES+Foundation.swift 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/Foundation/Array+Foundation.swift 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/Foundation/Blowfish+Foundation.swift 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/Foundation/ChaCha20+Foundation.swift 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/Foundation/Data+Extension.swift 92 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/Foundation/HMAC+Foundation.swift 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/Foundation/Rabbit+Foundation.swift 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/Foundation/String+FoundationExtension.swift 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/Foundation/Utils+Foundation.swift 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/Foundation/XChaCha20+Foundation.swift 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/Generics.swift 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/HKDF.swift 92 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/HMAC.swift 124 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/ISO10126Padding.swift 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/ISO78164Padding.swift 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/Int+Extension.swift 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/MD5.swift 166 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/NoPadding.swift 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/Operators.swift 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/PEM/DER.swift 90 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/PKCS/PBKDF1.swift 101 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/PKCS/PBKDF2.swift 124 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/PKCS/PKCS1v15.swift 96 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/PKCS/PKCS5.swift 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/PKCS/PKCS7.swift 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/PKCS/PKCS7Padding.swift 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/Padding.swift 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/Poly1305.swift 165 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/RSA/RSA+Cipher.swift 128 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/RSA/RSA+Signature.swift 262 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/RSA/RSA.swift 428 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/Rabbit.swift 221 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/SHA1.swift 162 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/SHA2.swift 372 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/SHA3.swift 303 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/Scrypt.swift 265 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/SecureBytes.swift 91 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/Signature.swift 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/StreamDecryptor.swift 92 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/StreamEncryptor.swift 68 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/String+Extension.swift 98 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/UInt128.swift 90 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/UInt16+Extension.swift 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/UInt32+Extension.swift 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/UInt64+Extension.swift 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/UInt8+Extension.swift 74 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/Updatable.swift 107 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/Utils.swift 117 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/XChaCha20.swift 232 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/CryptoSwift/Sources/CryptoSwift/ZeroPadding.swift 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Differentiator/LICENSE.md 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Differentiator/README.md 187 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Differentiator/Sources/Differentiator/AnimatableSectionModel.swift 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Differentiator/Sources/Differentiator/AnimatableSectionModelType+ItemPath.swift 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Differentiator/Sources/Differentiator/AnimatableSectionModelType.swift 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Differentiator/Sources/Differentiator/Changeset.swift 90 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Differentiator/Sources/Differentiator/Diff.swift 781 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Differentiator/Sources/Differentiator/IdentifiableType.swift 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Differentiator/Sources/Differentiator/IdentifiableValue.swift 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Differentiator/Sources/Differentiator/ItemPath.swift 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Differentiator/Sources/Differentiator/Optional+Extensions.swift 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Differentiator/Sources/Differentiator/SectionModel.swift 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Differentiator/Sources/Differentiator/SectionModelType.swift 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Differentiator/Sources/Differentiator/Utilities.swift 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/EmptyDataSet-Swift/EmptyDataSet-Swift/Sources/EmptyDataSet.swift 417 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/EmptyDataSet-Swift/EmptyDataSet-Swift/Sources/EmptyDataSetDelegate.swift 143 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/EmptyDataSet-Swift/EmptyDataSet-Swift/Sources/EmptyDataSetSource.swift 153 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/EmptyDataSet-Swift/EmptyDataSet-Swift/Sources/EmptyDataSetView+Extension.swift 202 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/EmptyDataSet-Swift/EmptyDataSet-Swift/Sources/EmptyDataSetView.swift 296 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/EmptyDataSet-Swift/LICENSE 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/EmptyDataSet-Swift/README.md 237 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/FFPage/FFPage/Controller/FFAdapterViewController.h 95 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/FFPage/FFPage/Controller/FFAdapterViewController.m 720 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/FFPage/FFPage/Controller/FFPageViewController.h 129 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/FFPage/FFPage/Controller/FFPageViewController.m 490 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/FFPage/FFPage/FFPage.h 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/FFPage/FFPage/Protocol/FFPageProtocol.h 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/FFPage/FFPage/Refresh/FFRereshView.h 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/FFPage/FFPage/Refresh/FFRereshView.m 436 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/FFPage/FFPage/Utils/FFDynamicItem.h 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/FFPage/FFPage/Utils/FFDynamicItem.m 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/FFPage/FFPage/Utils/UIScrollView+FFPage.h 72 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/FFPage/FFPage/Utils/UIScrollView+FFPage.m 133 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/FFPage/LICENSE 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/FFPage/README.md 85 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/HandyJSON/LICENSE 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/HandyJSON/README.md 695 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/HandyJSON/Source/AnyExtensions.swift 89 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/HandyJSON/Source/BuiltInBasicType.swift 279 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/HandyJSON/Source/BuiltInBridgeType.swift 79 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/HandyJSON/Source/CBridge.swift 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/HandyJSON/Source/Configuration.swift 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/HandyJSON/Source/ContextDescriptorType.swift 170 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/HandyJSON/Source/CustomDateFormatTransform.swift 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/HandyJSON/Source/DataTransform.swift 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/HandyJSON/Source/DateFormatterTransform.swift 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/HandyJSON/Source/DateTransform.swift 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/HandyJSON/Source/Deserializer.swift 181 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/HandyJSON/Source/EnumTransform.swift 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/HandyJSON/Source/EnumType.swift 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/HandyJSON/Source/Export.swift 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/HandyJSON/Source/ExtendCustomBasicType.swift 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/HandyJSON/Source/ExtendCustomModelType.swift 277 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/HandyJSON/Source/FieldDescriptor.swift 97 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/HandyJSON/Source/HandyJSON.h 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/HandyJSON/Source/HelpingMapper.swift 219 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/HandyJSON/Source/HexColorTransform.swift 115 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/HandyJSON/Source/ISO8601DateTransform.swift 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/HandyJSON/Source/Logger.swift 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/HandyJSON/Source/MangledName.swift 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/HandyJSON/Source/Measuable.swift 95 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/HandyJSON/Source/Metadata.swift 332 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/HandyJSON/Source/NSDecimalNumberTransform.swift 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/HandyJSON/Source/OtherExtension.swift 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/HandyJSON/Source/PointerType.swift 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/HandyJSON/Source/Properties.swift 68 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/HandyJSON/Source/PropertyInfo.swift 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/HandyJSON/Source/ReflectionHelper.swift 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/HandyJSON/Source/Serializer.swift 84 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/HandyJSON/Source/TransformOf.swift 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/HandyJSON/Source/TransformType.swift 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/HandyJSON/Source/Transformable.swift 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/HandyJSON/Source/URLTransform.swift 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/IQKeyboardManager/IQKeyboardManager/Categories/IQNSArray+Sort.h 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/IQKeyboardManager/IQKeyboardManager/Categories/IQNSArray+Sort.m 72 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/IQKeyboardManager/IQKeyboardManager/Categories/IQUIScrollView+Additions.h 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/IQKeyboardManager/IQKeyboardManager/Categories/IQUIScrollView+Additions.m 165 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/IQKeyboardManager/IQKeyboardManager/Categories/IQUITextFieldView+Additions.h 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/IQKeyboardManager/IQKeyboardManager/Categories/IQUITextFieldView+Additions.m 92 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/IQKeyboardManager/IQKeyboardManager/Categories/IQUIView+Hierarchy.h 135 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/IQKeyboardManager/IQKeyboardManager/Categories/IQUIView+Hierarchy.m 435 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/IQKeyboardManager/IQKeyboardManager/Categories/IQUIViewController+Additions.h 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/IQKeyboardManager/IQKeyboardManager/Categories/IQUIViewController+Additions.m 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/IQKeyboardManager/IQKeyboardManager/Constants/IQKeyboardManagerConstants.h 156 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/IQKeyboardManager/IQKeyboardManager/Constants/IQKeyboardManagerConstantsInternal.h 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/IQKeyboardManager/IQKeyboardManager/IQKeyboardManager.h 359 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/IQKeyboardManager/IQKeyboardManager/IQKeyboardManager.m 2558 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/IQKeyboardManager/IQKeyboardManager/IQKeyboardReturnKeyHandler.h 98 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/IQKeyboardManager/IQKeyboardManager/IQKeyboardReturnKeyHandler.m 733 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/IQKeyboardManager/IQKeyboardManager/IQTextView/IQTextView.h 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/IQKeyboardManager/IQKeyboardManager/IQTextView/IQTextView.m 213 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/IQKeyboardManager/IQKeyboardManager/IQToolbar/IQBarButtonItem.h 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/IQKeyboardManager/IQKeyboardManager/IQToolbar/IQBarButtonItem.m 119 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/IQKeyboardManager/IQKeyboardManager/IQToolbar/IQPreviousNextView.h 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/IQKeyboardManager/IQKeyboardManager/IQToolbar/IQPreviousNextView.m 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/IQKeyboardManager/IQKeyboardManager/IQToolbar/IQTitleBarButtonItem.h 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/IQKeyboardManager/IQKeyboardManager/IQToolbar/IQTitleBarButtonItem.m 163 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/IQKeyboardManager/IQKeyboardManager/IQToolbar/IQToolbar.h 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/IQKeyboardManager/IQKeyboardManager/IQToolbar/IQToolbar.m 160 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/IQKeyboardManager/IQKeyboardManager/IQToolbar/IQUIView+IQKeyboardToolbar.h 145 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/IQKeyboardManager/IQKeyboardManager/IQToolbar/IQUIView+IQKeyboardToolbar.m 561 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/IQKeyboardManager/IQKeyboardManager/PrivacyInfo.xcprivacy 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/IQKeyboardManager/LICENSE.md 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/IQKeyboardManager/README.md 223 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/Categories/IQNSArray+Sort.swift 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/Categories/IQUIScrollView+Additions.swift 117 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/Categories/IQUITextFieldView+Additions.swift 93 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/Categories/IQUIView+Hierarchy.swift 324 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/Categories/IQUIViewController+Additions.swift 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/Constants/IQKeyboardManagerConstants.swift 150 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/Constants/IQKeyboardManagerConstantsInternal.swift 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQKeyboardManager+Debug.swift 111 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQKeyboardManager+Internal.swift 193 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQKeyboardManager+OrientationNotification.swift 77 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQKeyboardManager+Position.swift 694 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQKeyboardManager+Toolbar.swift 398 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQKeyboardManager+UIKeyboardNotification.swift 341 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQKeyboardManager+UITextFieldViewNotification.swift 230 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQKeyboardManager.swift 427 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQKeyboardReturnKeyHandler.swift 695 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQTextView/IQPlaceholderable.swift 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQTextView/IQTextView.swift 186 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQToolbar/IQBarButtonItem.swift 112 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQToolbar/IQInvocation.swift 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQToolbar/IQPreviousNextView.swift 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQToolbar/IQTitleBarButtonItem.swift 138 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQToolbar/IQToolbar.swift 172 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQToolbar/IQUIView+IQKeyboardToolbar.swift 513 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/PrivacyInfo.xcprivacy 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/IQKeyboardManagerSwift/LICENSE.md 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/IQKeyboardManagerSwift/README.md 223 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Lantern/LICENSE 206 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Lantern/README.md 104 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Lantern/Sources/Lantern/Lantern.swift 337 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Lantern/Sources/Lantern/LanternAnimatedTransitioning.swift 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Lantern/Sources/Lantern/LanternCell.swift 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Lantern/Sources/Lantern/LanternDefaultPageIndicator.swift 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Lantern/Sources/Lantern/LanternFadeAnimator.swift 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Lantern/Sources/Lantern/LanternImageCell.swift 375 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Lantern/Sources/Lantern/LanternLog.swift 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Lantern/Sources/Lantern/LanternNoneAnimator.swift 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Lantern/Sources/Lantern/LanternNumberPageIndicator.swift 67 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Lantern/Sources/Lantern/LanternPageIndicator.swift 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Lantern/Sources/Lantern/LanternPhotoVideoCell.swift 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Lantern/Sources/Lantern/LanternSmoothZoomAnimator.swift 121 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Lantern/Sources/Lantern/LanternVideoPlayer.swift 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Lantern/Sources/Lantern/LanternView.swift 271 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Lantern/Sources/Lantern/LanternZoomAnimator.swift 145 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Lantern/Sources/Lantern/LanternZoomSupportedCell.swift 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Local Podspecs/JQTools.podspec.json 83 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/MJRefresh/LICENSE 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/MJRefresh/MJRefresh/Base/MJRefreshAutoFooter.h 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/MJRefresh/MJRefresh/Base/MJRefreshAutoFooter.m 216 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/MJRefresh/MJRefresh/Base/MJRefreshBackFooter.h 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/MJRefresh/MJRefresh/Base/MJRefreshBackFooter.m 158 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/MJRefresh/MJRefresh/Base/MJRefreshComponent.h 151 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/MJRefresh/MJRefresh/Base/MJRefreshComponent.m 323 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/MJRefresh/MJRefresh/Base/MJRefreshFooter.h 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/MJRefresh/MJRefresh/Base/MJRefreshFooter.m 71 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/MJRefresh/MJRefresh/Base/MJRefreshHeader.h 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/MJRefresh/MJRefresh/Base/MJRefreshHeader.m 297 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/MJRefresh/MJRefresh/Base/MJRefreshTrailer.h 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/MJRefresh/MJRefresh/Base/MJRefreshTrailer.m 179 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/MJRefresh/MJRefresh/Custom/Footer/Auto/MJRefreshAutoGifFooter.h 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/MJRefresh/MJRefresh/Custom/Footer/Auto/MJRefreshAutoGifFooter.m 121 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/MJRefresh/MJRefresh/Custom/Footer/Auto/MJRefreshAutoNormalFooter.h 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/MJRefresh/MJRefresh/Custom/Footer/Auto/MJRefreshAutoNormalFooter.m 81 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/MJRefresh/MJRefresh/Custom/Footer/Auto/MJRefreshAutoStateFooter.h 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/MJRefresh/MJRefresh/Custom/Footer/Auto/MJRefreshAutoStateFooter.m 119 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/MJRefresh/MJRefresh/Custom/Footer/Back/MJRefreshBackGifFooter.h 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/MJRefresh/MJRefresh/Custom/Footer/Back/MJRefreshBackGifFooter.m 132 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/MJRefresh/MJRefresh/Custom/Footer/Back/MJRefreshBackNormalFooter.h 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/MJRefresh/MJRefresh/Custom/Footer/Back/MJRefreshBackNormalFooter.m 132 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/MJRefresh/MJRefresh/Custom/Footer/Back/MJRefreshBackStateFooter.h 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/MJRefresh/MJRefresh/Custom/Footer/Back/MJRefreshBackStateFooter.m 93 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/MJRefresh/MJRefresh/Custom/Header/MJRefreshGifHeader.h 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/MJRefresh/MJRefresh/Custom/Header/MJRefreshGifHeader.m 135 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/MJRefresh/MJRefresh/Custom/Header/MJRefreshNormalHeader.h 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/MJRefresh/MJRefresh/Custom/Header/MJRefreshNormalHeader.m 137 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/MJRefresh/MJRefresh/Custom/Header/MJRefreshStateHeader.h 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/MJRefresh/MJRefresh/Custom/Header/MJRefreshStateHeader.m 191 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/MJRefresh/MJRefresh/Custom/Trailer/MJRefreshNormalTrailer.h 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/MJRefresh/MJRefresh/Custom/Trailer/MJRefreshNormalTrailer.m 80 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/MJRefresh/MJRefresh/Custom/Trailer/MJRefreshStateTrailer.h 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/MJRefresh/MJRefresh/Custom/Trailer/MJRefreshStateTrailer.m 87 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/MJRefresh/MJRefresh/MJRefresh.bundle/arrow@2x.png 补丁 | 查看 | 原始文档 | blame | 历史
Pods/MJRefresh/MJRefresh/MJRefresh.bundle/en.lproj/Localizable.strings 补丁 | 查看 | 原始文档 | blame | 历史
Pods/MJRefresh/MJRefresh/MJRefresh.bundle/ko.lproj/Localizable.strings 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/MJRefresh/MJRefresh/MJRefresh.bundle/ru.lproj/Localizable.strings 补丁 | 查看 | 原始文档 | blame | 历史
Pods/MJRefresh/MJRefresh/MJRefresh.bundle/trail_arrow@2x.png 补丁 | 查看 | 原始文档 | blame | 历史
Pods/MJRefresh/MJRefresh/MJRefresh.bundle/uk.lproj/Localizable.strings 补丁 | 查看 | 原始文档 | blame | 历史
Pods/MJRefresh/MJRefresh/MJRefresh.bundle/zh-Hans.lproj/Localizable.strings 补丁 | 查看 | 原始文档 | blame | 历史
Pods/MJRefresh/MJRefresh/MJRefresh.bundle/zh-Hant.lproj/Localizable.strings 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/MJRefresh/MJRefresh/MJRefresh.h 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/MJRefresh/MJRefresh/MJRefreshConfig.h 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/MJRefresh/MJRefresh/MJRefreshConfig.m 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/MJRefresh/MJRefresh/MJRefreshConst.h 115 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/MJRefresh/MJRefresh/MJRefreshConst.m 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/MJRefresh/MJRefresh/NSBundle+MJRefresh.h 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/MJRefresh/MJRefresh/NSBundle+MJRefresh.m 116 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/MJRefresh/MJRefresh/UICollectionViewLayout+MJRefresh.h 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/MJRefresh/MJRefresh/UICollectionViewLayout+MJRefresh.m 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/MJRefresh/MJRefresh/UIScrollView+MJExtension.h 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/MJRefresh/MJRefresh/UIScrollView+MJExtension.m 153 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/MJRefresh/MJRefresh/UIScrollView+MJRefresh.h 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/MJRefresh/MJRefresh/UIScrollView+MJRefresh.m 120 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/MJRefresh/MJRefresh/UIView+MJExtension.h 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/MJRefresh/MJRefresh/UIView+MJExtension.m 83 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/MJRefresh/README.md 457 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Manifest.lock 477 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/ObjcExceptionBridging/LICENSE.txt 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/ObjcExceptionBridging/README.md 583 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/ObjcExceptionBridging/Sources/ObjcExceptionBridging/ObjectiveCMarker.m 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/ObjcExceptionBridging/Sources/ObjcExceptionBridging/include/ObjcExceptionBridging.h 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/ObjectMapper/LICENSE 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/ObjectMapper/README-CN.md 500 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/ObjectMapper/Sources/CodableTransform.swift 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/ObjectMapper/Sources/CustomDateFormatTransform.swift 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/ObjectMapper/Sources/DataTransform.swift 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/ObjectMapper/Sources/DateFormatterTransform.swift 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/ObjectMapper/Sources/DateTransform.swift 75 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/ObjectMapper/Sources/DictionaryTransform.swift 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/ObjectMapper/Sources/EnumOperators.swift 120 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/ObjectMapper/Sources/EnumTransform.swift 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/ObjectMapper/Sources/FromJSON.swift 202 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/ObjectMapper/Sources/HexColorTransform.swift 144 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/ObjectMapper/Sources/ISO8601DateTransform.swift 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/ObjectMapper/Sources/ImmutableMappable.swift 376 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/ObjectMapper/Sources/IntegerOperators.swift 171 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/ObjectMapper/Sources/Map.swift 246 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/ObjectMapper/Sources/MapError.swift 68 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/ObjectMapper/Sources/Mappable.swift 139 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/ObjectMapper/Sources/Mapper.swift 493 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/ObjectMapper/Sources/NSDecimalNumberTransform.swift 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/ObjectMapper/Sources/Operators.swift 398 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/ObjectMapper/Sources/ToJSON.swift 177 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/ObjectMapper/Sources/TransformOf.swift 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/ObjectMapper/Sources/TransformOperators.swift 709 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/ObjectMapper/Sources/TransformType.swift 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/ObjectMapper/Sources/URLTransform.swift 67 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Pods.xcodeproj/project.pbxproj 13469 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Pods.xcodeproj/xcuserdata/yvkd.xcuserdatad/xcschemes/Alamofire.xcscheme 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Pods.xcodeproj/xcuserdata/yvkd.xcuserdatad/xcschemes/CryptoSwift.xcscheme 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Pods.xcodeproj/xcuserdata/yvkd.xcuserdatad/xcschemes/Differentiator.xcscheme 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Pods.xcodeproj/xcuserdata/yvkd.xcuserdatad/xcschemes/EmptyDataSet-Swift.xcscheme 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Pods.xcodeproj/xcuserdata/yvkd.xcuserdatad/xcschemes/FFPage.xcscheme 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Pods.xcodeproj/xcuserdata/yvkd.xcuserdatad/xcschemes/HandyJSON.xcscheme 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Pods.xcodeproj/xcuserdata/yvkd.xcuserdatad/xcschemes/IQKeyboardManager.xcscheme 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Pods.xcodeproj/xcuserdata/yvkd.xcuserdatad/xcschemes/IQKeyboardManagerSwift.xcscheme 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Pods.xcodeproj/xcuserdata/yvkd.xcuserdatad/xcschemes/JQTools-JQToolsRes.xcscheme 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Pods.xcodeproj/xcuserdata/yvkd.xcuserdatad/xcschemes/JQTools.xcscheme 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Pods.xcodeproj/xcuserdata/yvkd.xcuserdatad/xcschemes/Lantern.xcscheme 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Pods.xcodeproj/xcuserdata/yvkd.xcuserdatad/xcschemes/MJRefresh.xcscheme 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Pods.xcodeproj/xcuserdata/yvkd.xcuserdatad/xcschemes/ObjcExceptionBridging.xcscheme 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Pods.xcodeproj/xcuserdata/yvkd.xcuserdatad/xcschemes/ObjectMapper.xcscheme 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Pods.xcodeproj/xcuserdata/yvkd.xcuserdatad/xcschemes/Pods-DolphinEnglishLearnStudent.xcscheme 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Pods.xcodeproj/xcuserdata/yvkd.xcuserdatad/xcschemes/QMUIKit-QMUIResources.xcscheme 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Pods.xcodeproj/xcuserdata/yvkd.xcuserdatad/xcschemes/QMUIKit.xcscheme 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Pods.xcodeproj/xcuserdata/yvkd.xcuserdatad/xcschemes/RxCocoa.xcscheme 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Pods.xcodeproj/xcuserdata/yvkd.xcuserdatad/xcschemes/RxDataSources.xcscheme 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Pods.xcodeproj/xcuserdata/yvkd.xcuserdatad/xcschemes/RxRelay.xcscheme 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Pods.xcodeproj/xcuserdata/yvkd.xcuserdatad/xcschemes/RxSwift.xcscheme 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Pods.xcodeproj/xcuserdata/yvkd.xcuserdatad/xcschemes/SDWebImage.xcscheme 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Pods.xcodeproj/xcuserdata/yvkd.xcuserdatad/xcschemes/SPPageMenu.xcscheme 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Pods.xcodeproj/xcuserdata/yvkd.xcuserdatad/xcschemes/SVProgressHUD.xcscheme 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Pods.xcodeproj/xcuserdata/yvkd.xcuserdatad/xcschemes/SnapKit.xcscheme 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Pods.xcodeproj/xcuserdata/yvkd.xcuserdatad/xcschemes/SwifterSwift.xcscheme 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Pods.xcodeproj/xcuserdata/yvkd.xcuserdatad/xcschemes/SwiftyStoreKit.xcscheme 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Pods.xcodeproj/xcuserdata/yvkd.xcuserdatad/xcschemes/TZImagePickerController.xcscheme 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Pods.xcodeproj/xcuserdata/yvkd.xcuserdatad/xcschemes/UserDefaultsStore.xcscheme 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Pods.xcodeproj/xcuserdata/yvkd.xcuserdatad/xcschemes/VTMagic.xcscheme 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Pods.xcodeproj/xcuserdata/yvkd.xcuserdatad/xcschemes/WechatOpenSDK-XCFramework.xcscheme 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Pods.xcodeproj/xcuserdata/yvkd.xcuserdatad/xcschemes/XCGLogger.xcscheme 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Pods.xcodeproj/xcuserdata/yvkd.xcuserdatad/xcschemes/xcschememanagement.plist 171 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/LICENSE.TXT 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIConfigurationTemplate/QMUIConfigurationTemplate.h 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIConfigurationTemplate/QMUIConfigurationTemplate.m 297 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/AssetLibrary/QMUIAsset.h 158 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/AssetLibrary/QMUIAsset.m 345 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/AssetLibrary/QMUIAssetsGroup.h 84 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/AssetLibrary/QMUIAssetsGroup.m 104 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/AssetLibrary/QMUIAssetsManager.h 143 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/AssetLibrary/QMUIAssetsManager.m 395 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/CAAnimation+QMUI.h 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/CAAnimation+QMUI.m 97 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/CALayer+QMUIViewAnimation.h 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/CALayer+QMUIViewAnimation.m 98 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/ImagePickerLibrary/QMUIAlbumViewController.h 107 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/ImagePickerLibrary/QMUIAlbumViewController.m 280 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/ImagePickerLibrary/QMUIImagePickerCollectionViewCell.h 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/ImagePickerLibrary/QMUIImagePickerCollectionViewCell.m 217 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/ImagePickerLibrary/QMUIImagePickerHelper.h 83 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/ImagePickerLibrary/QMUIImagePickerHelper.m 147 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/ImagePickerLibrary/QMUIImagePickerPreviewViewController.h 92 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/ImagePickerLibrary/QMUIImagePickerPreviewViewController.m 415 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/ImagePickerLibrary/QMUIImagePickerViewController.h 153 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/ImagePickerLibrary/QMUIImagePickerViewController.m 566 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/NavigationBarTransition/UINavigationBar+Transition.h 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/NavigationBarTransition/UINavigationBar+Transition.m 281 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/NavigationBarTransition/UINavigationController+NavigationBarTransition.h 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/NavigationBarTransition/UINavigationController+NavigationBarTransition.m 604 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIAlertController.h 305 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIAlertController.m 1297 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIAnimation/QMUIAnimationHelper.h 94 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIAnimation/QMUIAnimationHelper.m 239 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIAnimation/QMUIDisplayLinkAnimation.h 140 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIAnimation/QMUIDisplayLinkAnimation.m 290 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIAnimation/QMUIEasings.h 233 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIAppearance.h 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIAppearance.m 80 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIBadge/QMUIBadgeProtocol.h 79 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIBadge/UIBarItem+QMUIBadge.h 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIBadge/UIBarItem+QMUIBadge.m 313 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIBadge/UIView+QMUIBadge.h 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIBadge/UIView+QMUIBadge.m 540 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIButton/QMUIButton.h 112 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIButton/QMUIButton.m 611 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIButton/QMUINavigationButton.h 85 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIButton/QMUINavigationButton.m 535 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIButton/QMUIToolbarButton.h 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIButton/QMUIToolbarButton.m 93 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUICellHeightCache.h 182 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUICellHeightCache.m 738 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUICellHeightKeyCache/QMUICellHeightKeyCache.h 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUICellHeightKeyCache/QMUICellHeightKeyCache.m 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUICellHeightKeyCache/UITableView+QMUICellHeightKeyCache.h 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUICellHeightKeyCache/UITableView+QMUICellHeightKeyCache.m 244 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUICellSizeKeyCache/QMUICellSizeKeyCache.h 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUICellSizeKeyCache/QMUICellSizeKeyCache.m 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUICellSizeKeyCache/UICollectionView+QMUICellSizeKeyCache.h 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUICellSizeKeyCache/UICollectionView+QMUICellSizeKeyCache.m 201 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUICollectionViewPagingLayout.h 92 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUICollectionViewPagingLayout.m 310 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIConsole/QMUIConsole.h 95 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIConsole/QMUIConsole.m 148 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIConsole/QMUIConsoleToolbar.h 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIConsole/QMUIConsoleToolbar.m 156 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIConsole/QMUIConsoleViewController.h 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIConsole/QMUIConsoleViewController.m 706 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIConsole/QMUILog+QMUIConsole.h 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIConsole/QMUILog+QMUIConsole.m 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIDialogViewController.h 228 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIDialogViewController.m 963 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIEmotionInputManager.h 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIEmotionInputManager.m 142 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIEmotionView.h 141 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIEmotionView.m 640 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIEmptyView.h 93 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIEmptyView.m 303 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIFloatLayoutView.h 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIFloatLayoutView.m 119 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIGridView.h 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIGridView.m 151 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIImagePreviewView/QMUIImagePreviewView.h 97 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIImagePreviewView/QMUIImagePreviewView.m 317 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIImagePreviewView/QMUIImagePreviewViewController.h 80 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIImagePreviewView/QMUIImagePreviewViewController.m 282 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIImagePreviewView/QMUIImagePreviewViewTransitionAnimator.h 75 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIImagePreviewView/QMUIImagePreviewViewTransitionAnimator.m 219 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIKeyboardManager.h 271 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIKeyboardManager.m 1215 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUILabel.h 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUILabel.m 197 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUILog/QMUILog.h 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUILog/QMUILogItem.h 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUILog/QMUILogItem.m 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUILog/QMUILogNameManager.h 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUILog/QMUILogNameManager.m 118 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUILog/QMUILogger.h 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUILog/QMUILogger.m 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUILogManagerViewController.h 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUILogManagerViewController.m 257 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUILogger+QMUIConfigurationTemplate.h 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUILogger+QMUIConfigurationTemplate.m 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIMarqueeLabel.h 93 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIMarqueeLabel.m 314 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIModalPresentationViewController.h 325 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIModalPresentationViewController.m 844 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIMoreOperationController.h 156 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIMoreOperationController.m 742 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIMultipleDelegates/NSObject+QMUIMultipleDelegates.h 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIMultipleDelegates/NSObject+QMUIMultipleDelegates.m 161 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIMultipleDelegates/QMUIMultipleDelegates.h 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIMultipleDelegates/QMUIMultipleDelegates.m 234 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUINavigationTitleView.h 183 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUINavigationTitleView.m 838 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIOrderedDictionary.h 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIOrderedDictionary.m 155 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIPieProgressView.h 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIPieProgressView.m 190 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIPopupContainerView.h 196 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIPopupContainerView.m 970 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIPopupMenuView/QMUIPopupMenuBaseItem.h 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIPopupMenuView/QMUIPopupMenuBaseItem.m 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIPopupMenuView/QMUIPopupMenuButtonItem.h 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIPopupMenuView/QMUIPopupMenuButtonItem.m 144 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIPopupMenuView/QMUIPopupMenuItemProtocol.h 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIPopupMenuView/QMUIPopupMenuView.h 85 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIPopupMenuView/QMUIPopupMenuView.m 310 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIScrollAnimator/QMUINavigationBarScrollingAnimator.h 114 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIScrollAnimator/QMUINavigationBarScrollingAnimator.m 130 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIScrollAnimator/QMUINavigationBarScrollingSnapAnimator.h 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIScrollAnimator/QMUINavigationBarScrollingSnapAnimator.m 94 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIScrollAnimator/QMUIScrollAnimator.h 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIScrollAnimator/QMUIScrollAnimator.m 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUISearchBar.h 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUISearchBar.m 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUISearchController.h 145 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUISearchController.m 427 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUISegmentedControl.h 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUISegmentedControl.m 117 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUITableView.h 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUITableView.m 72 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUITableViewCell.h 97 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUITableViewCell.m 337 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUITableViewHeaderFooterView.h 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUITableViewHeaderFooterView.m 143 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUITableViewProtocols.h 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUITestView.h 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUITestView.m 155 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUITextField.h 104 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUITextField.m 302 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUITextView.h 119 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUITextView.m 486 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUITheme/QMUITheme.h 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUITheme/QMUIThemeManager.h 120 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUITheme/QMUIThemeManager.m 150 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUITheme/QMUIThemeManagerCenter.h 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUITheme/QMUIThemeManagerCenter.m 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUITheme/QMUIThemePrivate.h 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUITheme/QMUIThemePrivate.m 539 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUITheme/UIColor+QMUITheme.h 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUITheme/UIColor+QMUITheme.m 194 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUITheme/UIImage+QMUITheme.h 81 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUITheme/UIImage+QMUITheme.m 563 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUITheme/UIView+QMUITheme.h 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUITheme/UIView+QMUITheme.m 226 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUITheme/UIViewController+QMUITheme.h 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUITheme/UIViewController+QMUITheme.m 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUITheme/UIVisualEffect+QMUITheme.h 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUITheme/UIVisualEffect+QMUITheme.m 140 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUITips.h 112 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUITips.m 320 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIWeakObjectContainer.h 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIWeakObjectContainer.m 99 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIWindowSizeMonitor.h 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIWindowSizeMonitor.m 213 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIZoomImageView.h 176 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIZoomImageView.m 1138 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/StaticTableView/QMUIStaticTableViewCellData.h 120 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/StaticTableView/QMUIStaticTableViewCellData.m 107 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/StaticTableView/QMUIStaticTableViewCellDataSource.h 83 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/StaticTableView/QMUIStaticTableViewCellDataSource.m 199 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/StaticTableView/UITableView+QMUIStaticCell.h 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/StaticTableView/UITableView+QMUIStaticCell.m 143 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/ToastView/QMUIToastAnimator.h 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/ToastView/QMUIToastAnimator.m 233 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/ToastView/QMUIToastBackgroundView.h 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/ToastView/QMUIToastBackgroundView.m 96 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/ToastView/QMUIToastContentView.h 86 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/ToastView/QMUIToastContentView.m 249 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/ToastView/QMUIToastView.h 175 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIComponents/ToastView/QMUIToastView.m 361 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUICore/QMUICommonDefines.h 840 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUICore/QMUIConfiguration.h 326 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUICore/QMUIConfiguration.m 1083 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUICore/QMUIConfigurationMacros.h 278 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUICore/QMUICore.h 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUICore/QMUIHelper.h 273 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUICore/QMUIHelper.m 1102 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUICore/QMUILab.h 151 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUICore/QMUIRuntime.h 344 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUICore/QMUIRuntime.m 219 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIKit.h 662 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIMainFrame/QMUICommonTableViewController.h 97 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIMainFrame/QMUICommonTableViewController.m 323 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIMainFrame/QMUICommonViewController.h 191 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIMainFrame/QMUICommonViewController.m 343 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIMainFrame/QMUINavigationController.h 180 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIMainFrame/QMUINavigationController.m 650 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIMainFrame/QMUITabBarViewController.h 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIMainFrame/QMUITabBarViewController.m 93 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIResources/Images.xcassets/Contents.json 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIResources/Images.xcassets/QMUI_console_clear.imageset/Contents.json 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIResources/Images.xcassets/QMUI_console_clear.imageset/QMUI_console_clear.pdf 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIResources/Images.xcassets/QMUI_console_filter.imageset/Contents.json 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIResources/Images.xcassets/QMUI_console_filter.imageset/QMUI_console_filter.pdf 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIResources/Images.xcassets/QMUI_console_filter_selected.imageset/Contents.json 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIResources/Images.xcassets/QMUI_console_filter_selected.imageset/QMUI_console_filter_selected.pdf 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIResources/Images.xcassets/QMUI_console_logo.imageset/Contents.json 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIResources/Images.xcassets/QMUI_console_logo.imageset/QMUI_console_logo.pdf 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIResources/Images.xcassets/QMUI_emotion_delete.imageset/Contents.json 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIResources/Images.xcassets/QMUI_emotion_delete.imageset/QMUI_emotion_delete.pdf 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIResources/Images.xcassets/QMUI_hiddenAlbum.imageset/Contents.json 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIResources/Images.xcassets/QMUI_hiddenAlbum.imageset/QMUI_hiddenAlbum.pdf 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIResources/Images.xcassets/QMUI_icloud_download_fault.imageset/Contents.json 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIResources/Images.xcassets/QMUI_icloud_download_fault.imageset/QMUI_icloud_download_fault.pdf 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIResources/Images.xcassets/QMUI_pickerImage_checkbox.imageset/Contents.json 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIResources/Images.xcassets/QMUI_pickerImage_checkbox.imageset/QMUI_pickerImage_checkbox.pdf 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIResources/Images.xcassets/QMUI_pickerImage_checkbox_checked.imageset/Contents.json 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIResources/Images.xcassets/QMUI_pickerImage_checkbox_checked.imageset/QMUI_pickerImage_checkbox_checked.pdf 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIResources/Images.xcassets/QMUI_pickerImage_favorite.imageset/Contents.json 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIResources/Images.xcassets/QMUI_pickerImage_favorite.imageset/QMUI_pickerImage_favorite.pdf 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIResources/Images.xcassets/QMUI_pickerImage_video_mark.imageset/Contents.json 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIResources/Images.xcassets/QMUI_pickerImage_video_mark.imageset/QMUI_pickerImage_video_mark.pdf 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIResources/Images.xcassets/QMUI_previewImage_checkbox.imageset/Contents.json 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIResources/Images.xcassets/QMUI_previewImage_checkbox.imageset/QMUI_previewImage_checkbox.pdf 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIResources/Images.xcassets/QMUI_previewImage_checkbox_checked.imageset/Contents.json 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIResources/Images.xcassets/QMUI_previewImage_checkbox_checked.imageset/QMUI_previewImage_checkbox_checked.pdf 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIResources/Images.xcassets/QMUI_tips_done.imageset/Contents.json 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIResources/Images.xcassets/QMUI_tips_done.imageset/QMUI_tips_done.pdf 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIResources/Images.xcassets/QMUI_tips_error.imageset/Contents.json 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIResources/Images.xcassets/QMUI_tips_error.imageset/QMUI_tips_error.pdf 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIResources/Images.xcassets/QMUI_tips_info.imageset/Contents.json 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/QMUIResources/Images.xcassets/QMUI_tips_info.imageset/QMUI_tips_info.pdf 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/CALayer+QMUI.h 141 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/CALayer+QMUI.m 475 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/NSArray+QMUI.h 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/NSArray+QMUI.m 125 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/NSAttributedString+QMUI.h 104 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/NSAttributedString+QMUI.m 183 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/NSCharacterSet+QMUI.h 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/NSCharacterSet+QMUI.m 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/NSDictionary+QMUI.h 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/NSDictionary+QMUI.m 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/NSMethodSignature+QMUI.h 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/NSMethodSignature+QMUI.m 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/NSNumber+QMUI.h 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/NSNumber+QMUI.m 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/NSObject+QMUI.h 318 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/NSObject+QMUI.m 531 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/NSParagraphStyle+QMUI.h 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/NSParagraphStyle+QMUI.m 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/NSPointerArray+QMUI.h 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/NSPointerArray+QMUI.m 60 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/NSShadow+QMUI.h 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/NSShadow+QMUI.m 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/NSString+QMUI.h 186 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/NSString+QMUI.m 270 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/NSURL+QMUI.h 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/NSURL+QMUI.m 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/QMUIBarProtocol/QMUIBarProtocol.h 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/QMUIBarProtocol/QMUIBarProtocolPrivate.h 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/QMUIBarProtocol/QMUIBarProtocolPrivate.m 84 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/QMUIBarProtocol/UINavigationBar+QMUIBarProtocol.h 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/QMUIBarProtocol/UINavigationBar+QMUIBarProtocol.m 119 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/QMUIBarProtocol/UITabBar+QMUIBarProtocol.h 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/QMUIBarProtocol/UITabBar+QMUIBarProtocol.m 119 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/QMUIStringPrivate.h 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/QMUIStringPrivate.m 346 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/UIActivityIndicatorView+QMUI.h 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/UIActivityIndicatorView+QMUI.m 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/UIApplication+QMUI.h 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/UIApplication+QMUI.m 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/UIBarItem+QMUI.h 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/UIBarItem+QMUI.m 122 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/UIBezierPath+QMUI.h 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/UIBezierPath+QMUI.m 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/UIBlurEffect+QMUI.h 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/UIBlurEffect+QMUI.m 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/UIButton+QMUI.h 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/UIButton+QMUI.m 235 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/UICollectionView+QMUI.h 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/UICollectionView+QMUI.m 124 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/UICollectionViewCell+QMUI.h 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/UICollectionViewCell+QMUI.m 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/UIColor+QMUI.h 205 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/UIColor+QMUI.m 377 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/UIControl+QMUI.h 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/UIControl+QMUI.m 310 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/UIFont+QMUI.h 103 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/UIFont+QMUI.m 82 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/UIGestureRecognizer+QMUI.h 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/UIGestureRecognizer+QMUI.m 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/UIImage+QMUI.h 406 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/UIImage+QMUI.m 797 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/UIImageView+QMUI.h 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/UIImageView+QMUI.m 225 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/UIInterface+QMUI.h 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/UIInterface+QMUI.m 222 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/UILabel+QMUI.h 106 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/UILabel+QMUI.m 344 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/UIMenuController+QMUI.h 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/UIMenuController+QMUI.m 124 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/UINavigationBar+QMUI.h 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/UINavigationBar+QMUI.m 377 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/UINavigationController+QMUI.h 117 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/UINavigationController+QMUI.m 642 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/UINavigationItem+QMUI.h 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/UINavigationItem+QMUI.m 68 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/UIScrollView+QMUI.h 82 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/UIScrollView+QMUI.m 163 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/UISearchBar+QMUI.h 108 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/UISearchBar+QMUI.m 872 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/UISearchController+QMUI.h 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/UISearchController+QMUI.m 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/UISlider+QMUI.h 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/UISlider+QMUI.m 476 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/UISwitch+QMUI.h 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/UISwitch+QMUI.m 81 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/UITabBar+QMUI.h 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/UITabBar+QMUI.m 299 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/UITabBarItem+QMUI.h 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/UITabBarItem+QMUI.m 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/UITableView+QMUI.h 148 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/UITableView+QMUI.m 613 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/UITableViewCell+QMUI.h 96 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/UITableViewCell+QMUI.m 491 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/UITableViewHeaderFooterView+QMUI.h 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/UITableViewHeaderFooterView+QMUI.m 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/UITextField+QMUI.h 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/UITextField+QMUI.m 122 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/UITextInputTraits+QMUI.h 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/UITextInputTraits+QMUI.m 109 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/UITextView+QMUI.h 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/UITextView+QMUI.m 112 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/UIToolbar+QMUI.h 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/UIToolbar+QMUI.m 134 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/UITraitCollection+QMUI.h 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/UITraitCollection+QMUI.m 119 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/UIView+QMUI.h 262 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/UIView+QMUI.m 780 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/UIView+QMUIBorder.h 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/UIView+QMUIBorder.m 353 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/UIViewController+QMUI.h 203 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/UIViewController+QMUI.m 583 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/UIVisualEffectView+QMUI.h 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/UIVisualEffectView+QMUI.m 151 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/UIWindow+QMUI.h 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/QMUIKit/UIKitExtensions/UIWindow+QMUI.m 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/QMUIKit/README.md 72 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/LICENSE.md 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/Platform/DataStructures/Bag.swift 181 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/Platform/DataStructures/InfiniteSequence.swift 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/Platform/DataStructures/PriorityQueue.swift 111 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/Platform/DataStructures/Queue.swift 148 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/Platform/DispatchQueue+Extensions.swift 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/Platform/Platform.Darwin.swift 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/Platform/Platform.Linux.swift 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/Platform/RecursiveLock.swift 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/README.md 254 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/Common/ControlTarget.swift 88 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/Common/DelegateProxy.swift 294 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/Common/DelegateProxyType.swift 435 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/Common/Infallible+Bind.swift 148 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/Common/Observable+Bind.swift 103 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/Common/RxCocoaObjCRuntimeError+Extensions.swift 161 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/Common/RxTarget.swift 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/Common/SectionedViewDataSourceType.swift 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/Common/TextInput.swift 78 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/Foundation/KVORepresentable+CoreGraphics.swift 68 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/Foundation/KVORepresentable+Swift.swift 88 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/Foundation/KVORepresentable.swift 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/Foundation/NSObject+Rx+KVORepresentable.swift 60 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/Foundation/NSObject+Rx+RawRepresentable.swift 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/Foundation/NSObject+Rx.swift 569 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/Foundation/NotificationCenter+Rx.swift 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/Foundation/URLSession+Rx.swift 240 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/Runtime/_RX.m 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/Runtime/_RXDelegateProxy.m 147 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/Runtime/_RXKVOObserver.m 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/Runtime/_RXObjCRuntime.m 1062 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/Runtime/include/RxCocoaRuntime.h 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/Runtime/include/_RX.h 93 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/Runtime/include/_RXDelegateProxy.h 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/Runtime/include/_RXKVOObserver.h 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/Runtime/include/_RXObjCRuntime.h 102 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/RxCocoa.h 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/RxCocoa.swift 155 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/Traits/ControlEvent.swift 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/Traits/ControlProperty.swift 118 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/Traits/Driver/BehaviorRelay+Driver.swift 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/Traits/Driver/ControlEvent+Driver.swift 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/Traits/Driver/ControlProperty+Driver.swift 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/Traits/Driver/Driver+Subscription.swift 203 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/Traits/Driver/Driver.swift 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/Traits/Driver/Infallible+Driver.swift 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/Traits/Driver/ObservableConvertibleType+Driver.swift 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/Traits/SharedSequence/ObservableConvertibleType+SharedSequence.swift 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/Traits/SharedSequence/SchedulerType+SharedSequence.swift 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/Traits/SharedSequence/SharedSequence+Concurrency.swift 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/Traits/SharedSequence/SharedSequence+Operators+arity.swift 656 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/Traits/SharedSequence/SharedSequence+Operators.swift 584 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/Traits/SharedSequence/SharedSequence.swift 231 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/Traits/Signal/ControlEvent+Signal.swift 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/Traits/Signal/ObservableConvertibleType+Signal.swift 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/Traits/Signal/PublishRelay+Signal.swift 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/Traits/Signal/Signal+Subscription.swift 178 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/Traits/Signal/Signal.swift 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/iOS/DataSources/RxCollectionViewReactiveArrayDataSource.swift 108 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/iOS/DataSources/RxPickerViewAdapter.swift 92 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/iOS/DataSources/RxTableViewReactiveArrayDataSource.swift 101 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/iOS/Events/ItemEvents.swift 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/iOS/NSTextStorage+Rx.swift 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/iOS/Protocols/RxCollectionViewDataSourceType.swift 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/iOS/Protocols/RxPickerViewDataSourceType.swift 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/iOS/Protocols/RxTableViewDataSourceType.swift 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/iOS/Proxies/RxCollectionViewDataSourcePrefetchingProxy.swift 92 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/iOS/Proxies/RxCollectionViewDataSourceProxy.swift 75 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/iOS/Proxies/RxCollectionViewDelegateProxy.swift 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/iOS/Proxies/RxDelegateProxyCrashFix.swift 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/iOS/Proxies/RxNavigationControllerDelegateProxy.swift 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/iOS/Proxies/RxPickerViewDataSourceProxy.swift 72 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/iOS/Proxies/RxPickerViewDelegateProxy.swift 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/iOS/Proxies/RxScrollViewDelegateProxy.swift 91 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/iOS/Proxies/RxSearchBarDelegateProxy.swift 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/iOS/Proxies/RxSearchControllerDelegateProxy.swift 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/iOS/Proxies/RxTabBarControllerDelegateProxy.swift 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/iOS/Proxies/RxTabBarDelegateProxy.swift 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/iOS/Proxies/RxTableViewDataSourcePrefetchingProxy.swift 93 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/iOS/Proxies/RxTableViewDataSourceProxy.swift 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/iOS/Proxies/RxTableViewDelegateProxy.swift 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/iOS/Proxies/RxTextStorageDelegateProxy.swift 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/iOS/Proxies/RxTextViewDelegateProxy.swift 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/iOS/Proxies/RxWKNavigationDelegateProxy.swift 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/iOS/UIActivityIndicatorView+Rx.swift 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/iOS/UIApplication+Rx.swift 106 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/iOS/UIBarButtonItem+Rx.swift 72 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/iOS/UIButton+Rx.swift 82 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/iOS/UICollectionView+Rx.swift 380 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/iOS/UIControl+Rx.swift 86 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/iOS/UIDatePicker+Rx.swift 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/iOS/UIGestureRecognizer+Rx.swift 75 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/iOS/UINavigationController+Rx.swift 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/iOS/UIPickerView+Rx.swift 224 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/iOS/UIRefreshControl+Rx.swift 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/iOS/UIScrollView+Rx.swift 131 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/iOS/UISearchBar+Rx.swift 136 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/iOS/UISearchController+Rx.swift 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/iOS/UISegmentedControl+Rx.swift 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/iOS/UISlider+Rx.swift 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/iOS/UIStepper+Rx.swift 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/iOS/UISwitch+Rx.swift 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/iOS/UITabBar+Rx.swift 92 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/iOS/UITabBarController+Rx.swift 79 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/iOS/UITableView+Rx.swift 429 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/iOS/UITextField+Rx.swift 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/iOS/UITextView+Rx.swift 125 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/iOS/WKWebView+Rx.swift 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/macOS/NSButton+Rx.swift 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/macOS/NSControl+Rx.swift 87 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/macOS/NSSlider+Rx.swift 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/macOS/NSTextField+Rx.swift 87 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/macOS/NSTextView+Rx.swift 94 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxCocoa/RxCocoa/macOS/NSView+Rx.swift 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxDataSources/LICENSE.md 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxDataSources/README.md 187 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxDataSources/Sources/RxDataSources/AnimationConfiguration.swift 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxDataSources/Sources/RxDataSources/Array+Extensions.swift 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxDataSources/Sources/RxDataSources/CollectionViewSectionedDataSource.swift 169 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxDataSources/Sources/RxDataSources/DataSources.swift 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxDataSources/Sources/RxDataSources/Deprecated.swift 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxDataSources/Sources/RxDataSources/FloatingPointType+IdentifiableType.swift 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxDataSources/Sources/RxDataSources/IntegerType+IdentifiableType.swift 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxDataSources/Sources/RxDataSources/RxCollectionViewSectionedAnimatedDataSource.swift 101 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxDataSources/Sources/RxDataSources/RxCollectionViewSectionedReloadDataSource.swift 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxDataSources/Sources/RxDataSources/RxPickerViewAdapter.swift 233 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxDataSources/Sources/RxDataSources/RxTableViewSectionedAnimatedDataSource.swift 133 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxDataSources/Sources/RxDataSources/RxTableViewSectionedReloadDataSource.swift 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxDataSources/Sources/RxDataSources/String+IdentifiableType.swift 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxDataSources/Sources/RxDataSources/TableViewSectionedDataSource.swift 227 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxDataSources/Sources/RxDataSources/UI+SectionedViewType.swift 136 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxDataSources/Sources/RxDataSources/ViewTransition.swift 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxRelay/LICENSE.md 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxRelay/README.md 254 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxRelay/RxRelay/BehaviorRelay.swift 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxRelay/RxRelay/Observable+Bind.swift 149 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxRelay/RxRelay/PublishRelay.swift 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxRelay/RxRelay/ReplayRelay.swift 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxRelay/RxRelay/Utils.swift 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/LICENSE.md 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/Platform/AtomicInt.swift 71 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/Platform/DataStructures/Bag.swift 181 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/Platform/DataStructures/InfiniteSequence.swift 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/Platform/DataStructures/PriorityQueue.swift 111 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/Platform/DataStructures/Queue.swift 148 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/Platform/DispatchQueue+Extensions.swift 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/Platform/Platform.Darwin.swift 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/Platform/Platform.Linux.swift 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/Platform/RecursiveLock.swift 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/README.md 254 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/AnyObserver.swift 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Binder.swift 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Cancelable.swift 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Concurrency/AsyncLock.swift 100 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Concurrency/Lock.swift 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Concurrency/LockOwnerType.swift 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Concurrency/SynchronizedDisposeType.swift 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Concurrency/SynchronizedOnType.swift 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Concurrency/SynchronizedUnsubscribeType.swift 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/ConnectableObservableType.swift 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Date+Dispatch.swift 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Disposable.swift 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Disposables/AnonymousDisposable.swift 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Disposables/BinaryDisposable.swift 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Disposables/BooleanDisposable.swift 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Disposables/CompositeDisposable.swift 147 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Disposables/Disposables.swift 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Disposables/DisposeBag.swift 144 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Disposables/DisposeBase.swift 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Disposables/NopDisposable.swift 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Disposables/RefCountDisposable.swift 112 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Disposables/ScheduledDisposable.swift 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Disposables/SerialDisposable.swift 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Disposables/SingleAssignmentDisposable.swift 72 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Disposables/SubscriptionDisposable.swift 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Errors.swift 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Event.swift 104 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Extensions/Bag+Rx.swift 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/GroupedObservable.swift 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/ImmediateSchedulerType.swift 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Observable+Concurrency.swift 79 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Observable.swift 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/ObservableConvertibleType.swift 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/ObservableType+Extensions.swift 174 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/ObservableType.swift 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Observables/AddRef.swift 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Observables/Amb.swift 157 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Observables/AsMaybe.swift 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Observables/AsSingle.swift 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Observables/Buffer.swift 138 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Observables/Catch.swift 275 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Observables/CombineLatest+Collection.swift 165 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Observables/CombineLatest+arity.swift 843 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Observables/CombineLatest.swift 131 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Observables/CompactMap.swift 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Observables/Concat.swift 131 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Observables/Create.swift 78 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Observables/Debounce.swift 119 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Observables/Debug.swift 102 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Observables/Decode.swift 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Observables/DefaultIfEmpty.swift 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Observables/Deferred.swift 75 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Observables/Delay.swift 174 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Observables/DelaySubscription.swift 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Observables/Dematerialize.swift 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Observables/DistinctUntilChanged.swift 137 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Observables/Do.swift 112 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Observables/ElementAt.swift 105 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Observables/Empty.swift 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Observables/Enumerated.swift 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Observables/Error.swift 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Observables/Filter.swift 86 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Observables/First.swift 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Observables/Generate.swift 87 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Observables/GroupBy.swift 133 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Observables/Just.swift 87 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Observables/Map.swift 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Observables/Materialize.swift 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Observables/Merge.swift 600 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Observables/Multicast.swift 405 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Observables/Never.swift 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Observables/ObserveOn.swift 243 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Observables/Optional.swift 95 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Observables/Producer.swift 92 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Observables/Range.swift 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Observables/Reduce.swift 109 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Observables/Repeat.swift 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Observables/RetryWhen.swift 211 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Observables/Sample.swift 139 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Observables/Scan.swift 100 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Observables/Sequence.swift 89 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Observables/ShareReplayScope.swift 443 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Observables/SingleAsync.swift 104 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Observables/Sink.swift 75 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Observables/Skip.swift 158 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Observables/SkipUntil.swift 152 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Observables/SkipWhile.swift 87 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Observables/StartWith.swift 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Observables/SubscribeOn.swift 103 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Observables/Switch.swift 251 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Observables/SwitchIfEmpty.swift 104 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Observables/Take.swift 193 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Observables/TakeLast.swift 78 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Observables/TakeWithPredicate.swift 285 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Observables/Throttle.swift 160 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Observables/Timeout.swift 151 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Observables/Timer.swift 117 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Observables/ToArray.swift 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Observables/Using.swift 90 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Observables/Window.swift 168 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Observables/WithLatestFrom.swift 151 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Observables/WithUnretained.swift 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Observables/Zip+Collection.swift 168 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Observables/Zip+arity.swift 934 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Observables/Zip.swift 135 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/ObserverType.swift 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Observers/AnonymousObserver.swift 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Observers/ObserverBase.swift 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Observers/TailRecursiveSink.swift 151 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Reactive.swift 80 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Rx.swift 142 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/RxMutableBox.swift 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/SchedulerType.swift 71 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Schedulers/ConcurrentDispatchQueueScheduler.swift 82 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Schedulers/ConcurrentMainScheduler.swift 87 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Schedulers/CurrentThreadScheduler.swift 131 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Schedulers/HistoricalScheduler.swift 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Schedulers/HistoricalSchedulerTimeConverter.swift 67 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Schedulers/Internal/DispatchQueueConfiguration.swift 97 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Schedulers/Internal/InvocableScheduledItem.swift 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Schedulers/Internal/InvocableType.swift 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Schedulers/Internal/ScheduledItem.swift 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Schedulers/Internal/ScheduledItemType.swift 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Schedulers/MainScheduler.swift 80 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Schedulers/OperationQueueScheduler.swift 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Schedulers/RecursiveScheduler.swift 220 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Schedulers/SchedulerServices+Emulation.swift 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Schedulers/SerialDispatchQueueScheduler.swift 131 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Schedulers/VirtualTimeConverterType.swift 97 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Schedulers/VirtualTimeScheduler.swift 267 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Subjects/AsyncSubject.swift 154 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Subjects/BehaviorSubject.swift 157 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Subjects/PublishSubject.swift 140 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Subjects/ReplaySubject.swift 271 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Subjects/SubjectType.swift 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/SwiftSupport/SwiftSupport.swift 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Traits/Infallible/Infallible+CombineLatest+Collection.swift 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Traits/Infallible/Infallible+CombineLatest+arity.swift 276 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Traits/Infallible/Infallible+Concurrency.swift 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Traits/Infallible/Infallible+Create.swift 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Traits/Infallible/Infallible+Debug.swift 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Traits/Infallible/Infallible+Operators.swift 711 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Traits/Infallible/Infallible+Zip+arity.swift 138 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Traits/Infallible/Infallible.swift 102 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Traits/Infallible/ObservableConvertibleType+Infallible.swift 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Traits/PrimitiveSequence/Completable+AndThen.swift 132 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Traits/PrimitiveSequence/Completable.swift 320 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Traits/PrimitiveSequence/Maybe.swift 387 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Traits/PrimitiveSequence/ObservableType+PrimitiveSequence.swift 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Traits/PrimitiveSequence/PrimitiveSequence+Concurrency.swift 164 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Traits/PrimitiveSequence/PrimitiveSequence+Zip+arity.swift 521 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Traits/PrimitiveSequence/PrimitiveSequence.swift 342 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/RxSwift/RxSwift/Traits/PrimitiveSequence/Single.swift 403 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/LICENSE 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/README.md 396 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/NSButton+WebCache.h 340 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/NSButton+WebCache.m 162 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/NSData+ImageContentType.h 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/NSData+ImageContentType.m 165 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/NSImage+Compatibility.h 67 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/NSImage+Compatibility.m 120 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/SDAnimatedImage.h 135 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/SDAnimatedImage.m 431 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/SDAnimatedImagePlayer.h 113 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/SDAnimatedImagePlayer.m 355 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/SDAnimatedImageRep.h 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/SDAnimatedImageRep.m 151 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/SDAnimatedImageView+WebCache.h 168 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/SDAnimatedImageView+WebCache.m 79 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/SDAnimatedImageView.h 110 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/SDAnimatedImageView.m 551 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/SDCallbackQueue.h 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/SDCallbackQueue.m 114 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/SDDiskCache.h 145 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/SDDiskCache.m 334 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/SDGraphicsImageRenderer.h 79 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/SDGraphicsImageRenderer.m 264 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/SDImageAPNGCoder.h 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/SDImageAPNGCoder.m 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/SDImageAWebPCoder.h 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/SDImageAWebPCoder.m 98 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/SDImageCache.h 474 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/SDImageCache.m 1038 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/SDImageCacheConfig.h 153 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/SDImageCacheConfig.m 68 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/SDImageCacheDefine.h 179 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/SDImageCacheDefine.m 144 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/SDImageCachesManager.h 81 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/SDImageCachesManager.m 560 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/SDImageCoder.h 327 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/SDImageCoder.m 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/SDImageCoderHelper.h 227 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/SDImageCoderHelper.m 1044 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/SDImageCodersManager.h 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/SDImageCodersManager.m 145 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/SDImageFrame.h 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/SDImageFrame.m 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/SDImageGIFCoder.h 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/SDImageGIFCoder.m 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/SDImageGraphics.h 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/SDImageGraphics.m 126 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/SDImageHEICCoder.h 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/SDImageHEICCoder.m 101 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/SDImageIOAnimatedCoder.h 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/SDImageIOAnimatedCoder.m 1086 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/SDImageIOCoder.h 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/SDImageIOCoder.m 420 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/SDImageLoader.h 146 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/SDImageLoader.m 178 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/SDImageLoadersManager.h 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/SDImageLoadersManager.m 123 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/SDImageTransformer.h 241 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/SDImageTransformer.m 331 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/SDMemoryCache.h 78 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/SDMemoryCache.m 158 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/SDWebImageCacheKeyFilter.h 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/SDWebImageCacheKeyFilter.m 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/SDWebImageCacheSerializer.h 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/SDWebImageCacheSerializer.m 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/SDWebImageCompat.h 101 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/SDWebImageCompat.m 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/SDWebImageDefine.h 403 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/SDWebImageDefine.m 159 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/SDWebImageDownloader.h 320 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/SDWebImageDownloader.m 665 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/SDWebImageDownloaderConfig.h 113 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/SDWebImageDownloaderConfig.m 60 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/SDWebImageDownloaderDecryptor.h 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/SDWebImageDownloaderDecryptor.m 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/SDWebImageDownloaderOperation.h 191 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/SDWebImageDownloaderOperation.m 760 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/SDWebImageDownloaderRequestModifier.h 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/SDWebImageDownloaderRequestModifier.m 71 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/SDWebImageDownloaderResponseModifier.h 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/SDWebImageDownloaderResponseModifier.m 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/SDWebImageError.h 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/SDWebImageError.m 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/SDWebImageIndicator.h 119 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/SDWebImageIndicator.m 291 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/SDWebImageManager.h 290 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/SDWebImageManager.m 784 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/SDWebImageOperation.h 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/SDWebImageOperation.m 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/SDWebImageOptionsProcessor.h 72 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/SDWebImageOptionsProcessor.m 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/SDWebImagePrefetcher.h 168 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/SDWebImagePrefetcher.m 341 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/SDWebImageTransition.h 131 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/SDWebImageTransition.m 194 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/UIButton+WebCache.h 402 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/UIButton+WebCache.m 198 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/UIImage+ExtendedCacheData.h 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/UIImage+ExtendedCacheData.m 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/UIImage+ForceDecode.h 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/UIImage+ForceDecode.m 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/UIImage+GIF.h 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/UIImage+GIF.m 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/UIImage+MemoryCacheCost.h 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/UIImage+MemoryCacheCost.m 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/UIImage+Metadata.h 89 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/UIImage+Metadata.m 221 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/UIImage+MultiFormat.h 81 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/UIImage+MultiFormat.m 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/UIImage+Transform.h 152 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/UIImage+Transform.m 890 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/UIImageView+HighlightedWebCache.h 142 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/UIImageView+HighlightedWebCache.m 85 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/UIImageView+WebCache.h 209 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/UIImageView+WebCache.m 78 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/UIView+WebCache.h 124 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/UIView+WebCache.m 460 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/UIView+WebCacheOperation.h 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/UIView+WebCacheOperation.m 81 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/UIView+WebCacheState.h 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Core/UIView+WebCacheState.m 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Private/NSBezierPath+SDRoundedCorners.h 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Private/NSBezierPath+SDRoundedCorners.m 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Private/SDAssociatedObject.h 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Private/SDAssociatedObject.m 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Private/SDAsyncBlockOperation.h 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Private/SDAsyncBlockOperation.m 92 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Private/SDDeviceHelper.h 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Private/SDDeviceHelper.m 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Private/SDDisplayLink.h 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Private/SDDisplayLink.m 260 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Private/SDFileAttributeHelper.h 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Private/SDFileAttributeHelper.m 127 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Private/SDImageAssetManager.h 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Private/SDImageAssetManager.m 166 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Private/SDImageCachesManagerOperation.h 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Private/SDImageCachesManagerOperation.m 83 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Private/SDImageFramePool.h 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Private/SDImageFramePool.m 164 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Private/SDImageIOAnimatedCoderInternal.h 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Private/SDInternalMacros.h 187 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Private/SDInternalMacros.m 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Private/SDWeakProxy.h 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Private/SDWeakProxy.m 79 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Private/SDWebImageTransitionInternal.h 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Private/SDmetamacros.h 667 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Private/UIColor+SDHexString.h 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/SDWebImage/Private/UIColor+SDHexString.m 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SDWebImage/WebImage/SDWebImage.h 91 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SPPageMenu/README.md 193 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SPPageMenu/SPPageMenu/SPPageMenu.h 216 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SPPageMenu/SPPageMenu/SPPageMenu.m 1925 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SVProgressHUD/LICENSE 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SVProgressHUD/README.md 221 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SVProgressHUD/SVProgressHUD/PrivacyInfo.xcprivacy 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SVProgressHUD/SVProgressHUD/SVIndefiniteAnimatedView.h 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SVProgressHUD/SVProgressHUD/SVIndefiniteAnimatedView.m 142 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SVProgressHUD/SVProgressHUD/SVProgressAnimatedView.h 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SVProgressHUD/SVProgressHUD/SVProgressAnimatedView.m 96 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SVProgressHUD/SVProgressHUD/SVProgressHUD.bundle/angle-mask.png 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SVProgressHUD/SVProgressHUD/SVProgressHUD.bundle/angle-mask@2x.png 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SVProgressHUD/SVProgressHUD/SVProgressHUD.bundle/angle-mask@3x.png 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SVProgressHUD/SVProgressHUD/SVProgressHUD.bundle/error.png 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SVProgressHUD/SVProgressHUD/SVProgressHUD.bundle/error@2x.png 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SVProgressHUD/SVProgressHUD/SVProgressHUD.bundle/error@3x.png 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SVProgressHUD/SVProgressHUD/SVProgressHUD.bundle/info.png 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SVProgressHUD/SVProgressHUD/SVProgressHUD.bundle/info@2x.png 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SVProgressHUD/SVProgressHUD/SVProgressHUD.bundle/info@3x.png 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SVProgressHUD/SVProgressHUD/SVProgressHUD.bundle/success.png 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SVProgressHUD/SVProgressHUD/SVProgressHUD.bundle/success@2x.png 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SVProgressHUD/SVProgressHUD/SVProgressHUD.bundle/success@3x.png 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SVProgressHUD/SVProgressHUD/SVProgressHUD.h 392 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SVProgressHUD/SVProgressHUD/SVProgressHUD.m 1524 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SVProgressHUD/SVProgressHUD/SVRadialGradientLayer.h 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SVProgressHUD/SVProgressHUD/SVRadialGradientLayer.m 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SnapKit/LICENSE 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SnapKit/README.md 155 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SnapKit/Sources/Constraint.swift 341 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SnapKit/Sources/ConstraintAttributes.swift 203 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SnapKit/Sources/ConstraintConfig.swift 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SnapKit/Sources/ConstraintConstantTarget.swift 213 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SnapKit/Sources/ConstraintDSL.swift 209 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SnapKit/Sources/ConstraintDescription.swift 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SnapKit/Sources/ConstraintDirectionalInsetTarget.swift 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SnapKit/Sources/ConstraintDirectionalInsets.swift 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SnapKit/Sources/ConstraintInsetTarget.swift 72 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SnapKit/Sources/ConstraintInsets.swift 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SnapKit/Sources/ConstraintItem.swift 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SnapKit/Sources/ConstraintLayoutGuide+Extensions.swift 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SnapKit/Sources/ConstraintLayoutGuide.swift 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SnapKit/Sources/ConstraintLayoutGuideDSL.swift 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SnapKit/Sources/ConstraintLayoutSupport.swift 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SnapKit/Sources/ConstraintLayoutSupportDSL.swift 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SnapKit/Sources/ConstraintMaker.swift 224 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SnapKit/Sources/ConstraintMakerEditable.swift 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SnapKit/Sources/ConstraintMakerExtendable.swift 195 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SnapKit/Sources/ConstraintMakerFinalizable.swift 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SnapKit/Sources/ConstraintMakerPrioritizable.swift 70 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SnapKit/Sources/ConstraintMakerRelatable+Extensions.swift 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SnapKit/Sources/ConstraintMakerRelatable.swift 115 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SnapKit/Sources/ConstraintMultiplierTarget.swift 75 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SnapKit/Sources/ConstraintOffsetTarget.swift 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SnapKit/Sources/ConstraintPriority.swift 77 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SnapKit/Sources/ConstraintPriorityTarget.swift 85 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SnapKit/Sources/ConstraintRelatableTarget.swift 72 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SnapKit/Sources/ConstraintRelation.swift 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SnapKit/Sources/ConstraintView+Extensions.swift 152 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SnapKit/Sources/ConstraintView.swift 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SnapKit/Sources/ConstraintViewDSL.swift 101 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SnapKit/Sources/Debugging.swift 169 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SnapKit/Sources/LayoutConstraint.swift 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SnapKit/Sources/LayoutConstraintItem.swift 93 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SnapKit/Sources/Typealiases.swift 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SnapKit/Sources/UILayoutSupport+Extensions.swift 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/LICENSE 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/README.md 392 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/AppKit/NSColorExtensions.swift 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/AppKit/NSImageExtensions.swift 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/AppKit/NSViewExtensions.swift 160 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/Combine/FutureExtensions.swift 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/CoreAnimation/CAGradientLayerExtensions.swift 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/CoreAnimation/CATransform3DExtensions.swift 275 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/CoreGraphics/CGAffineTransformExtensions.swift 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/CoreGraphics/CGColorExtensions.swift 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/CoreGraphics/CGFloatExtensions.swift 68 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/CoreGraphics/CGPointExtensions.swift 146 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/CoreGraphics/CGRectExtensions.swift 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/CoreGraphics/CGSizeExtensions.swift 252 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/CoreGraphics/CGVectorExtensions.swift 96 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/CoreLocation/CLLocationArrayExtensions.swift 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/CoreLocation/CLLocationExtensions.swift 81 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/CoreLocation/CLVisitExtensions.swift 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/CryptoKit/DigestExtensions.swift 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/Dispatch/DispatchQueueExtensions.swift 68 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/Foundation/CalendarExtensions.swift 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/Foundation/DataExtensions.swift 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/Foundation/DateExtensions.swift 1037 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/Foundation/FileManagerExtensions.swift 79 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/Foundation/LocaleExtensions.swift 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/Foundation/MeasurementExtensions.swift 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/Foundation/NSAttributedStringExtensions.swift 211 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/Foundation/NSPredicateExtensions.swift 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/Foundation/NSRegularExpressionExtensions.swift 167 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/Foundation/NotificationCenterExtensions.swift 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/Foundation/URLExtensions.swift 208 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/Foundation/URLRequestExtensions.swift 82 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/Foundation/URLSessionExtensions.swift 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/Foundation/UserDefaultsExtensions.swift 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/HealthKit/HKActivitySummaryExtensions.swift 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/MapKit/MKMapViewExtensions.swift 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/MapKit/MKMultiPointExtensions.swift 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/MapKit/MKPolylineExtensions.swift 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/SceneKit/SCNBoxExtensions.swift 83 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/SceneKit/SCNCapsuleExtensions.swift 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/SceneKit/SCNConeExtensions.swift 78 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/SceneKit/SCNCylinderExtensions.swift 68 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/SceneKit/SCNGeometryExtensions.swift 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/SceneKit/SCNMaterialExtensions.swift 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/SceneKit/SCNPlaneExtensions.swift 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/SceneKit/SCNShapeExtensions.swift 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/SceneKit/SCNSphereExtensions.swift 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/SceneKit/SCNVector3Extensions.swift 159 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/Shared/ColorExtensions.swift 1837 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/Shared/EdgeInsetsExtensions.swift 164 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/Shared/FontExtensions.swift 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/SpriteKit/SKNodeExtensions.swift 95 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/SpriteKit/SKSpriteNodeExtensions.swift 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/StoreKit/SKProductExtensions.swift 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/SwiftStdlib/ArrayExtensions.swift 153 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/SwiftStdlib/BidirectionalCollectionExtensions.swift 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/SwiftStdlib/BinaryFloatingPointExtensions.swift 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/SwiftStdlib/BinaryIntegerExtensions.swift 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/SwiftStdlib/BoolExtensions.swift 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/SwiftStdlib/CharacterExtensions.swift 108 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/SwiftStdlib/CollectionExtensions.swift 167 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/SwiftStdlib/ComparableExtensions.swift 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/SwiftStdlib/DecodableExtensions.swift 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/SwiftStdlib/DefaultStringInterpolationExtensions.swift 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/SwiftStdlib/DictionaryExtensions.swift 286 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/SwiftStdlib/DoubleExtensions.swift 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/SwiftStdlib/FloatExtensions.swift 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/SwiftStdlib/FloatingPointExtensions.swift 91 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/SwiftStdlib/IntExtensions.swift 202 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/SwiftStdlib/KeyedDecodingContainerExtensions.swift 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/SwiftStdlib/MutableCollectionExtensions.swift 68 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/SwiftStdlib/OptionalExtensions.swift 162 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/SwiftStdlib/RangeReplaceableCollectionExtensions.swift 202 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/SwiftStdlib/SequenceExtensions.swift 310 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/SwiftStdlib/SignedIntegerExtensions.swift 98 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/SwiftStdlib/SignedNumericExtensions.swift 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/SwiftStdlib/StringExtensions.swift 1401 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/SwiftStdlib/StringProtocolExtensions.swift 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/UIKit/UIActivityExtensions.swift 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/UIKit/UIAlertControllerExtensions.swift 123 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/UIKit/UIApplicationExtensions.swift 70 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/UIKit/UIBarButtonItemExtensions.swift 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/UIKit/UIBezierPathExtensions.swift 67 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/UIKit/UIButtonExtensions.swift 226 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/UIKit/UICollectionViewExtensions.swift 178 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/UIKit/UIColorExtensions.swift 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/UIKit/UIFontExtensions.swift 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/UIKit/UIGestureRecognizerExtensions.swift 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/UIKit/UIImageExtensions.swift 446 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/UIKit/UIImageViewExtensions.swift 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/UIKit/UILabelExtensions.swift 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/UIKit/UILayoutPriorityExtensions.swift 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/UIKit/UINavigationBarExtensions.swift 88 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/UIKit/UINavigationControllerExtensions.swift 60 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/UIKit/UINavigationItemExtensions.swift 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/UIKit/UIRefreshControlExtensions.swift 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/UIKit/UIScrollViewExtensions.swift 140 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/UIKit/UISearchBarExtensions.swift 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/UIKit/UISegmentedControlExtensions.swift 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/UIKit/UISliderExtensions.swift 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/UIKit/UIStackViewExtensions.swift 117 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/UIKit/UIStoryboardExtensions.swift 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/UIKit/UISwitchExtensions.swift 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/UIKit/UITabBarExtensions.swift 81 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/UIKit/UITableViewExtensions.swift 181 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/UIKit/UITextFieldExtensions.swift 191 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/UIKit/UITextViewExtensions.swift 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/UIKit/UIViewControllerExtensions.swift 154 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/UIKit/UIViewExtensions.swift 719 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/UIKit/UIWindowExtensions.swift 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwifterSwift/Sources/SwifterSwift/WebKit/WKWebViewExtensions.swift 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwiftyStoreKit/LICENSE.md 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwiftyStoreKit/README.md 812 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwiftyStoreKit/Sources/SwiftyStoreKit/AppleReceiptValidator.swift 127 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwiftyStoreKit/Sources/SwiftyStoreKit/CompleteTransactionsController.swift 77 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwiftyStoreKit/Sources/SwiftyStoreKit/InAppProductQueryRequest.swift 81 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwiftyStoreKit/Sources/SwiftyStoreKit/InAppReceipt.swift 285 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwiftyStoreKit/Sources/SwiftyStoreKit/InAppReceiptRefreshRequest.swift 85 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwiftyStoreKit/Sources/SwiftyStoreKit/InAppReceiptVerificator.swift 119 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwiftyStoreKit/Sources/SwiftyStoreKit/OS.swift 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwiftyStoreKit/Sources/SwiftyStoreKit/PaymentQueueController.swift 261 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwiftyStoreKit/Sources/SwiftyStoreKit/PaymentsController.swift 144 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwiftyStoreKit/Sources/SwiftyStoreKit/ProductsInfoController.swift 78 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwiftyStoreKit/Sources/SwiftyStoreKit/RestorePurchasesController.swift 108 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwiftyStoreKit/Sources/SwiftyStoreKit/SKProduct+LocalizedPrice.swift 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwiftyStoreKit/Sources/SwiftyStoreKit/SKProductDiscount+LocalizedPrice.swift 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwiftyStoreKit/Sources/SwiftyStoreKit/SwiftyStoreKit+Types.swift 332 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/SwiftyStoreKit/Sources/SwiftyStoreKit/SwiftyStoreKit.swift 312 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/TZImagePickerController/LICENSE 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/TZImagePickerController/README.md 175 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/TZImagePickerController/TZImagePickerController/Location/TZLocationManager.h 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/TZImagePickerController/TZImagePickerController/Location/TZLocationManager.m 90 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/NSBundle+TZImagePicker.h 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/NSBundle+TZImagePicker.m 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZAssetCell.h 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZAssetCell.m 548 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZAssetModel.h 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZAssetModel.m 79 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZAuthLimitedFooterTipView.h 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZAuthLimitedFooterTipView.m 87 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZGifPhotoPreviewController.h 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZGifPhotoPreviewController.m 173 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZImageCropManager.h 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZImageCropManager.m 199 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZImageManager.h 140 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZImageManager.m 1077 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZImagePickerController.bundle/MMVideoPreviewPlay@2x.png 补丁 | 查看 | 原始文档 | blame | 历史
Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZImagePickerController.bundle/MMVideoPreviewPlayHL@2x.png 补丁 | 查看 | 原始文档 | blame | 历史
Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZImagePickerController.bundle/VideoSendIcon@2x.png 补丁 | 查看 | 原始文档 | blame | 历史
Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZImagePickerController.bundle/addMore@2x.png 补丁 | 查看 | 原始文档 | blame | 历史
Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZImagePickerController.bundle/ar.lproj/Localizable.strings 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZImagePickerController.bundle/de.lproj/Localizable.strings 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZImagePickerController.bundle/en.lproj/Localizable.strings 补丁 | 查看 | 原始文档 | blame | 历史
Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZImagePickerController.bundle/es.lproj/Localizable.strings 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZImagePickerController.bundle/fr.lproj/Localizable.strings 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZImagePickerController.bundle/iCloudError@2x.png 补丁 | 查看 | 原始文档 | blame | 历史
Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZImagePickerController.bundle/ja.lproj/Localizable.strings 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZImagePickerController.bundle/ko-KP.lproj/Localizable.strings 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZImagePickerController.bundle/navi_back@2x.png 补丁 | 查看 | 原始文档 | blame | 历史
Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZImagePickerController.bundle/photo_def_photoPickerVc@2x.png 补丁 | 查看 | 原始文档 | blame | 历史
Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZImagePickerController.bundle/photo_def_previewVc@2x.png 补丁 | 查看 | 原始文档 | blame | 历史
Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZImagePickerController.bundle/photo_number_icon@2x.png 补丁 | 查看 | 原始文档 | blame | 历史
Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZImagePickerController.bundle/photo_original_def@2x.png 补丁 | 查看 | 原始文档 | blame | 历史
Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZImagePickerController.bundle/photo_original_sel@2x.png 补丁 | 查看 | 原始文档 | blame | 历史
Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZImagePickerController.bundle/photo_sel_photoPickerVc@2x.png 补丁 | 查看 | 原始文档 | blame | 历史
Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZImagePickerController.bundle/photo_sel_previewVc@2x.png 补丁 | 查看 | 原始文档 | blame | 历史
Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZImagePickerController.bundle/preview_number_icon@2x.png 补丁 | 查看 | 原始文档 | blame | 历史
Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZImagePickerController.bundle/preview_original_def@2x.png 补丁 | 查看 | 原始文档 | blame | 历史
Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZImagePickerController.bundle/pt.lproj/Localizable.strings 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZImagePickerController.bundle/right_arrow@2x.png 补丁 | 查看 | 原始文档 | blame | 历史
Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZImagePickerController.bundle/ru.lproj/Localizable.strings 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZImagePickerController.bundle/takePicture80@2x.png 补丁 | 查看 | 原始文档 | blame | 历史
Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZImagePickerController.bundle/takePicture@2x.png 补丁 | 查看 | 原始文档 | blame | 历史
Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZImagePickerController.bundle/tip@2x.png 补丁 | 查看 | 原始文档 | blame | 历史
Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZImagePickerController.bundle/vi.lproj/Localizable.strings 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZImagePickerController.bundle/zh-Hans.lproj/Localizable.strings 补丁 | 查看 | 原始文档 | blame | 历史
Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZImagePickerController.bundle/zh-Hant.lproj/Localizable.strings 补丁 | 查看 | 原始文档 | blame | 历史
Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZImagePickerController.h 396 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZImagePickerController.m 1111 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZImageRequestOperation.h 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZImageRequestOperation.m 77 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZPhotoPickerController.h 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZPhotoPickerController.m 1255 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZPhotoPreviewCell.h 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZPhotoPreviewCell.m 576 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZPhotoPreviewController.h 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZPhotoPreviewController.m 686 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZProgressView.h 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZProgressView.m 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZVideoCropController.h 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZVideoCropController.m 673 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZVideoEditedPreviewController.h 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZVideoEditedPreviewController.m 129 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZVideoPlayerController.h 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZVideoPlayerController.m 325 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/UIView+TZLayout.h 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/UIView+TZLayout.m 126 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/Alamofire/Alamofire-Info.plist 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/Alamofire/Alamofire-dummy.m 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/Alamofire/Alamofire-prefix.pch 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/Alamofire/Alamofire-umbrella.h 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/Alamofire/Alamofire.debug.xcconfig 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/Alamofire/Alamofire.modulemap 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/Alamofire/Alamofire.release.xcconfig 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/CryptoSwift/CryptoSwift-Info.plist 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/CryptoSwift/CryptoSwift-dummy.m 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/CryptoSwift/CryptoSwift-prefix.pch 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/CryptoSwift/CryptoSwift-umbrella.h 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/CryptoSwift/CryptoSwift.debug.xcconfig 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/CryptoSwift/CryptoSwift.modulemap 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/CryptoSwift/CryptoSwift.release.xcconfig 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/Differentiator/Differentiator-Info.plist 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/Differentiator/Differentiator-dummy.m 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/Differentiator/Differentiator-prefix.pch 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/Differentiator/Differentiator-umbrella.h 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/Differentiator/Differentiator.debug.xcconfig 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/Differentiator/Differentiator.modulemap 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/Differentiator/Differentiator.release.xcconfig 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/EmptyDataSet-Swift/EmptyDataSet-Swift-Info.plist 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/EmptyDataSet-Swift/EmptyDataSet-Swift-dummy.m 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/EmptyDataSet-Swift/EmptyDataSet-Swift-prefix.pch 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/EmptyDataSet-Swift/EmptyDataSet-Swift-umbrella.h 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/EmptyDataSet-Swift/EmptyDataSet-Swift.debug.xcconfig 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/EmptyDataSet-Swift/EmptyDataSet-Swift.modulemap 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/EmptyDataSet-Swift/EmptyDataSet-Swift.release.xcconfig 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/FFPage/FFPage-Info.plist 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/FFPage/FFPage-dummy.m 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/FFPage/FFPage-prefix.pch 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/FFPage/FFPage-umbrella.h 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/FFPage/FFPage.debug.xcconfig 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/FFPage/FFPage.modulemap 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/FFPage/FFPage.release.xcconfig 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/HandyJSON/HandyJSON-Info.plist 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/HandyJSON/HandyJSON-dummy.m 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/HandyJSON/HandyJSON-prefix.pch 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/HandyJSON/HandyJSON-umbrella.h 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/HandyJSON/HandyJSON.debug.xcconfig 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/HandyJSON/HandyJSON.modulemap 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/HandyJSON/HandyJSON.release.xcconfig 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/IQKeyboardManager/IQKeyboardManager-Info.plist 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/IQKeyboardManager/IQKeyboardManager-dummy.m 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/IQKeyboardManager/IQKeyboardManager-prefix.pch 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/IQKeyboardManager/IQKeyboardManager-umbrella.h 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/IQKeyboardManager/IQKeyboardManager.debug.xcconfig 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/IQKeyboardManager/IQKeyboardManager.modulemap 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/IQKeyboardManager/IQKeyboardManager.release.xcconfig 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/IQKeyboardManagerSwift/IQKeyboardManagerSwift-Info.plist 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/IQKeyboardManagerSwift/IQKeyboardManagerSwift-dummy.m 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/IQKeyboardManagerSwift/IQKeyboardManagerSwift-prefix.pch 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/IQKeyboardManagerSwift/IQKeyboardManagerSwift-umbrella.h 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/IQKeyboardManagerSwift/IQKeyboardManagerSwift.debug.xcconfig 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/IQKeyboardManagerSwift/IQKeyboardManagerSwift.modulemap 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/IQKeyboardManagerSwift/IQKeyboardManagerSwift.release.xcconfig 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/JQTools/JQTools-Info.plist 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/JQTools/JQTools-dummy.m 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/JQTools/JQTools-prefix.pch 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/JQTools/JQTools-umbrella.h 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/JQTools/JQTools.debug.xcconfig 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/JQTools/JQTools.modulemap 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/JQTools/JQTools.release.xcconfig 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/JQTools/ResourceBundle-JQToolsRes-JQTools-Info.plist 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/Lantern/Lantern-Info.plist 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/Lantern/Lantern-dummy.m 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/Lantern/Lantern-prefix.pch 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/Lantern/Lantern-umbrella.h 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/Lantern/Lantern.debug.xcconfig 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/Lantern/Lantern.modulemap 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/Lantern/Lantern.release.xcconfig 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/MJRefresh/MJRefresh-Info.plist 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/MJRefresh/MJRefresh-dummy.m 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/MJRefresh/MJRefresh-prefix.pch 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/MJRefresh/MJRefresh-umbrella.h 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/MJRefresh/MJRefresh.debug.xcconfig 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/MJRefresh/MJRefresh.modulemap 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/MJRefresh/MJRefresh.release.xcconfig 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/ObjcExceptionBridging/ObjcExceptionBridging-Info.plist 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/ObjcExceptionBridging/ObjcExceptionBridging-dummy.m 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/ObjcExceptionBridging/ObjcExceptionBridging-prefix.pch 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/ObjcExceptionBridging/ObjcExceptionBridging-umbrella.h 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/ObjcExceptionBridging/ObjcExceptionBridging.debug.xcconfig 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/ObjcExceptionBridging/ObjcExceptionBridging.modulemap 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/ObjcExceptionBridging/ObjcExceptionBridging.release.xcconfig 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/ObjectMapper/ObjectMapper-Info.plist 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/ObjectMapper/ObjectMapper-dummy.m 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/ObjectMapper/ObjectMapper-prefix.pch 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/ObjectMapper/ObjectMapper-umbrella.h 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/ObjectMapper/ObjectMapper.debug.xcconfig 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/ObjectMapper/ObjectMapper.modulemap 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/ObjectMapper/ObjectMapper.release.xcconfig 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/Pods-DolphinEnglishLearnStudent/Pods-DolphinEnglishLearnStudent-Info.plist 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/Pods-DolphinEnglishLearnStudent/Pods-DolphinEnglishLearnStudent-acknowledgements.markdown 1009 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/Pods-DolphinEnglishLearnStudent/Pods-DolphinEnglishLearnStudent-acknowledgements.plist 1203 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/Pods-DolphinEnglishLearnStudent/Pods-DolphinEnglishLearnStudent-dummy.m 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/Pods-DolphinEnglishLearnStudent/Pods-DolphinEnglishLearnStudent-frameworks-Debug-input-files.xcfilelist 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/Pods-DolphinEnglishLearnStudent/Pods-DolphinEnglishLearnStudent-frameworks-Debug-output-files.xcfilelist 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/Pods-DolphinEnglishLearnStudent/Pods-DolphinEnglishLearnStudent-frameworks-Release-input-files.xcfilelist 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/Pods-DolphinEnglishLearnStudent/Pods-DolphinEnglishLearnStudent-frameworks-Release-output-files.xcfilelist 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/Pods-DolphinEnglishLearnStudent/Pods-DolphinEnglishLearnStudent-frameworks.sh 240 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/Pods-DolphinEnglishLearnStudent/Pods-DolphinEnglishLearnStudent-umbrella.h 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/Pods-DolphinEnglishLearnStudent/Pods-DolphinEnglishLearnStudent.debug.xcconfig 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/Pods-DolphinEnglishLearnStudent/Pods-DolphinEnglishLearnStudent.modulemap 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/Pods-DolphinEnglishLearnStudent/Pods-DolphinEnglishLearnStudent.release.xcconfig 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/QMUIKit/QMUIKit-Info.plist 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/QMUIKit/QMUIKit-dummy.m 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/QMUIKit/QMUIKit-prefix.pch 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/QMUIKit/QMUIKit-umbrella.h 183 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/QMUIKit/QMUIKit.debug.xcconfig 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/QMUIKit/QMUIKit.modulemap 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/QMUIKit/QMUIKit.release.xcconfig 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/QMUIKit/ResourceBundle-QMUIResources-QMUIKit-Info.plist 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/RxCocoa/RxCocoa-Info.plist 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/RxCocoa/RxCocoa-dummy.m 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/RxCocoa/RxCocoa-prefix.pch 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/RxCocoa/RxCocoa-umbrella.h 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/RxCocoa/RxCocoa.debug.xcconfig 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/RxCocoa/RxCocoa.modulemap 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/RxCocoa/RxCocoa.release.xcconfig 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/RxDataSources/RxDataSources-Info.plist 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/RxDataSources/RxDataSources-dummy.m 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/RxDataSources/RxDataSources-prefix.pch 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/RxDataSources/RxDataSources-umbrella.h 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/RxDataSources/RxDataSources.debug.xcconfig 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/RxDataSources/RxDataSources.modulemap 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/RxDataSources/RxDataSources.release.xcconfig 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/RxRelay/RxRelay-Info.plist 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/RxRelay/RxRelay-dummy.m 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/RxRelay/RxRelay-prefix.pch 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/RxRelay/RxRelay-umbrella.h 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/RxRelay/RxRelay.debug.xcconfig 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/RxRelay/RxRelay.modulemap 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/RxRelay/RxRelay.release.xcconfig 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/RxSwift/RxSwift-Info.plist 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/RxSwift/RxSwift-dummy.m 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/RxSwift/RxSwift-prefix.pch 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/RxSwift/RxSwift-umbrella.h 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/RxSwift/RxSwift.debug.xcconfig 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/RxSwift/RxSwift.modulemap 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/RxSwift/RxSwift.release.xcconfig 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/SDWebImage/SDWebImage-Info.plist 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/SDWebImage/SDWebImage-dummy.m 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/SDWebImage/SDWebImage-prefix.pch 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/SDWebImage/SDWebImage-umbrella.h 77 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/SDWebImage/SDWebImage.debug.xcconfig 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/SDWebImage/SDWebImage.modulemap 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/SDWebImage/SDWebImage.release.xcconfig 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/SPPageMenu/SPPageMenu-Info.plist 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/SPPageMenu/SPPageMenu-dummy.m 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/SPPageMenu/SPPageMenu-prefix.pch 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/SPPageMenu/SPPageMenu-umbrella.h 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/SPPageMenu/SPPageMenu.debug.xcconfig 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/SPPageMenu/SPPageMenu.modulemap 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/SPPageMenu/SPPageMenu.release.xcconfig 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/SVProgressHUD/SVProgressHUD-Info.plist 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/SVProgressHUD/SVProgressHUD-dummy.m 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/SVProgressHUD/SVProgressHUD-prefix.pch 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/SVProgressHUD/SVProgressHUD-umbrella.h 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/SVProgressHUD/SVProgressHUD.debug.xcconfig 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/SVProgressHUD/SVProgressHUD.modulemap 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/SVProgressHUD/SVProgressHUD.release.xcconfig 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/SnapKit/SnapKit-Info.plist 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/SnapKit/SnapKit-dummy.m 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/SnapKit/SnapKit-prefix.pch 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/SnapKit/SnapKit-umbrella.h 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/SnapKit/SnapKit.debug.xcconfig 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/SnapKit/SnapKit.modulemap 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/SnapKit/SnapKit.release.xcconfig 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/SwifterSwift/SwifterSwift-Info.plist 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/SwifterSwift/SwifterSwift-dummy.m 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/SwifterSwift/SwifterSwift-prefix.pch 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/SwifterSwift/SwifterSwift-umbrella.h 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/SwifterSwift/SwifterSwift.debug.xcconfig 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/SwifterSwift/SwifterSwift.modulemap 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/SwifterSwift/SwifterSwift.release.xcconfig 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/SwiftyStoreKit/SwiftyStoreKit-Info.plist 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/SwiftyStoreKit/SwiftyStoreKit-dummy.m 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/SwiftyStoreKit/SwiftyStoreKit-prefix.pch 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/SwiftyStoreKit/SwiftyStoreKit-umbrella.h 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/SwiftyStoreKit/SwiftyStoreKit.debug.xcconfig 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/SwiftyStoreKit/SwiftyStoreKit.modulemap 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/SwiftyStoreKit/SwiftyStoreKit.release.xcconfig 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/TZImagePickerController/TZImagePickerController-Info.plist 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/TZImagePickerController/TZImagePickerController-dummy.m 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/TZImagePickerController/TZImagePickerController-prefix.pch 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/TZImagePickerController/TZImagePickerController-umbrella.h 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/TZImagePickerController/TZImagePickerController.debug.xcconfig 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/TZImagePickerController/TZImagePickerController.modulemap 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/TZImagePickerController/TZImagePickerController.release.xcconfig 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/UserDefaultsStore/UserDefaultsStore-Info.plist 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/UserDefaultsStore/UserDefaultsStore-dummy.m 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/UserDefaultsStore/UserDefaultsStore-prefix.pch 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/UserDefaultsStore/UserDefaultsStore-umbrella.h 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/UserDefaultsStore/UserDefaultsStore.debug.xcconfig 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/UserDefaultsStore/UserDefaultsStore.modulemap 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/UserDefaultsStore/UserDefaultsStore.release.xcconfig 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/VTMagic/VTMagic-Info.plist 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/VTMagic/VTMagic-dummy.m 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/VTMagic/VTMagic-prefix.pch 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/VTMagic/VTMagic-umbrella.h 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/VTMagic/VTMagic.debug.xcconfig 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/VTMagic/VTMagic.modulemap 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/VTMagic/VTMagic.release.xcconfig 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/WechatOpenSDK-XCFramework/WechatOpenSDK-XCFramework-xcframeworks-input-files.xcfilelist 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/WechatOpenSDK-XCFramework/WechatOpenSDK-XCFramework-xcframeworks-output-files.xcfilelist 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/WechatOpenSDK-XCFramework/WechatOpenSDK-XCFramework-xcframeworks.sh 121 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/WechatOpenSDK-XCFramework/WechatOpenSDK-XCFramework.debug.xcconfig 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/WechatOpenSDK-XCFramework/WechatOpenSDK-XCFramework.release.xcconfig 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/XCGLogger/XCGLogger-Info.plist 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/XCGLogger/XCGLogger-dummy.m 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/XCGLogger/XCGLogger-prefix.pch 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/XCGLogger/XCGLogger-umbrella.h 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/XCGLogger/XCGLogger.debug.xcconfig 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/XCGLogger/XCGLogger.modulemap 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/Target Support Files/XCGLogger/XCGLogger.release.xcconfig 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/UserDefaultsStore/LICENSE 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/UserDefaultsStore/README.md 189 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/UserDefaultsStore/Sources/Identifiable.swift 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/UserDefaultsStore/Sources/SingleUserDefaultsStore.swift 110 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/UserDefaultsStore/Sources/UserDefaultsStore.swift 217 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/VTMagic/LICENSE 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/VTMagic/README.md 228 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/VTMagic/VTMagic/UIColor+VTMagic.h 71 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/VTMagic/VTMagic/UIColor+VTMagic.m 90 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/VTMagic/VTMagic/UIScrollView+VTMagic.h 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/VTMagic/VTMagic/UIScrollView+VTMagic.m 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/VTMagic/VTMagic/UIViewController+VTMagic.h 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/VTMagic/VTMagic/UIViewController+VTMagic.m 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/VTMagic/VTMagic/VTContentView.h 133 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/VTMagic/VTMagic/VTContentView.m 214 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/VTMagic/VTMagic/VTEnumType.h 74 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/VTMagic/VTMagic/VTMagic.h 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/VTMagic/VTMagic/VTMagicController.h 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/VTMagic/VTMagic/VTMagicController.m 130 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/VTMagic/VTMagic/VTMagicMacros.h 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/VTMagic/VTMagic/VTMagicProtocol.h 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/VTMagic/VTMagic/VTMagicView.h 481 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/VTMagic/VTMagic/VTMagicView.m 1167 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/VTMagic/VTMagic/VTMenuBar.h 242 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/VTMagic/VTMagic/VTMenuBar.m 377 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/WechatOpenSDK-XCFramework/WechatOpenSDK-XCFramework.xcframework/.DS_Store 补丁 | 查看 | 原始文档 | blame | 历史
Pods/WechatOpenSDK-XCFramework/WechatOpenSDK-XCFramework.xcframework/Info.plist 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/WechatOpenSDK-XCFramework/WechatOpenSDK-XCFramework.xcframework/README.txt 159 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/WechatOpenSDK-XCFramework/WechatOpenSDK-XCFramework.xcframework/ios-arm64_armv7/Headers/WXApi.h 231 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/WechatOpenSDK-XCFramework/WechatOpenSDK-XCFramework.xcframework/ios-arm64_armv7/Headers/WXApiObject.h 1396 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/WechatOpenSDK-XCFramework/WechatOpenSDK-XCFramework.xcframework/ios-arm64_armv7/Headers/WechatAuthSDK.h 68 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/WechatOpenSDK-XCFramework/WechatOpenSDK-XCFramework.xcframework/ios-arm64_armv7/libWechatOpenSDK.a 补丁 | 查看 | 原始文档 | blame | 历史
Pods/WechatOpenSDK-XCFramework/WechatOpenSDK-XCFramework.xcframework/ios-arm64_i386_x86_64-simulator/Headers/WXApi.h 231 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/WechatOpenSDK-XCFramework/WechatOpenSDK-XCFramework.xcframework/ios-arm64_i386_x86_64-simulator/Headers/WXApiObject.h 1396 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/WechatOpenSDK-XCFramework/WechatOpenSDK-XCFramework.xcframework/ios-arm64_i386_x86_64-simulator/Headers/WechatAuthSDK.h 68 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/WechatOpenSDK-XCFramework/WechatOpenSDK-XCFramework.xcframework/ios-arm64_i386_x86_64-simulator/libWechatOpenSDK.a 补丁 | 查看 | 原始文档 | blame | 历史
Pods/XCGLogger/.swift-version 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/XCGLogger/LICENSE.txt 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/XCGLogger/README.md 596 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/XCGLogger/Sources/XCGLogger/Destinations/AppleSystemLogDestination.swift 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/XCGLogger/Sources/XCGLogger/Destinations/AutoRotatingFileDestination.swift 286 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/XCGLogger/Sources/XCGLogger/Destinations/BaseDestination.swift 181 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/XCGLogger/Sources/XCGLogger/Destinations/BaseQueuedDestination.swift 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/XCGLogger/Sources/XCGLogger/Destinations/ConsoleDestination.swift 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/XCGLogger/Sources/XCGLogger/Destinations/DestinationProtocol.swift 124 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/XCGLogger/Sources/XCGLogger/Destinations/FileDestination.swift 245 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/XCGLogger/Sources/XCGLogger/Destinations/TestDestination.swift 109 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/XCGLogger/Sources/XCGLogger/Extensions/DispatchQueue+XCGAdditions.swift 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/XCGLogger/Sources/XCGLogger/Extensions/URL+XCGAdditions.swift 82 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/XCGLogger/Sources/XCGLogger/Filters/DevFilter.swift 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/XCGLogger/Sources/XCGLogger/Filters/FileNameFilter.swift 126 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/XCGLogger/Sources/XCGLogger/Filters/FilterProtocol.swift 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/XCGLogger/Sources/XCGLogger/Filters/TagFilter.swift 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/XCGLogger/Sources/XCGLogger/Filters/UserInfoFilter.swift 143 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/XCGLogger/Sources/XCGLogger/LogFormatters/ANSIColorLogFormatter.swift 342 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/XCGLogger/Sources/XCGLogger/LogFormatters/Base64LogFormatter.swift 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/XCGLogger/Sources/XCGLogger/LogFormatters/LogFormatterProtocol.swift 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/XCGLogger/Sources/XCGLogger/LogFormatters/PrePostFixLogFormatter.swift 100 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/XCGLogger/Sources/XCGLogger/LogFormatters/XcodeColorsLogFormatter.swift 257 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/XCGLogger/Sources/XCGLogger/Misc/HelperFunctions.swift 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/XCGLogger/Sources/XCGLogger/Misc/LogDetails.swift 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Pods/XCGLogger/Sources/XCGLogger/XCGLogger.swift 1553 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DolphinEnglishLearnStudent.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
New file
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>IDEDidComputeMac32BitWarning</key>
    <true/>
</dict>
</plist>
DolphinEnglishLearnStudent.xcodeproj/xcshareddata/xcschemes/DolphinEnglishLearnStudent.xcscheme
New file
@@ -0,0 +1,78 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
   LastUpgradeVersion = "1530"
   version = "1.7">
   <BuildAction
      parallelizeBuildables = "YES"
      buildImplicitDependencies = "YES"
      buildArchitectures = "Automatic">
      <BuildActionEntries>
         <BuildActionEntry
            buildForTesting = "YES"
            buildForRunning = "YES"
            buildForProfiling = "YES"
            buildForArchiving = "YES"
            buildForAnalyzing = "YES">
            <BuildableReference
               BuildableIdentifier = "primary"
               BlueprintIdentifier = "130278252BFD957100DDCE81"
               BuildableName = "DolphinEnglishLearnStudent.app"
               BlueprintName = "DolphinEnglishLearnStudent"
               ReferencedContainer = "container:DolphinEnglishLearnStudent.xcodeproj">
            </BuildableReference>
         </BuildActionEntry>
      </BuildActionEntries>
   </BuildAction>
   <TestAction
      buildConfiguration = "Debug"
      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
      shouldUseLaunchSchemeArgsEnv = "YES"
      shouldAutocreateTestPlan = "YES">
   </TestAction>
   <LaunchAction
      buildConfiguration = "Debug"
      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
      launchStyle = "0"
      useCustomWorkingDirectory = "NO"
      ignoresPersistentStateOnLaunch = "NO"
      debugDocumentVersioning = "YES"
      debugServiceExtension = "internal"
      allowLocationSimulation = "YES">
      <BuildableProductRunnable
         runnableDebuggingMode = "0">
         <BuildableReference
            BuildableIdentifier = "primary"
            BlueprintIdentifier = "130278252BFD957100DDCE81"
            BuildableName = "DolphinEnglishLearnStudent.app"
            BlueprintName = "DolphinEnglishLearnStudent"
            ReferencedContainer = "container:DolphinEnglishLearnStudent.xcodeproj">
         </BuildableReference>
      </BuildableProductRunnable>
   </LaunchAction>
   <ProfileAction
      buildConfiguration = "Release"
      shouldUseLaunchSchemeArgsEnv = "YES"
      savedToolIdentifier = ""
      useCustomWorkingDirectory = "NO"
      debugDocumentVersioning = "YES">
      <BuildableProductRunnable
         runnableDebuggingMode = "0">
         <BuildableReference
            BuildableIdentifier = "primary"
            BlueprintIdentifier = "130278252BFD957100DDCE81"
            BuildableName = "DolphinEnglishLearnStudent.app"
            BlueprintName = "DolphinEnglishLearnStudent"
            ReferencedContainer = "container:DolphinEnglishLearnStudent.xcodeproj">
         </BuildableReference>
      </BuildableProductRunnable>
   </ProfileAction>
   <AnalyzeAction
      buildConfiguration = "Debug">
   </AnalyzeAction>
   <ArchiveAction
      buildConfiguration = "Debug"
      revealArchiveInOrganizer = "YES">
   </ArchiveAction>
</Scheme>
DolphinEnglishLearnStudent.xcworkspace/contents.xcworkspacedata
New file
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
   version = "1.0">
   <FileRef
      location = "group:DolphinEnglishLearnStudent.xcodeproj">
   </FileRef>
   <FileRef
      location = "group:Pods/Pods.xcodeproj">
   </FileRef>
</Workspace>
DolphinEnglishLearnStudent.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
New file
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>IDEDidComputeMac32BitWarning</key>
    <true/>
</dict>
</plist>
DolphinEnglishLearnStudent/AppDelegate.swift
@@ -11,44 +11,44 @@
class AppDelegate: UIResponder, UIApplicationDelegate {
                var window: UIWindow?
    var window: UIWindow?
                func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
                                // Override point for customization after application launch.
                                sleep(2)
                                return true
                }
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.
        sleep(2)
        return true
    }
                // MARK: UISceneSession Lifecycle
    // MARK: UISceneSession Lifecycle
                func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
                                // Called when a new scene session is being created.
                                // Use this method to select a configuration to create the new scene with.
                                return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
                }
    func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
        // Called when a new scene session is being created.
        // Use this method to select a configuration to create the new scene with.
        return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
    }
                func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
                                // Called when the user discards a scene session.
                                // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
                                // Use this method to release any resources that were specific to the discarded scenes, as they will not return.
                }
    func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
        // Called when the user discards a scene session.
        // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
        // Use this method to release any resources that were specific to the discarded scenes, as they will not return.
    }
                func application(_ application: UIApplication, handleOpen url: URL) -> Bool {
                                return WXApi.handleOpen(url, delegate: self)
                }
    func application(_ application: UIApplication, handleOpen url: URL) -> Bool {
        return WXApi.handleOpen(url, delegate: self)
    }
                func application(_ application: UIApplication, open url: URL, sourceApplication: String?, annotation: Any) -> Bool {
                                return WXApi.handleOpen(url, delegate: self)
                }
    func application(_ application: UIApplication, open url: URL, sourceApplication: String?, annotation: Any) -> Bool {
        return WXApi.handleOpen(url, delegate: self)
    }
}
extension AppDelegate:WXApiDelegate{
                func onReq(_ req: BaseReq) {
    func onReq(_ req: BaseReq) {
                }
    }
                func onResp(_ resp: BaseResp) {
    func onResp(_ resp: BaseResp) {
                }
    }
}
DolphinEnglishLearnStudent/Assets.xcassets/Bg/bg_abc.imageset/Contents.json
@@ -1,14 +1,18 @@
{
  "images" : [
    {
      "filename" : "bg_abc.png",
      "idiom" : "ipad",
      "idiom" : "universal",
      "scale" : "1x"
    },
    {
      "filename" : "bg_abc@2x.png",
      "idiom" : "ipad",
      "filename" : "bg_abc.png",
      "idiom" : "universal",
      "scale" : "2x"
    },
    {
      "filename" : "bg_abc@2x.png",
      "idiom" : "universal",
      "scale" : "3x"
    }
  ],
  "info" : {
DolphinEnglishLearnStudent/Assets.xcassets/Bg/bg_card.imageset/Contents.json
@@ -1,14 +1,18 @@
{
  "images" : [
    {
      "filename" : "bg_card.png",
      "idiom" : "ipad",
      "idiom" : "universal",
      "scale" : "1x"
    },
    {
      "filename" : "bg_card@2x.png",
      "idiom" : "ipad",
      "filename" : "bg_card.png",
      "idiom" : "universal",
      "scale" : "2x"
    },
    {
      "filename" : "bg_card@2x.png",
      "idiom" : "universal",
      "scale" : "3x"
    }
  ],
  "info" : {
DolphinEnglishLearnStudent/Assets.xcassets/Bg/bg_login.imageset/Contents.json
@@ -1,14 +1,18 @@
{
  "images" : [
    {
      "filename" : "bg_login.png",
      "idiom" : "ipad",
      "idiom" : "universal",
      "scale" : "1x"
    },
    {
      "filename" : "bg_login@2x.png",
      "idiom" : "ipad",
      "filename" : "bg_login.png",
      "idiom" : "universal",
      "scale" : "2x"
    },
    {
      "filename" : "bg_login@2x.png",
      "idiom" : "universal",
      "scale" : "3x"
    }
  ],
  "info" : {
DolphinEnglishLearnStudent/Assets.xcassets/Bg/bg_logo.imageset/Contents.json
@@ -1,14 +1,18 @@
{
  "images" : [
    {
      "filename" : "wecom-temp-67726-6439a5b9afc49824ac67b405478482e0.png",
      "idiom" : "ipad",
      "idiom" : "universal",
      "scale" : "1x"
    },
    {
      "filename" : "wecom-temp-67726-6439a5b9afc49824ac67b405478482e01.png",
      "idiom" : "ipad",
      "filename" : "wecom-temp-67726-6439a5b9afc49824ac67b405478482e0.png",
      "idiom" : "universal",
      "scale" : "2x"
    },
    {
      "filename" : "wecom-temp-67726-6439a5b9afc49824ac67b405478482e01.png",
      "idiom" : "universal",
      "scale" : "3x"
    }
  ],
  "info" : {
DolphinEnglishLearnStudent/Assets.xcassets/Btn/btn_add_un.imageset/Contents.json
@@ -1,14 +1,18 @@
{
  "images" : [
    {
      "filename" : "btn_add_un.png",
      "idiom" : "ipad",
      "idiom" : "universal",
      "scale" : "1x"
    },
    {
      "filename" : "btn_add_un@2x.png",
      "idiom" : "ipad",
      "filename" : "btn_add_un.png",
      "idiom" : "universal",
      "scale" : "2x"
    },
    {
      "filename" : "btn_add_un@2x.png",
      "idiom" : "universal",
      "scale" : "3x"
    }
  ],
  "info" : {
DolphinEnglishLearnStudent/Assets.xcassets/Btn/btn_radio_cir.imageset/Contents.json
@@ -1,16 +1,16 @@
{
  "images" : [
    {
      "filename" : "btn_radio.png",
      "idiom" : "universal",
      "scale" : "1x"
    },
    {
      "filename" : "btn_radio@2x.png",
      "filename" : "btn_radio.png",
      "idiom" : "universal",
      "scale" : "2x"
    },
    {
      "filename" : "btn_radio@2x.png",
      "idiom" : "universal",
      "scale" : "3x"
    }
DolphinEnglishLearnStudent/Assets.xcassets/Btn/btn_radio_u_cir.imageset/Contents.json
@@ -1,16 +1,16 @@
{
  "images" : [
    {
      "filename" : "btn_radio_u.png",
      "idiom" : "universal",
      "scale" : "1x"
    },
    {
      "filename" : "btn_radio_u@2x.png",
      "filename" : "btn_radio_u.png",
      "idiom" : "universal",
      "scale" : "2x"
    },
    {
      "filename" : "btn_radio_u@2x.png",
      "idiom" : "universal",
      "scale" : "3x"
    }
DolphinEnglishLearnStudent/Assets.xcassets/Btn/btn_refresh.imageset/Contents.json
@@ -1,16 +1,16 @@
{
  "images" : [
    {
      "filename" : "btn_refresh.png",
      "idiom" : "universal",
      "scale" : "1x"
    },
    {
      "filename" : "btn_refresh@2x.png",
      "filename" : "btn_refresh.png",
      "idiom" : "universal",
      "scale" : "2x"
    },
    {
      "filename" : "btn_refresh@2x.png",
      "idiom" : "universal",
      "scale" : "3x"
    }
DolphinEnglishLearnStudent/Assets.xcassets/Btn/placeH.imageset/Contents.json
@@ -1,16 +1,16 @@
{
  "images" : [
    {
      "filename" : "placeH.png",
      "idiom" : "universal",
      "scale" : "1x"
    },
    {
      "filename" : "placeH@2x.png",
      "filename" : "placeH.png",
      "idiom" : "universal",
      "scale" : "2x"
    },
    {
      "filename" : "placeH@2x.png",
      "idiom" : "universal",
      "scale" : "3x"
    }
DolphinEnglishLearnStudent/Assets.xcassets/Icon/icon_answer.imageset/Contents.json
@@ -1,12 +1,12 @@
{
  "images" : [
    {
      "filename" : "icon_answer.png",
      "filename" : "zuiba-xuanzhong@2x.png",
      "idiom" : "universal",
      "scale" : "1x"
    },
    {
      "filename" : "icon_answer@2x.png",
      "filename" : "zuiba-xuanzhong@3x.png",
      "idiom" : "universal",
      "scale" : "2x"
    },
DolphinEnglishLearnStudent/Assets.xcassets/Icon/icon_answer.imageset/icon_answer.png
Binary files differ
DolphinEnglishLearnStudent/Assets.xcassets/Icon/icon_answer.imageset/icon_answer@2x.png
Binary files differ
DolphinEnglishLearnStudent/Assets.xcassets/Icon/icon_answer.imageset/zuiba-xuanzhong@2x.png
DolphinEnglishLearnStudent/Assets.xcassets/Icon/icon_answer.imageset/zuiba-xuanzhong@3x.png
DolphinEnglishLearnStudent/Assets.xcassets/Icon/icon_play.imageset/Contents.json
@@ -1,16 +1,16 @@
{
  "images" : [
    {
      "filename" : "icon_play.png",
      "idiom" : "universal",
      "scale" : "1x"
    },
    {
      "filename" : "icon_play@2x.png",
      "filename" : "play@2x.png",
      "idiom" : "universal",
      "scale" : "2x"
    },
    {
      "filename" : "play@3x.png",
      "idiom" : "universal",
      "scale" : "3x"
    }
DolphinEnglishLearnStudent/Assets.xcassets/Icon/icon_play.imageset/icon_play.png
Binary files differ
DolphinEnglishLearnStudent/Assets.xcassets/Icon/icon_play.imageset/icon_play@2x.png
Binary files differ
DolphinEnglishLearnStudent/Assets.xcassets/Icon/icon_play.imageset/play@2x.png
DolphinEnglishLearnStudent/Assets.xcassets/Icon/icon_play.imageset/play@3x.png
DolphinEnglishLearnStudent/Assets.xcassets/Icon/icon_play_1.imageset/Contents.json
@@ -1,16 +1,16 @@
{
  "images" : [
    {
      "filename" : "icon_play_1.png",
      "idiom" : "universal",
      "scale" : "1x"
    },
    {
      "filename" : "icon_play_1@2x.png",
      "filename" : "Group 4@2x.png",
      "idiom" : "universal",
      "scale" : "2x"
    },
    {
      "filename" : "Group 4@3x.png",
      "idiom" : "universal",
      "scale" : "3x"
    }
DolphinEnglishLearnStudent/Assets.xcassets/Icon/icon_play_1.imageset/Group 4@2x.png
DolphinEnglishLearnStudent/Assets.xcassets/Icon/icon_play_1.imageset/Group 4@3x.png
DolphinEnglishLearnStudent/Assets.xcassets/Icon/icon_play_1.imageset/icon_play_1.png
Binary files differ
DolphinEnglishLearnStudent/Assets.xcassets/Icon/icon_play_1.imageset/icon_play_1@2x.png
Binary files differ
DolphinEnglishLearnStudent/Assets.xcassets/Icon/icon_playing.imageset/Contents.json
@@ -1,16 +1,16 @@
{
  "images" : [
    {
      "filename" : "icon_playing.png",
      "idiom" : "universal",
      "scale" : "1x"
    },
    {
      "filename" : "icon_playing@2x.png",
      "filename" : "icon_playing.png",
      "idiom" : "universal",
      "scale" : "2x"
    },
    {
      "filename" : "icon_playing@2x.png",
      "idiom" : "universal",
      "scale" : "3x"
    }
DolphinEnglishLearnStudent/Assets.xcassets/Icon/icon_poker.imageset/Contents.json
@@ -1,16 +1,16 @@
{
  "images" : [
    {
      "filename" : "icon_poker.png",
      "idiom" : "universal",
      "scale" : "1x"
    },
    {
      "filename" : "icon_poker@2x.png",
      "filename" : "icon_poker.png",
      "idiom" : "universal",
      "scale" : "2x"
    },
    {
      "filename" : "icon_poker@2x.png",
      "idiom" : "universal",
      "scale" : "3x"
    }
DolphinEnglishLearnStudent/Assets.xcassets/Icon/icon_question.imageset/Contents.json
@@ -1,16 +1,16 @@
{
  "images" : [
    {
      "filename" : "icon_question.png",
      "idiom" : "universal",
      "scale" : "1x"
    },
    {
      "filename" : "icon_question@2x.png",
      "filename" : "wenhao (1)@2x.png",
      "idiom" : "universal",
      "scale" : "2x"
    },
    {
      "filename" : "wenhao (1)@3x.png",
      "idiom" : "universal",
      "scale" : "3x"
    }
DolphinEnglishLearnStudent/Assets.xcassets/Icon/icon_question.imageset/icon_question.png
Binary files differ
DolphinEnglishLearnStudent/Assets.xcassets/Icon/icon_question.imageset/icon_question@2x.png
Binary files differ
DolphinEnglishLearnStudent/Assets.xcassets/Icon/icon_question.imageset/wenhao (1)@2x.png
DolphinEnglishLearnStudent/Assets.xcassets/Icon/icon_question.imageset/wenhao (1)@3x.png
DolphinEnglishLearnStudent/Assets.xcassets/Icon/icon_share.imageset/Contents.json
@@ -1,14 +1,18 @@
{
  "images" : [
    {
      "filename" : "icon_share.png",
      "idiom" : "ipad",
      "idiom" : "universal",
      "scale" : "1x"
    },
    {
      "filename" : "icon_share@2x.png",
      "idiom" : "ipad",
      "filename" : "icon_share.png",
      "idiom" : "universal",
      "scale" : "2x"
    },
    {
      "filename" : "icon_share@2x.png",
      "idiom" : "universal",
      "scale" : "3x"
    }
  ],
  "info" : {
DolphinEnglishLearnStudent/Assets.xcassets/Icon/icon_success.imageset/Contents.json
@@ -1,16 +1,16 @@
{
  "images" : [
    {
      "filename" : "icon_success.png",
      "idiom" : "universal",
      "scale" : "1x"
    },
    {
      "filename" : "icon_success@2x.png",
      "filename" : "icon_success.png",
      "idiom" : "universal",
      "scale" : "2x"
    },
    {
      "filename" : "icon_success@2x.png",
      "idiom" : "universal",
      "scale" : "3x"
    }
DolphinEnglishLearnStudent/Assets.xcassets/Icon/icon_success_small.imageset/Contents.json
@@ -1,16 +1,16 @@
{
  "images" : [
    {
      "filename" : "icon_success_small.png",
      "idiom" : "universal",
      "scale" : "1x"
    },
    {
      "filename" : "icon_success_small@2x.png",
      "filename" : "icon_success_small.png",
      "idiom" : "universal",
      "scale" : "2x"
    },
    {
      "filename" : "icon_success_small@2x.png",
      "idiom" : "universal",
      "scale" : "3x"
    }
DolphinEnglishLearnStudent/Assets.xcassets/Icon/icon_vip.imageset/Contents.json
@@ -1,16 +1,16 @@
{
  "images" : [
    {
      "filename" : "icon_vip.png",
      "idiom" : "universal",
      "scale" : "1x"
    },
    {
      "filename" : "icon_vip@2x.png",
      "filename" : "icon_vip.png",
      "idiom" : "universal",
      "scale" : "2x"
    },
    {
      "filename" : "icon_vip@2x.png",
      "idiom" : "universal",
      "scale" : "3x"
    }
DolphinEnglishLearnStudent/Assets.xcassets/Icon/icon_waring.imageset/Contents.json
@@ -1,16 +1,16 @@
{
  "images" : [
    {
      "filename" : "icon_waring.png",
      "idiom" : "universal",
      "scale" : "1x"
    },
    {
      "filename" : "icon_waring@2x.png",
      "filename" : "icon_waring.png",
      "idiom" : "universal",
      "scale" : "2x"
    },
    {
      "filename" : "icon_waring@2x.png",
      "idiom" : "universal",
      "scale" : "3x"
    }
DolphinEnglishLearnStudent/Assets.xcassets/Icon/icon_waring_small.imageset/Contents.json
@@ -1,16 +1,16 @@
{
  "images" : [
    {
      "filename" : "icon_waring_small.png",
      "idiom" : "universal",
      "scale" : "1x"
    },
    {
      "filename" : "icon_waring_small@2x.png",
      "filename" : "icon_waring_small.png",
      "idiom" : "universal",
      "scale" : "2x"
    },
    {
      "filename" : "icon_waring_small@2x.png",
      "idiom" : "universal",
      "scale" : "3x"
    }
DolphinEnglishLearnStudent/Assets.xcassets/Icon/share_wx.imageset/Contents.json
@@ -1,16 +1,16 @@
{
  "images" : [
    {
      "filename" : "share_wx.png",
      "idiom" : "universal",
      "scale" : "1x"
    },
    {
      "filename" : "share_wx@2x.png",
      "filename" : "share_wx.png",
      "idiom" : "universal",
      "scale" : "2x"
    },
    {
      "filename" : "share_wx@2x.png",
      "idiom" : "universal",
      "scale" : "3x"
    }
DolphinEnglishLearnStudent/Assets.xcassets/Icon/share_wxFri.imageset/Contents.json
@@ -1,16 +1,16 @@
{
  "images" : [
    {
      "filename" : "share_wxFri.png",
      "idiom" : "universal",
      "scale" : "1x"
    },
    {
      "filename" : "share_wxFri@2x.png",
      "filename" : "share_wxFri.png",
      "idiom" : "universal",
      "scale" : "2x"
    },
    {
      "filename" : "share_wxFri@2x.png",
      "idiom" : "universal",
      "scale" : "3x"
    }
DolphinEnglishLearnStudent/Base/BaseVC.swift
@@ -14,113 +14,113 @@
class BaseVC: UIViewController {
                var disposeBag:DisposeBag!
                let refreshStatus = BehaviorSubject(value: RefreshStatus.others)
    var disposeBag:DisposeBag!
    let refreshStatus = BehaviorSubject(value: RefreshStatus.others)
                var yy_popBlock:(() -> Void)?
                open var nav_back_img:UIImage = UIImage.init(named: "btn_back") ?? UIImage.init() {
                                didSet {
                                                let btn = navigationItem.leftBarButtonItem?.customView as! UIButton
                                                btn.setImage(nav_back_img, for: .normal)
                                }
                }
    var yy_popBlock:(() -> Void)?
    open var nav_back_img:UIImage = UIImage.init(named: "btn_back") ?? UIImage.init() {
        didSet {
            let btn = navigationItem.leftBarButtonItem?.customView as! UIButton
            btn.setImage(nav_back_img, for: .normal)
        }
    }
                override func viewWillAppear(_ animated: Bool) {
                                super.viewWillAppear(animated)
                                navigationController?.delegate?.navigationController?(navigationController!, willShow: self, animated: true)
                }
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        navigationController?.delegate?.navigationController?(navigationController!, willShow: self, animated: true)
    }
                override func viewDidLoad() {
                                super.viewDidLoad()
    override func viewDidLoad() {
        super.viewDidLoad()
                                disposeBag = DisposeBag()
                                setUI()
                                setRx()
                                setData()
        disposeBag = DisposeBag()
        setUI()
        setRx()
        setData()
                                if navigationController?.viewControllers.count ?? 0 > 1{
                                                let backButton = QMUIButton(type: .custom)
                                                backButton.setImage(UIImage(named: "btn_back"), for: .normal)
                                                backButton.setTitle(self.title, for: .normal)
                                                backButton.setTitleColor(.black.withAlphaComponent(0.81), for: .normal)
                                                backButton.titleLabel?.font = UIFont.systemFont(ofSize: 18, weight: .medium)
                                                backButton.imagePosition = .left
                                                backButton.spacingBetweenImageAndTitle = 35
                                                backButton.addTarget(self, action: #selector(backItemEvent), for: .touchUpInside)
                                                navigationItem.leftBarButtonItem = UIBarButtonItem(customView: backButton)
                                }
        if navigationController?.viewControllers.count ?? 0 > 1{
            let backButton = QMUIButton(type: .custom)
            backButton.setImage(UIImage(named: "btn_back"), for: .normal)
            backButton.setTitle(self.title, for: .normal)
            backButton.setTitleColor(.black.withAlphaComponent(0.81), for: .normal)
            backButton.titleLabel?.font = UIFont.systemFont(ofSize: 18, weight: .medium)
            backButton.imagePosition = .left
            backButton.spacingBetweenImageAndTitle = 35
            backButton.addTarget(self, action: #selector(backItemEvent), for: .touchUpInside)
            navigationItem.leftBarButtonItem = UIBarButtonItem(customView: backButton)
        }
                                if !self.isKind(of: HomeVC.self) && !self.isKind(of: HomeListenSubVC.self) && !self.isKind(of: HomeListenFight_lesson_1_VC.self) && !self.isKind(of: HomeListenFight_lesson_2_VC.self) && !self.isKind(of: HomeListenFight_lesson_3_VC.self) && !self.isKind(of: HomeListenFight_lesson_4_VC.self) && !self.isKind(of: HomeListenFight_lesson_5_VC.self) && !self.isKind(of: HomeListenGame_1_VC.self) && !self.isKind(of: HomeListenGame_2_VC.self) && !self.isKind(of: HomeListenStory_1_VC.self) && !self.isKind(of: HomeListenStory_2_VC.self) && !self.isKind(of: LoginVC.self){
                                                let titleV = UIView()
//                                                titleV.bounds = CGRect(x: 0, y: 0, width: 156, height: 24)
                                                titleV.sizeToFit()
                                                let imgV = UIImageView(image: UIImage(named: "bg_logo"))
                                                imgV.contentMode = .scaleAspectFit
                                                titleV.addSubview(imgV)
                                                imgV.snp.makeConstraints { make in
                                                                make.edges.equalToSuperview()
                                                }
        if !self.isKind(of: HomeVC.self) && !self.isKind(of: HomeListenSubVC.self) && !self.isKind(of: HomeListenFight_lesson_1_VC.self) && !self.isKind(of: HomeListenFight_lesson_2_VC.self) && !self.isKind(of: HomeListenFight_lesson_3_VC.self) && !self.isKind(of: HomeListenFight_lesson_4_VC.self) && !self.isKind(of: HomeListenFight_lesson_5_VC.self) && !self.isKind(of: HomeListenGame_1_VC.self) && !self.isKind(of: HomeListenGame_2_VC.self) && !self.isKind(of: HomeListenStory_1_VC.self) && !self.isKind(of: HomeListenStory_2_VC.self) && !self.isKind(of: LoginVC.self){
            let titleV = UIView()
            //                                                titleV.bounds = CGRect(x: 0, y: 0, width: 156, height: 24)
            titleV.sizeToFit()
            let imgV = UIImageView(image: UIImage(named: "bg_logo"))
            imgV.contentMode = .scaleAspectFit
            titleV.addSubview(imgV)
            imgV.snp.makeConstraints { make in
                make.edges.equalToSuperview()
            }
                                                view.addSubview(titleV)
                                                titleV.snp.makeConstraints { make in
                                                                make.center.equalToSuperview()
                                                }
            view.addSubview(titleV)
            titleV.snp.makeConstraints { make in
                make.center.equalToSuperview()
            }
                                                navigationItem.titleView = titleV
                                }
                }
            navigationItem.titleView = titleV
        }
    }
                func setRx(){
                }
    func setRx(){
    }
                func setUI(){
                                view.backgroundColor = Config.ThemeBGColor
    func setUI(){
        view.backgroundColor = Config.ThemeBGColor
                }
    }
                func setData(){
    func setData(){
                }
    }
                func refreshUI(){}
    func refreshUI(){}
                func push(vc:UIViewController){
                                vc.hidesBottomBarWhenPushed = true
                                navigationController?.pushViewController(vc, animated: true)
                }
    func push(vc:UIViewController){
        vc.hidesBottomBarWhenPushed = true
        navigationController?.pushViewController(vc, animated: true)
    }
                override var preferredStatusBarStyle: UIStatusBarStyle{
                                return .lightContent
                }
    override var preferredStatusBarStyle: UIStatusBarStyle{
        return .lightContent
    }
                @objc fileprivate func backItemEvent() {
                                // 拦截pop事件
                                if (yy_popBlock != nil) {
                                                yy_popBlock?()
                                                return
                                }
                                navigationController?.popViewController(animated: true)
                }
    @objc fileprivate func backItemEvent() {
        // 拦截pop事件
        if (yy_popBlock != nil) {
            yy_popBlock?()
            return
        }
        navigationController?.popViewController(animated: true)
    }
                override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
                                coordinator.animate(alongsideTransition: { [weak self] (context) in
                                                let orient = UIApplication.shared.statusBarOrientation
                                                switch orient {
                                                                case .landscapeLeft, .landscapeRight:
                                                                                //横屏时禁止左拽滑出
                                                                                self?.navigationController?.interactivePopGestureRecognizer?.isEnabled = false
                                                                default:
                                                                                //竖屏时允许左拽滑出
                                                                                self?.navigationController?.interactivePopGestureRecognizer?.isEnabled = true
                                                }
                                })
                                super.viewWillTransition(to: size, with: coordinator)
                }
    override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
        coordinator.animate(alongsideTransition: { [weak self] (context) in
            let orient = UIApplication.shared.statusBarOrientation
            switch orient {
            case .landscapeLeft, .landscapeRight:
                //横屏时禁止左拽滑出
                self?.navigationController?.interactivePopGestureRecognizer?.isEnabled = false
            default:
                //竖屏时允许左拽滑出
                self?.navigationController?.interactivePopGestureRecognizer?.isEnabled = true
            }
        })
        super.viewWillTransition(to: size, with: coordinator)
    }
                deinit {
                                LogInfo(String(format: "%@ 已释放", NSStringFromClass(self.classForCoder).components(separatedBy: ".").last!))
                }
    deinit {
        LogInfo(String(format: "%@ 已释放", NSStringFromClass(self.classForCoder).components(separatedBy: ".").last!))
    }
}
DolphinEnglishLearnStudent/Config/Config.swift
@@ -19,117 +19,123 @@
let ShareAppleKey = "af37e916cd2b4a0293e37ac405ba4f1c"
var sceneDelegate:SceneDelegate? = {
                var uiScreen:UIScene?
                UIApplication.shared.connectedScenes.forEach { scenes in
                                uiScreen = scenes
                }
                return (uiScreen?.delegate as? SceneDelegate)
    var uiScreen:UIScene?
    UIApplication.shared.connectedScenes.forEach { scenes in
        uiScreen = scenes
    }
    return (uiScreen?.delegate as? SceneDelegate)
}()
struct Config {
                static let ThemeBGColor:UIColor = UIColor(hexStr: "#C3BFB3")
                static let ThemeColor:UIColor = UIColor(hexStr: "#4195D3")
                static let NavFontColor = UIColor.black.withAlphaComponent(0.8)
                static let NavFont = UIFont.systemFont(ofSize: 15, weight: .medium)
    static let ThemeBGColor:UIColor = UIColor(hexStr: "#C3BFB3")
    static let ThemeColor:UIColor = UIColor(hexStr: "#4195D3")
    static let NavFontColor = UIColor.black.withAlphaComponent(0.8)
    static let NavFont = UIFont.systemFont(ofSize: 15, weight: .medium)
                static var RatioW:Double{get{return JQ_ScreenW / 810.0}}
                static var RatioH:Double{get{return JQ_ScreenH / 1080.0}}
    static var RatioW:Double{get{return JQ_ScreenW / 810.0}}
    static var RatioH:Double{get{return JQ_ScreenH / 1080.0}}
}
func LogSuccess(_ items:Any...,separator:String=" ",file:String=#file,function:String=#function,line:Int=#line){
#if DEBUG
                if #available(iOS 14.0, *) {
                                let logger = Logger(subsystem: "English", category: function)
                                logger.error("\(items)")
                }else{
                                let file = (file as NSString).lastPathComponent.split(separator: ".").first!;
                                print("✅✅✅ SUCCESS: \(file)  \(function) [Line: \(line)]: \(items)",separator);
                }
    if #available(iOS 14.0, *) {
        let logger = Logger(subsystem: "English", category: function)
        logger.error("\(items)")
    }else{
        let file = (file as NSString).lastPathComponent.split(separator: ".").first!;
        print("✅✅✅ SUCCESS: \(file)  \(function) [Line: \(line)]: \(items)",separator);
    }
#endif
}
func LogError(_ items:Any...,separator:String=" ",file:String=#file,function:String=#function,line:Int=#line){
#if DEBUG
                if #available(iOS 14.0, *) {
                                let logger = Logger(subsystem: "English", category: function)
                                logger.error("\(items)")
                }else{
                                let file = (file as NSString).lastPathComponent.split(separator: ".").first!;
                                print("❌❌❌ ERROR: \(file)  \(function) [Line: \(line)]: \(items)",separator);
                }
    if #available(iOS 14.0, *) {
        let logger = Logger(subsystem: "English", category: function)
        logger.error("\(items)")
    }else{
        let file = (file as NSString).lastPathComponent.split(separator: ".").first!;
        print("❌❌❌ ERROR: \(file)  \(function) [Line: \(line)]: \(items)",separator);
    }
#endif
}
func LogInfo(_ items:Any...,separator:String=" ",file:String=#file,function:String=#function,line:Int=#line){
#if DEBUG
                if #available(iOS 14.0, *) {
                                let logger = Logger(subsystem: "English", category: function)
                                logger.error("\(items)")
                }else{
                                let file = (file as NSString).lastPathComponent.split(separator: ".").first!;
                                print("⚠️⚠️⚠️INFO: \(file)  \(function) [Line: \(line)]: \(items)",separator);
                }
    if #available(iOS 14.0, *) {
        let logger = Logger(subsystem: "English", category: function)
        logger.error("\(items)")
    }else{
        let file = (file as NSString).lastPathComponent.split(separator: ".").first!;
        print("⚠️⚠️⚠️INFO: \(file)  \(function) [Line: \(line)]: \(items)",separator);
    }
#endif
}
func LogResponse(_ items:Any...,separator:String=" ",file:String=#file,function:String=#function,line:Int=#line){
#if DEBUG
                print("返回数据")
                print(items);
    print("返回数据")
    print(items);
#endif
}
//提示框
func alert(msg: String) {
                SVProgressHUD.showInfo(withStatus: msg)
    SVProgressHUD.showInfo(withStatus: msg)
}
func alertError(msg:String){
                SVProgressHUD.showError(withStatus: msg)
    SVProgressHUD.showError(withStatus: msg)
}
func alertSuccess(msg:String){
                SVProgressHUD.showSuccess(withStatus: msg)
    SVProgressHUD.showSuccess(withStatus: msg)
}
func showHUD(_ text:String? = nil){
                SVProgressHUD.show(withStatus: text)
    SVProgressHUD.show(withStatus: text)
}
func hiddenHUD(_ delay:TimeInterval? = nil){
                if delay != nil{
                                SVProgressHUD.dismiss(withDelay: delay!)
                }else{
                                SVProgressHUD.dismiss()
                }
    if delay != nil{
        SVProgressHUD.dismiss(withDelay: delay!)
    }else{
        SVProgressHUD.dismiss()
    }
}
extension UIButton{
                public func openCountDown(_ t:Int = 59,defultTitle:String = "获取验证码",textColor:UIColor,unenableColor:UIColor){
                                var time = t //倒计时时间
                                let queue = DispatchQueue.main
                                let timer = DispatchSource.makeTimerSource(flags: [], queue: queue)
                                timer.schedule(wallDeadline: DispatchWallTime.now(), repeating: .seconds(1));
                                timer.setEventHandler(handler: {
                                                if time <= 0 {
                                                                timer.cancel()
                                                                DispatchQueue.main.async(execute: {
                                                                                self.setTitle(defultTitle, for: .normal)
                                                                                self.setTitleColor(textColor, for: .normal)
                                                                                self.isUserInteractionEnabled = true
                                                                });
                                                }else {
                                                                self.setTitle("\(time)s", for: .normal)
                                                                self.setTitleColor(unenableColor, for: .normal)
                                                                self.isUserInteractionEnabled = false
                                                }
                                                time -= 1
                                });
                                timer.resume()
                }
    public func openCountDown(_ t:Int = 59,defultTitle:String = "获取验证码",textColor:UIColor,unenableColor:UIColor){
        var time = t //倒计时时间
        let queue = DispatchQueue.main
        let timer = DispatchSource.makeTimerSource(flags: [], queue: queue)
        timer.schedule(wallDeadline: DispatchWallTime.now(), repeating: .seconds(1));
        timer.setEventHandler(handler: {
            if time <= 0 {
                timer.cancel()
                DispatchQueue.main.async(execute: {
                    self.setTitle(defultTitle, for: .normal)
                    self.setTitleColor(textColor, for: .normal)
                    self.isUserInteractionEnabled = true
                });
            }else {
                self.setTitle("\(time)s", for: .normal)
                self.setTitleColor(unenableColor, for: .normal)
                self.isUserInteractionEnabled = false
            }
            time -= 1
        });
        timer.resume()
    }
}
extension UIImage{
    var themeGreen:UIImage{
        return self.withTintColor(UIColor(hexStr: "#51aaed"))
    }
}
DolphinEnglishLearnStudent/Models/CommonModel.swift
@@ -10,443 +10,464 @@
import UserDefaultsStore
struct LoginModel:HandyJSON{
                var token:LoginTokenModel?
    var token:LoginTokenModel?
}
struct LoginTokenModel:HandyJSON,Identifiable,Codable{
                static let idKey = \LoginTokenModel.id
                var id: Int = 0
    static let idKey = \LoginTokenModel.id
    var id: Int = 0
                var access_token = ""
                var expires_in = 0
                var request_time = 0 //请求时间
    var access_token = ""
    var expires_in = 0
    var request_time = 0 //请求时间
                private static let userInfo = UserDefaultsStore<LoginTokenModel>(uniqueIdentifier: "LoginTokenModel")!
    private static let userInfo = UserDefaultsStore<LoginTokenModel>(uniqueIdentifier: "LoginTokenModel")!
                static func saveToken(_ model:LoginTokenModel){
                                do{
                                                try LoginTokenModel.userInfo.save(model)
                                }catch{
    static func saveToken(_ model:LoginTokenModel){
        do{
            try LoginTokenModel.userInfo.save(model)
        }catch{
                                }
                }
        }
    }
                static func isOverdue()->Bool{
                                if let token = LoginTokenModel.getToken(){
                                                //过期时间(秒)
                                                let overdueTimeval = token.expires_in * 60 + token.request_time
                                                if overdueTimeval < Int(Date().timeIntervalSince1970){
                                                                return true
                                                }
                                                return false
                                }
                                return true
                }
    static func isOverdue()->Bool{
        if let token = LoginTokenModel.getToken(){
            //过期时间(秒)
            let overdueTimeval = token.expires_in * 60 + token.request_time
                static func getToken()->LoginTokenModel?{
                                return LoginTokenModel.userInfo.allObjects().first
                }
            if overdueTimeval < Int(Date().timeIntervalSince1970){
                return true
            }
            return false
        }
        return true
    }
                static func clearToken(){
                                LoginTokenModel.userInfo.deleteAll()
                }
    static func getToken()->LoginTokenModel?{
        return LoginTokenModel.userInfo.allObjects().first
    }
    static func clearToken(){
        LoginTokenModel.userInfo.deleteAll()
    }
}
struct RecommendModel:HandyJSON{
                var basicCount: Int = 0
                var coverImg: String = ""
                var createBy: String = ""
                var createTime: String = ""
                var detail: String = ""
                var detailImg: String = ""
                var disabled: Bool = false
                var id: Int = 0
                var insertTime: String = ""
                var integral: Int = 0
                var inventory: Int = 0
                var isDelete: Int = 0
                var name: String = ""
                var price: Int = 0
                var surplus: Int = 0
                var total: Int = 0
                var type: Int = 0
                var typeIds: String = ""
                var updateBy: String = ""
                var updateTime: String = ""
                var userCount: Int = 0
    var basicCount: Int = 0
    var coverImg: String = ""
    var createBy: String = ""
    var createTime: String = ""
    var detail: String = ""
    var detailImg: String = ""
    var disabled: Bool = false
    var id: Int = 0
    var insertTime: String = ""
    var integral: Int = 0
    var inventory: Int = 0
    var isDelete: Int = 0
    var name: String = ""
    var price: Int = 0
    var surplus: Int = 0
    var total: Int = 0
    var type: Int = 0
    var typeIds: String = ""
    var updateBy: String = ""
    var updateTime: String = ""
    var userCount: Int = 0
}
struct MarketModel:HandyJSON{
                var basicCount: Int = 0
                var coverImg: String = ""
                var createBy: String = ""
                var createTime: String = ""
                var detail: String = ""
                var detailImg: String = ""
                var disabled: Bool = false
                var id: Int = 0
                var integral: Int = 0
                var inventory: Int = 0
                var isDelete: Int = 0
                var name: String = ""
                var price: Double = 0
                var surplus: Int?
                var total: Int = 0
                var type: Int = 0
                var typeIds: String = ""
                var updateBy: String = ""
                var updateTime: String = ""
                var userCount: Int?
    var basicCount: Int = 0
    var coverImg: String = ""
    var createBy: String = ""
    var createTime: String = ""
    var detail: String = ""
    var detailImg: String = ""
    var disabled: Bool = false
    var id: Int = 0
    var integral: Int = 0
    var inventory: Int = 0
    var isDelete: Int = 0
    var name: String = ""
    var price: Double = 0
    var surplus: Int?
    var total: Int = 0
    var type: Int = 0
    var typeIds: String = ""
    var updateBy: String = ""
    var updateTime: String = ""
    var userCount: Int?
}
struct MarketTypeModel:HandyJSON,Hashable{
                var id = 0
                var name = ""
    var id = 0
    var name = ""
}
struct MarketDetailModel:HandyJSON{
                var exchangeNumber: Int = 0
                var good: MarketModel?
                var goodTypes = [MarketTypeModel]()
                var orderNumber: String = ""
                var residueNumber:Int?
                var recipient: MarketRecipientModel?
    var exchangeNumber: Int = 0
    var good: MarketModel?
    var goodTypes = [MarketTypeModel]()
    var orderNumber: String = ""
    var residueNumber:Int?
    var recipient: MarketRecipientModel?
}
struct MarketRecipientModel:HandyJSON,Hashable{
                var id = 0
                var name = ""
    var id = 0
    var name = ""
}
struct AddressModel:HandyJSON{
                var address: String = ""
                var city: String = ""
                var cityCode: String = ""
                var createBy: String = ""
                var createTime: String = ""
                var disabled: Bool = false
                var id: Int = 0
                var isDefault: Int = 0
                var province: String = ""
                var provinceCode: String = ""
                var recipient: String = ""
                var recipientPhone: String = ""
                var updateBy: String = ""
                var updateTime: String = ""
                var userId: Int = 0
                var orderId:Int = 0
    var address: String = ""
    var city: String = ""
    var cityCode: String = ""
    var createBy: String = ""
    var createTime: String = ""
    var disabled: Bool = false
    var id: Int = 0
    var isDefault: Int = 0
    var province: String = ""
    var provinceCode: String = ""
    var recipient: String = ""
    var recipientPhone: String = ""
    var updateBy: String = ""
    var updateTime: String = ""
    var userId: Int = 0
    var orderId:Int = 0
}
struct AddressTreeModel:HandyJSON{
                var id = 0
                var name = ""
                var code = ""
                var parentId = 0
                var children:[AddressTreeModel]?
    var id = 0
    var name = ""
    var code = ""
    var parentId = 0
    var children:[AddressTreeModel]?
}
struct IntegralModel:HandyJSON{
                var createBy: String = ""
                var createTime: String = ""
                var disabled: Bool = false
                var gameId: Int = 0
                var id: Int = 0
                var integral: String = ""
                var method: String = ""
                var storyId: Int = 0
                var type: String = ""
                var updateBy: String = ""
                var updateTime: String = ""
                var userId: Int = 0
    var createBy: String = ""
    var createTime: String = ""
    var disabled: Bool = false
    var gameId: Int = 0
    var id: Int = 0
    var integral: String = ""
    var method: String = ""
    var storyId: Int = 0
    var type: String = ""
    var updateBy: String = ""
    var updateTime: String = ""
    var userId: Int = 0
}
struct ExchangeRecordModel:HandyJSON{
                var completeTime: String = ""
                var consigneeAddress: String = ""
                var consigneeName: String = ""
                var consigneePhone: String = ""
                var count: Int = 0
                var createBy: String = ""
                var createTime: String = ""
                var disabled: Bool = false
                var express: String = ""
                var expressNumber: String = ""
                var expressTime: String = ""
                var goodsId: Int = 0
                var goodsName: String = ""
                var id: Int = 0
                var insertTime: String = ""
                var integral: Int = 0
                var orderNumber: String = ""
                var orderId:Int = 0
                var state: Int = 0 //订单状态1待发货2已发货3已完成
                var updateBy: String = ""
                var updateTime: String = ""
                var userId: Int = 0
                var goodsType = [String]()
                var coverImg:String = ""
    var completeTime: String = ""
    var consigneeAddress: String = ""
    var consigneeName: String = ""
    var consigneePhone: String = ""
    var count: Int = 0
    var createBy: String = ""
    var createTime: String = ""
    var disabled: Bool = false
    var express: String = ""
    var expressNumber: String = ""
    var expressTime: String = ""
    var goodsId: Int = 0
    var goodsName: String = ""
    var id: Int = 0
    var insertTime: String = ""
    var integral: Int = 0
    var orderNumber: String = ""
    var orderId:Int = 0
    var state: Int = 0 //订单状态1待发货2已发货3已完成
    var updateBy: String = ""
    var updateTime: String = ""
    var userId: Int = 0
    var goodsType = [String]()
    var coverImg:String = ""
}
struct StudyGamesModel:HandyJSON{
                var gameRecordList = [StudyGamesRecordModel]()
                var record:StudyDataRecordModel?
    var gameRecordList = [StudyGamesRecordModel]()
    var record:StudyDataRecordModel?
}
struct StudyGamesRecordModel:HandyJSON{
                var accuracy: Int = 0
                var createBy: String = ""
                var time = ""
//                var createTime: String = ""
                var disabled: Bool = false
                var gameDifficulty: Int = 0
                var gameId: Int = 0
                var gameName: String = ""
                var id: Int = 0
                var updateBy: String = ""
//                var updateTime: String = ""
                var userId: Int = 0
                var useTime: Int = 0
    var accuracy: Int = 0
    var createBy: String = ""
    var time = ""
    //                var createTime: String = ""
    var disabled: Bool = false
    var gameDifficulty: Int = 0
    var gameId: Int = 0
    var gameName: String = ""
    var id: Int = 0
    var updateBy: String = ""
    //                var updateTime: String = ""
    var userId: Int = 0
    var useTime: Int = 0
}
struct StudyDataRecordModel:HandyJSON{
                var answer: Int = 0
                var createBy: String = ""
                var createTime: String = ""
                var day: Int = 0
                var disabled: Bool = false
                var id: Int = 0
                var induction: Int = 0
                var listen: Int = 0
                var look: Int = 0
                var monthStudy: Int = 0
                var pair: Int = 0
                var surplus: Int = 0
                var todayStudy: Int = 0
                var totalStudy: Int = 0
                var updateBy: String = ""
                var updateTime: String = ""
                var userId: Int = 0
                var week: Int = 0
                var weekStudy: Int = 0
    var answer: Int = 0
    var createBy: String = ""
    var createTime: String = ""
    var day: Int = 0
    var disabled: Bool = false
    var id: Int = 0
    var induction: Int = 0
    var listen: Int = 0
    var look: Int = 0
    var monthStudy: Int = 0
    var pair: Int = 0
    var surplus: Int = 0
    var todayStudy: Int = 0
    var totalStudy: Int = 0
    var updateBy: String = ""
    var updateTime: String = ""
    var userId: Int = 0
    var week: Int = 0
    var weekStudy: Int = 0
}
struct ListenWeekModel:HandyJSON{
                var id = 0
                var day = 0
                var quarter = 0
                var title = ""
                var totalIntegral = 0
                var type = 0
                var week = 0
                var canStudy = 0
    var id = 0
    var day = 0
    var quarter = 0
    var title = ""
    var totalIntegral = 0
    var type = 0
    var week = 0
    var canStudy = 0
}
class ListenNewModel:HandyJSON{
                var data:ListenNewDataModel?
                var subjectList = [[Listen1SubModel]]()
    var data:ListenNewDataModel?
    var list = [ListenSubCardModel]()
    var subjectList = [[Listen1SubModel]]()
    var accuracy:Double = 0
                required init(){}
    required init(){}
}
class ListenNewDataModel:HandyJSON{
                var id:String = ""
                var integral = 0
    var id:String = ""
    var integral = 0
                required init(){}
    required init(){}
}
class Listen1Model:HandyJSON{
                var data:Listen1DataModel?
                var subjectList = [Listen1SubModel]()
                var storyList = [Listen1SubModel]()
    var data:Listen1DataModel?
    var subjectList = [Listen1SubModel]()
    var storyList = [Listen1SubModel]()
                //超级记忆专用
                var photoList = [SimpleListenDataModel]()
                var voiceList = [SimpleListenDataModel]()
    //超级记忆专用
    var photoList = [SimpleListenDataModel]()
    var voiceList = [SimpleListenDataModel]()
                required init(){}
    required init(){}
}
struct TeamScheduleModel:HandyJSON{
                var answerNumber = 0
                var correctNumber = 0
                var teamIds = [Int]() //题组ids
                var topicIds = [Int]() //已回答正确的题目Id
                var schedule = 0
    var answerNumber = 0
    var correctNumber = 0
    var teamIds = [Int]() //题组ids
    var topicIds = [Int]() //已回答正确的题目Id
    var schedule = 0
}
struct Listen1DataModel:HandyJSON{
                var createBy: String = ""
                var createTime: String = ""
                var day: Int = 0
                var disabled: Bool = false
                var id: Int = 0
                var integral: Int = 0
                var isVip: Int = 0
                var studyId: Int = 0
                var subject: String = ""
                var updateBy: String = ""
                var updateTime: String = ""
                var week: Int = 0
                var answerCount = 0
                var answerIntegral = 0
                var answerTime = 0
                var time = 0
                var count = 0
                var lookIntegral = 0
    var createBy: String = ""
    var createTime: String = ""
    var day: Int = 0
    var disabled: Bool = false
    var id: Int = 0
    var integral: Int = 0
    var isVip: Int = 0
    var studyId: Int = 0
    var subject: String = ""
    var updateBy: String = ""
    var updateTime: String = ""
    var week: Int = 0
    var answerCount = 0
    var answerIntegral = 0
    var answerTime = 0
    var time = 0
    var count = 0
    var lookIntegral = 0
                //custom
                var playNow:Bool = false //立刻播放
    //custom
    var playNow:Bool = false //立刻播放
}
class ListenSubCardModel:HandyJSON{
    var createBy: String = ""
    var createTime: String = ""
    var day: Int = 0
    var disabled: Bool = false
    var id: Int = 0
    var objectId: Int = 0
    var one: Int = 0
    var status: Int = 0
    var two: Int = 0
    var type: Int = 0
    var updateBy: String = ""
    var updateTime: String = ""
    var userId: Int = 0
    var week: Int = 0
    required init(){}
}
class Listen1SubModel:HandyJSON,Hashable{
                static func == (lhs: Listen1SubModel, rhs: Listen1SubModel) -> Bool {
                                return lhs.id == rhs.id
                }
//                var hashValue: Int{
//                                return id
//                }
    static func == (lhs: Listen1SubModel, rhs: Listen1SubModel) -> Bool {
        return lhs.id == rhs.id
    }
                func hash(into hasher: inout Hasher) {
    //                var hashValue: Int{
    //                                return id
    //                }
                }
    func hash(into hasher: inout Hasher) {
    }
                required init() {}
    required init() {}
                var correct: String = ""
                var createBy: String = ""
                var createTime: String = ""
                var disabled: Bool = false
                var english: String = ""
                var error: String = ""
                var id: Int = 0
                var img: String = ""
                var name: String = ""
                var state: Int = 0
                var type: String = ""
                var updateBy: String = ""
                var updateTime: String = ""
    var correct: String = ""
    var createBy: String = ""
    var createTime: String = ""
    var disabled: Bool = false
    var english: String = ""
    var error: String = ""
    var id: Int = 0
    var img: String = ""
    var name: String = ""
    var state: Int = 0
    var type: String = ""
    var updateBy: String = ""
    var updateTime: String = ""
                //学习类型四专用
                var isQuestion:Int = 0
    //学习类型四专用
    var isQuestion:Int = 0
                //游戏类型2专用
                var isOpen:Bool = false
    //游戏类型2专用
    var isOpen:Bool = false
                // 自主学习1,3专用 (是否已回答)
                var isAnster:Bool = false
    // 自主学习1,3专用 (是否已回答)
    var isAnster:Bool = false
}
@available(*,deprecated,message: "废弃")
struct Listen4Model:HandyJSON{
                var data = [Listen4DataModel]()
    var data = [Listen4DataModel]()
}
struct Listen4DataModel:HandyJSON{
                var answerSubject: Int = 0
                var createBy: String = ""
                var createTime: String = ""
                var day: Int = 0
                var disabled: Bool = false
                var id: Int = 0
                var integral: Int = 0
                var isAnswer: Int = 0
                var isVip: Int = 0
                var studyId: Int = 0
                var subject: Int = 0
                var subjectList = [Listen1SubModel]()
                var updateBy: String = ""
                var updateTime: String = ""
                var week: Int = 0
    var answerSubject: Int = 0
    var createBy: String = ""
    var createTime: String = ""
    var day: Int = 0
    var disabled: Bool = false
    var id: Int = 0
    var integral: Int = 0
    var isAnswer: Int = 0
    var isVip: Int = 0
    var studyId: Int = 0
    var subject: Int = 0
    var subjectList = [Listen1SubModel]()
    var updateBy: String = ""
    var updateTime: String = ""
    var week: Int = 0
}
struct StudyScheduleModel:HandyJSON,Hashable{
                init() {}
                var answer: Int = 0
                var day: Int = 0
                var computeSchedule:Int = 0
                var id: Int = 0
                var induction: Int = 0
                var listen: Int = 0
                var look: Int = 0
                var monthStudy: Int = 0
                var pair: Int = 0
                var surplus: String = ""
                var todayStudy: Int = 0
                var totalStudy: Int = 0
                var userId: Int = 0
                var week: Int = 0
                var weekStudy: Int = 0
                var gameDifficulty:Int = 0
    init() {}
                var hashValue: Int{
                                return answer+day+computeSchedule+induction+listen+look+monthStudy+pair+todayStudy+totalStudy+week+weekStudy+gameDifficulty
                }
    var answer: Int = 0
    var day: Int = 0
    var computeSchedule:Int = 0
    var id: Int = 0
    var induction: Int = 0
    var listen: Int = 0
    var look: Int = 0
    var monthStudy: Int = 0
    var pair: Int = 0
    var surplus: String = ""
    var todayStudy: Int = 0
    var totalStudy: Int = 0
    var userId: Int = 0
    var week: Int = 0
    var weekStudy: Int = 0
    var gameDifficulty:Int = 0
    var hashValue: Int{
        return answer+day+computeSchedule+induction+listen+look+monthStudy+pair+todayStudy+totalStudy+week+weekStudy+gameDifficulty
    }
}
class SimpleListenDataModel:HandyJSON,Hashable{
                required init() {}
    required init() {}
                static func == (lhs: SimpleListenDataModel, rhs: SimpleListenDataModel) -> Bool {
                                return lhs.id == rhs.id
                }
    static func == (lhs: SimpleListenDataModel, rhs: SimpleListenDataModel) -> Bool {
        return lhs.id == rhs.id
    }
                func hash(into hasher: inout Hasher) {
    func hash(into hasher: inout Hasher) {
                }
    }
                var id = 0
                var photo = ""
                var voice = ""
    var id = 0
    var photo = ""
    var voice = ""
                //游戏类型2专用
                var isOpen:Bool = false
                var type = 0 // 1:图片 2:音频
    //游戏类型2专用
    var isOpen:Bool = false
    var type = 0 // 1:图片 2:音频
}
struct PromptVoiceModel:HandyJSON{
                var correct: String = ""
                var createBy: String = ""
                var createTime: String = ""
                var disabled: Bool = false
                var error: String = ""
                var id: Int = 0
                var img: String = ""
                var integral: String = ""
                var integralShare: String = ""
                var phone: String = ""
                var time: String = ""
                var title: String = ""
                var updateBy: String = ""
                var updateTime: String = ""
    var correct: String = ""
    var createBy: String = ""
    var createTime: String = ""
    var disabled: Bool = false
    var error: String = ""
    var id: Int = 0
    var img: String = ""
    var integral: String = ""
    var integralShare: String = ""
    var phone: String = ""
    var time: String = ""
    var title: String = ""
    var updateBy: String = ""
    var updateTime: String = ""
}
struct ShareInfoModel:HandyJSON{
                var title = ""
                var phone = ""
                var img = ""
    var title = ""
    var phone = ""
    var img = ""
}
struct VIPInfoModel:HandyJSON{
                var id = 0
                var info = ""
                var isVip = 0
                var time = 0
                var amount = 0
    var id = 0
    var info = ""
    var isVip = 0
    var time = 0
    var amount = 0
}
struct PaymentInfoModel:HandyJSON{
                var id = 0
                var orderId = 0
    var id = 0
    var orderId = 0
}
DolphinEnglishLearnStudent/Moudle/Home/HomeListenFight_lesson_1_VC.swift
@@ -10,348 +10,391 @@
import RxRelay
class FightAnswerViewModel{
                var selectIndex = BehaviorRelay<IndexPath?>(value: nil)
                var answerType = BehaviorRelay<Fight_lessonType>(value: .none)
    var selectIndex = BehaviorRelay<IndexPath?>(value: nil)
    var answerType = BehaviorRelay<Fight_lessonType>(value: .none)
}
/// 题目类型一
class HomeListenFight_lesson_1_VC: BaseVC {
                private var viewModel = FightAnswerViewModel()
                private var listenNewModel:ListenNewModel!
                private var randomElement:Listen1SubModel?
                private var page:Int!
                private(set) var isListen:Bool = false
                private var isAnsterComplete:Bool = false //是否已经回答完成[小题]
                private var isAnsterDone:Bool = false //是否已经回答完成[大题]
    private var viewModel = FightAnswerViewModel()
    private var listenNewModel:ListenNewModel!
    private var randomElement:Listen1SubModel?
    private var page:Int!
    private(set) var isListen:Bool = false
    private var isAnsterComplete:Bool = false //是否已经回答完成[小题]
    private var isAnsterDone:Bool = false //是否已经回答完成[大题]
                private var isAnsterModel = [Listen1SubModel]()
                private var menuView:VoiceHandleView?
    private var isAnsterModel = [Listen1SubModel]()
                private lazy var collectionView:UICollectionView = {
                                let flowLayout = UICollectionViewFlowLayout()
                                let w = (JQ_ScreenW - 194 * 2 - 25) / 2
                                flowLayout.itemSize = CGSize(width: w, height: w * 0.745)
                                flowLayout.minimumLineSpacing = 25
                                flowLayout.minimumInteritemSpacing = 25
                                flowLayout.scrollDirection = .vertical
                                let collection = UICollectionView(frame: .zero, collectionViewLayout: flowLayout)
                                collection.register(UINib(nibName: "ListenFight_lesson_1_CCell", bundle: nil), forCellWithReuseIdentifier: "_ListenFight_lesson_1_CCell")
                                collection.isScrollEnabled = false
                                return collection
                }()
    //已回答过的题
    private var answerList = [Listen1SubModel]()
                var rootViewModel:HomeListenFightViewModel!
                var teamScheduleModel:TeamScheduleModel?{
                                didSet{
                                                if let m = teamScheduleModel{
                                                                var temp = [Listen1SubModel]()
                                                                for v in listenNewModel.subjectList[page]{
                                                                                //已回答
                                                                                if m.topicIds.contains(v.id){
                                                                                                temp.append(v)
                                                                                }
                                                                }
                                                                isAnsterModel.insert(contentsOf: temp, at: 0)
    private var menuView:VoiceHandleView?
    private var handleClouse:(()->Void)?
                                                                //todo
//                                                                let teamId = weakSelf.listenNewModel.data?.id.components(separatedBy: ",")[weakSelf.page]
//                                                                weakSelf.rootViewModel.insertCorrectAnswer(teamId: teamId, answerId: weakSelf.listenNewModel.subjectList[weakSelf.page][index.row].id)
    private lazy var collectionView:UICollectionView = {
        let flowLayout = UICollectionViewFlowLayout()
        let w = (JQ_ScreenW - 194 * 2 - 25) / 2
        flowLayout.itemSize = CGSize(width: w, height: w * 0.745)
        flowLayout.minimumLineSpacing = 25
        flowLayout.minimumInteritemSpacing = 25
        flowLayout.scrollDirection = .vertical
        let collection = UICollectionView(frame: .zero, collectionViewLayout: flowLayout)
        collection.register(UINib(nibName: "ListenFight_lesson_1_CCell", bundle: nil), forCellWithReuseIdentifier: "_ListenFight_lesson_1_CCell")
        collection.isScrollEnabled = false
        return collection
    }()
    var rootViewModel:HomeListenFightViewModel!
    var teamScheduleModel:TeamScheduleModel?{
        didSet{
            if let m = teamScheduleModel{
                var temp = [Listen1SubModel]()
                for v in listenNewModel.subjectList[page]{
                    //已回答
                    if m.topicIds.contains(v.id){
                        temp.append(v)
                    }
                }
                isAnsterModel.insert(contentsOf: temp, at: 0)
            }
        }
    }
    required init(page:Int,listenNewModel:ListenNewModel){
        super.init(nibName: nil, bundle: nil)
        self.page = page
        self.listenNewModel = listenNewModel
    }
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    override func viewDidLoad() {
        super.viewDidLoad()
        navigationItem.titleView = UIView()
        collectionView.reloadData()
        print("加载======DidLoad")
        //回传记录,始终保持答题进度
//        if let team = teamScheduleModel{
//            let v = team.schedule - 1
//            for i in 0..<v{
//                isAnsterModel.append(listenNewModel.subjectList[page][i])
//            }
//        }
        //制造随机
        //        listenNewModel.subjectList[page].shuffle()
        getNextAnswer(isFirst: true)
        menuView?.listenType = .lesson1
        DispatchQueue.main.asyncAfter(deadline: .now()+2) {
            VoicePlayer.share().playerAt(url: self.randomElement?.correct)
            self.menuView?.playing()
        }
    }
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        VoicePlayer.share().delegate = self
    }
    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        VoicePlayer.share().delegate = nil
        VoicePlayer.share().playerInterrupt()
    }
    func restore(){
        teamScheduleModel?.topicIds.removeAll()
        teamScheduleModel?.teamIds.removeAll()
        answerList.removeAll()
        rootViewModel.currentPage.accept(0)
        rootViewModel.answerCount.accept(1)
        isAnsterDone = false
        isAnsterModel.removeAll()
        isAnsterComplete = false
        viewModel.answerType.accept(.none)
        viewModel.selectIndex.accept(nil)
        menuView?.resetView()
        setUI()
        collectionView.reloadData()
        getNextAnswer(isFirst: true)
    }
                                                }
                                }
                }
    func tobefore(){
        if isAnsterModel.count == 1{return}
                required init(page:Int,listenNewModel:ListenNewModel){
                                super.init(nibName: nil, bundle: nil)
                                self.page = page
                                self.listenNewModel = listenNewModel
                }
        isAnsterModel.removeLast()
                required init?(coder: NSCoder) {
                                fatalError("init(coder:) has not been implemented")
                }
        rootViewModel.answerCount.accept(page + isAnsterModel.count)
                override func viewDidLoad() {
                                super.viewDidLoad()
                                navigationItem.titleView = UIView()
                                collectionView.reloadData()
                                print("加载======DidLoad")
        randomElement = isAnsterModel.last
        menuView?.playUrl = randomElement?.correct
        VoicePlayer.share().playerAt(url: self.randomElement?.correct)
        menuView?.playing()
                                //回传记录,始终保持答题进度
                                if let team = teamScheduleModel{
                                                let v = team.schedule - 1
                                                for i in 0..<v{
                                                                isAnsterModel.append(listenNewModel.subjectList[page][i])
                                                }
                                }
        //        listenNewModel.subjectList[page].shuffle()
        collectionView.reloadData()
    }
                                //制造随机
                                listenNewModel.subjectList[page].shuffle()
                                getNextAnswer(isFirst: true)
                                menuView?.listenType = .lesson1
    /// 下一题
    /// - Parameter isFirst: 是否首次进入,首次页码不+1
    private func getNextAnswer(isFirst:Bool = false){
        isListen = false
        if isAnsterModel.count == 4{
                                DispatchQueue.main.asyncAfter(deadline: .now()+2) {
                                                VoicePlayer.share().playerAt(url: self.randomElement?.correct)
                                                self.menuView?.playing()
                                }
            DispatchQueue.main.asyncAfter(deadline: .now()+2){[weak self] in
                guard let weakSelf = self else { return }
                NotificationCenter.default.post(name: NextLession_Noti, object: nil)
                weakSelf.viewModel.answerType.accept(.none)
                weakSelf.viewModel.selectIndex.accept(nil)
                weakSelf.isListen = false
                let v = weakSelf.rootViewModel.answerCount.value
                weakSelf.rootViewModel.answerCount.accept(v + 1)
            }
            return
        }
        randomElement = listenNewModel.subjectList[page].randomElement() //随机抽题
        if randomElement != nil{
            //如果已经回答,或标记为已回答(返回上一小题时)
            if isAnsterModel.contains(randomElement!){
                answerList.append(randomElement!)
                getNextAnswer();return
            }
            //没有回答
            if !isAnsterModel.contains(randomElement!){
                isAnsterModel.append(randomElement!)
            }
        }
        menuView?.playUrl = randomElement?.correct
        //        listenNewModel.subjectList[page].shuffle()
        isAnsterComplete = false
        viewModel.answerType.accept(.none)
        setUI()
        //自动播放下一题语音
        DispatchQueue.main.asyncAfter(deadline: .now()+2) {
            VoicePlayer.share().playerAt(url: self.randomElement?.correct)
            self.menuView?.playing()
        }
        if !isAnsterDone && isAnsterModel.count <= 4 && !isFirst{
            let v = rootViewModel.answerCount.value
            rootViewModel.answerCount.accept(v + 1)
        }
    }
    override func setUI() {
        super.setUI()
        if !view.subviews.contains(collectionView){
            collectionView.delegate = self
            collectionView.dataSource = self
            collectionView.backgroundColor = UIColor(hexStr: "#C3BFB3")
            view.addSubview(collectionView)
            collectionView.snp.makeConstraints { make in
                make.top.equalTo(self.view.safeAreaLayoutGuide.snp.top).offset(101)
                make.left.equalTo(194)
                make.right.equalTo(-194)
                make.bottom.equalToSuperview()
            }
        }
        if menuView == nil{
            menuView = VoiceHandleView()
            view.addSubview(menuView!)
            menuView?.snp.makeConstraints { make in
                make.top.equalTo(self.view.safeAreaLayoutGuide.snp.top).offset(24)
                make.centerX.equalToSuperview()
                //                                                                make.height.equalTo(52)
                make.height.equalTo(40)
                //                make.width.equalTo(159)
                make.width.equalTo(189)
            }
        }else{
            menuView?.snp.remakeConstraints { make in
                make.top.equalTo(self.view.safeAreaLayoutGuide.snp.top).offset(24)
                make.centerX.equalToSuperview()
                //                                                                make.height.equalTo(52)
                make.height.equalTo(40)
                //                                                                make.width.equalTo(159)
                make.width.equalTo(189)
            }
        }
        collectionView.reloadData()
    }
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        let flowLayout = self.collectionView.collectionViewLayout as! UICollectionViewFlowLayout
        let w = (self.collectionView.size.width - flowLayout.minimumLineSpacing) / 2
        let h = (self.collectionView.size.height - flowLayout.minimumInteritemSpacing) / 2
        if flowLayout.itemSize.width != w || flowLayout.itemSize.height != h{
            flowLayout.itemSize = CGSize(width: w, height: h)
            collectionView.reloadData()
        }
    }
    override func setRx() {
        viewModel.selectIndex.subscribe(onNext: {[weak self] index in
            guard let weakSelf = self else { return }
            guard let index = index else { return }
            if weakSelf.viewModel.answerType.value == .success {return}
            //判断选中是否正确逻辑
            if let cell = self?.collectionView.cellForItem(at: index) as? ListenFight_lesson_1_CCell{
                var answer:Fight_lessonType = .none
                if self?.randomElement?.id == weakSelf.listenNewModel.subjectList[weakSelf.page][index.row].id{
                    answer = .success
                    self?.randomElement?.isAnster = true
                    self?.isListen = false
                    if self?.isAnsterComplete == false{
                        self?.rootViewModel.correctNum += 1
                    }
                    self?.isAnsterComplete = true
                    VoicePlayer.share().playSuccessVoice()
                    let teamId = weakSelf.listenNewModel.data?.id.components(separatedBy: ",")[weakSelf.page]
                    weakSelf.rootViewModel.insertCorrectAnswer(teamId: teamId, answerId: weakSelf.listenNewModel.subjectList[weakSelf.page][index.row].id)
                    var cin = 0
                    for (ci,v) in weakSelf.listenNewModel.subjectList[weakSelf.page].enumerated(){
                        if v.id == self?.randomElement?.id{
                            cin = ci;break
                        }
                    }
                    if weakSelf.page >= 1{cin += 4}
                    let model = weakSelf.listenNewModel.list[cin]
                    model.status = 2
                    Services.answerQuestion(id: model.id, status: 2).subscribe(onNext: {_ in
                        weakSelf.handleClouse?()
                    }).disposed(by: weakSelf.disposeBag)
                    DispatchQueue.main.asyncAfter(deadline: .now()+2) {
                        VoicePlayer.share().playerAt(url: weakSelf.listenNewModel.subjectList[weakSelf.page][index.row].correct)
                    }
                }else{
                    answer = .fail
                    VoicePlayer.share().playFailVoice()
                    self?.isListen = false
                    if self?.isAnsterComplete == false{
                        self?.rootViewModel.errorNum += 1
                    }
                    var cin = 0
                    for (ci,v) in weakSelf.listenNewModel.subjectList[weakSelf.page].enumerated(){
                        if v.id == self?.randomElement?.id{
                            cin = ci;break
                        }
                    }
                    if weakSelf.page >= 1{cin += 4}
                    let model = weakSelf.listenNewModel.list[cin]
                    model.status = 3
                    Services.answerQuestion(id: model.id, status: 3).subscribe(onNext: {_ in
                        weakSelf.handleClouse?()
                    }).disposed(by: weakSelf.disposeBag)
                }
                switch answer {
                case .success:
                    self?.viewModel.answerType.accept(.success)
                    self?.answerSuccess(cell)
                case .fail:
                    self?.viewModel.answerType.accept(.fail)
                    self?.collectionView.reloadData()
                default:break
                }
            }
        }).disposed(by: disposeBag)
        NotificationCenter.default.rx.notification(ResetLession_Noti).take(until: self.rx.deallocated).subscribe(onNext: {[weak self]_ in
            self?.restore()
            self?.viewDidLoad()
        }).disposed(by: disposeBag)
    }
                }
    //回答正确
    private func answerSuccess(_ cell:ListenFight_lesson_1_CCell){
        menuView?.snp.removeConstraints()
        menuView?.playing()
        menuView?.jq_cornerRadius = 0
        let v = cell.view_topHandle.convert(cell.bounds, to: self.view)
        UIView.animate(withDuration: 0.3) {
            self.menuView?.snp.updateConstraints { make in
                make.top.equalTo(self.view).offset(v.origin.y)
                make.left.equalToSuperview().offset(v.origin.x)
                make.width.equalTo(v.size.width - 10)
                make.height.equalTo(40)
            }
            self.view.layoutIfNeeded()
        }completion: { _ in
            self.collectionView.reloadData()
        }
    }
                override func viewDidAppear(_ animated: Bool) {
                                super.viewDidAppear(animated)
                                VoicePlayer.share().delegate = self
                }
                override func viewDidDisappear(_ animated: Bool) {
                                super.viewDidDisappear(animated)
                                VoicePlayer.share().delegate = nil
                                VoicePlayer.share().playerInterrupt()
                }
                func restore(){
                                isAnsterDone = false
                                isAnsterModel.removeAll()
                                isAnsterComplete = false
                                viewModel.answerType.accept(.none)
                                viewModel.selectIndex.accept(nil)
                                menuView?.resetView()
                                setUI()
                                collectionView.reloadData()
                                getNextAnswer(isFirst: true)
                }
                func tobefore(){
                                if isAnsterModel.count == 1{return}
                                isAnsterModel.removeLast()
                                rootViewModel.answerCount.accept(page + isAnsterModel.count)
                                randomElement = isAnsterModel.last
                                menuView?.playUrl = randomElement?.correct
                                VoicePlayer.share().playerAt(url: self.randomElement?.correct)
                                menuView?.playing()
                                listenNewModel.subjectList[page].shuffle()
                                collectionView.reloadData()
                }
                /// 下一题
                /// - Parameter isFirst: 是否首次进入,首次页码不+1
                private func getNextAnswer(isFirst:Bool = false){
                                isListen = false
                                if isAnsterModel.count == 4{
                                                DispatchQueue.main.asyncAfter(deadline: .now()+3){[weak self] in
                                                                guard let weakSelf = self else { return }
                                                                NotificationCenter.default.post(name: NextLession_Noti, object: nil)
                                                                weakSelf.viewModel.answerType.accept(.none)
                                                                weakSelf.viewModel.selectIndex.accept(nil)
                                                                weakSelf.isListen = false
                                                                let v = weakSelf.rootViewModel.answerCount.value
                                                                weakSelf.rootViewModel.answerCount.accept(v + 1)
                                                }
                                                return
                                }
                                randomElement = listenNewModel.subjectList[page].randomElement() //随机抽题
                                if randomElement != nil{
                                                //如果已经回答,或标记为已回答(返回上一小题时)
                                                if isAnsterModel.contains(randomElement!){
                                                                getNextAnswer();return
                                                }
                                                //没有回答
                                                if !isAnsterModel.contains(randomElement!){
                                                                isAnsterModel.append(randomElement!)
                                                }
                                }
                                menuView?.playUrl = randomElement?.correct
                                listenNewModel.subjectList[page].shuffle()
                                isAnsterComplete = false
                                viewModel.answerType.accept(.none)
                                collectionView.reloadData()
                                setUI()
                                //自动播放下一题语音
                                DispatchQueue.main.asyncAfter(deadline: .now()+2) {
                                                VoicePlayer.share().playerAt(url: self.randomElement?.correct)
                                                self.menuView?.playing()
                                }
                                if !isAnsterDone && isAnsterModel.count <= 4 && !isFirst{
                                                let v = rootViewModel.answerCount.value
                                                rootViewModel.answerCount.accept(v + 1)
                                }
                }
                override func setUI() {
                                super.setUI()
                                if !view.subviews.contains(collectionView){
                                                collectionView.delegate = self
                                                collectionView.dataSource = self
                                                collectionView.backgroundColor = UIColor(hexStr: "#C3BFB3")
                                                view.addSubview(collectionView)
                                                collectionView.snp.makeConstraints { make in
                                                                make.top.equalTo(self.view.safeAreaLayoutGuide.snp.top).offset(101)
                                                                make.left.equalTo(194)
                                                                make.right.equalTo(-194)
                                                                make.bottom.equalToSuperview()
                                                }
                                }
                                if menuView == nil{
                                                menuView = VoiceHandleView()
                                                view.addSubview(menuView!)
                                                menuView?.snp.makeConstraints { make in
                                                                make.top.equalTo(self.view.safeAreaLayoutGuide.snp.top).offset(24)
                                                                make.centerX.equalToSuperview()
                                                                make.height.equalTo(52)
                                                                make.width.equalTo(159)
                                                }
                                }else{
                                                menuView?.snp.remakeConstraints { make in
                                                                make.top.equalTo(self.view.safeAreaLayoutGuide.snp.top).offset(24)
                                                                make.centerX.equalToSuperview()
                                                                make.height.equalTo(52)
                                                                make.width.equalTo(159)
                                                }
                                }
                                collectionView.reloadData()
                }
                override func viewDidLayoutSubviews() {
                                super.viewDidLayoutSubviews()
                                let flowLayout = self.collectionView.collectionViewLayout as! UICollectionViewFlowLayout
                                let w = (self.collectionView.size.width - flowLayout.minimumLineSpacing) / 2
                                let h = (self.collectionView.size.height - flowLayout.minimumInteritemSpacing) / 2
                                if flowLayout.itemSize.width != w || flowLayout.itemSize.height != h{
                                                flowLayout.itemSize = CGSize(width: w, height: h)
                                                collectionView.reloadData()
                                }
                }
                override func setRx() {
                                viewModel.selectIndex.subscribe(onNext: {[weak self] index in
                                                guard let weakSelf = self else { return }
                                                guard let index = index else { return }
                                                if weakSelf.viewModel.answerType.value == .success {return}
                                                //判断选中是否正确逻辑
                                                if let cell = self?.collectionView.dequeueReusableCell(withReuseIdentifier: "_ListenFight_lesson_1_CCell", for: index) as? ListenFight_lesson_1_CCell{
                                                                var answer:Fight_lessonType = .none
                                                                if self?.randomElement?.id == weakSelf.listenNewModel.subjectList[weakSelf.page][index.row].id{
                                                                                answer = .success
                                                                                self?.randomElement?.isAnster = true
                                                                                self?.isListen = false
                                                                                if self?.isAnsterComplete == false{
                                                                                                self?.rootViewModel.correctNum += 1
                                                                                }
                                                                                self?.isAnsterComplete = true
                                                                                VoicePlayer.share().playSuccessVoice()
                                                                                let teamId = weakSelf.listenNewModel.data?.id.components(separatedBy: ",")[weakSelf.page]
                                                                                weakSelf.rootViewModel.insertCorrectAnswer(teamId: teamId, answerId: weakSelf.listenNewModel.subjectList[weakSelf.page][index.row].id)
                                                                                DispatchQueue.main.asyncAfter(deadline: .now()+2) {
                                                                                                VoicePlayer.share().playerAt(url: weakSelf.listenNewModel.subjectList[weakSelf.page][index.row].correct)
                                                                                }
                                                                }else{
                                                                                answer = .fail
                                                                                VoicePlayer.share().playFailVoice()
                                                                                self?.isListen = false
                                                                                if self?.isAnsterComplete == false{
                                                                                                self?.rootViewModel.errorNum += 1
                                                                                }
                                                                }
                                                                switch answer {
                                                                                case .success:
                                                                                                self?.viewModel.answerType.accept(.success)
                                                                                                self?.answerSuccess(cell)
                                                                                case .fail:
                                                                                                self?.viewModel.answerType.accept(.fail)
                                                                                                self?.collectionView.reloadData()
                                                                                default:break
                                                                }
                                                }
                                }).disposed(by: disposeBag)
                }
                //回答正确
                private func answerSuccess(_ cell:ListenFight_lesson_1_CCell){
                                menuView?.snp.removeConstraints()
                                menuView?.playing()
                                menuView?.jq_cornerRadius = 0
                                let v = cell.view_topHandle.convert(cell.bounds, to: self.view)
                                UIView.animate(withDuration: 0.3) {
                                                self.menuView?.snp.updateConstraints { make in
                                                                make.top.equalTo(self.view).offset(v.origin.y + UIDevice.jq_safeEdges.top + 101 + 50)
                                                                make.left.equalToSuperview().offset(v.origin.x + 194)
                                                                make.width.equalTo(v.size.width - 10)
                                                                make.height.equalTo(40)
                                                }
                                                self.view.layoutIfNeeded()
                                }completion: { _ in
                                                self.collectionView.reloadData()
                                }
                }
    func handleClouseAction(clouse:@escaping ()->Void){
        self.handleClouse = clouse
    }
}
extension HomeListenFight_lesson_1_VC:UICollectionViewDelegate{
                func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
                                if !isListen{
                                                alertError(msg: "请先听题");return
                                }
                                viewModel.selectIndex.accept(indexPath)
                }
    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        if !isListen{
            alertError(msg: "请先听题");return
        }
        viewModel.selectIndex.accept(indexPath)
    }
}
extension HomeListenFight_lesson_1_VC:UICollectionViewDataSource{
                func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
                                let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "_ListenFight_lesson_1_CCell", for: indexPath) as! ListenFight_lesson_1_CCell
                                cell.jq_addShadows(shadowColor: .black.withAlphaComponent(0.31), corner: 5, radius: 5, offset: CGSize(width: 0, height: 1), opacity: 1)
                                cell.backgroundColor = .white
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "_ListenFight_lesson_1_CCell", for: indexPath) as! ListenFight_lesson_1_CCell
        cell.jq_addShadows(shadowColor: .black.withAlphaComponent(0.31), corner: 5, radius: 5, offset: CGSize(width: 0, height: 1), opacity: 1)
        cell.backgroundColor = .white
                                if viewModel.selectIndex.value == indexPath{
                                                cell.setState(state: viewModel.answerType.value)
                                }else{
                                                cell.setState(state: .none)
                                }
                                cell.setListen1SubModel(listenNewModel.subjectList[page][indexPath.row])
                                return cell
                }
        if viewModel.selectIndex.value == indexPath{
            cell.setState(state: viewModel.answerType.value)
        }else{
            cell.setState(state: .none)
        }
        cell.setListen1SubModel(listenNewModel.subjectList[page][indexPath.row])
        return cell
    }
                func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
                                return listenNewModel.subjectList[page].count
                }
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return listenNewModel.subjectList[page].count
    }
}
extension HomeListenFight_lesson_1_VC:VoicePlayerDelegate{
                func playing() {
                                isListen = false
                                self.view.isUserInteractionEnabled = false
                }
    func playing() {
        isListen = false
        self.view.isUserInteractionEnabled = false
    }
                func playComplete() {
                                isListen = true
                                self.view.isUserInteractionEnabled = true
                                self.menuView?.resetView()
    func playComplete() {
        isListen = true
        self.view.isUserInteractionEnabled = true
        self.menuView?.resetView()
                                if isAnsterComplete{
                                                getNextAnswer()
                                }
                }
        if isAnsterComplete{
            getNextAnswer()
        }
    }
}
DolphinEnglishLearnStudent/Moudle/Home/Listen/CCell/ListenFight_lesson_1_CCell.swift
@@ -8,55 +8,55 @@
import UIKit
enum Fight_lessonType {
                case success
                case fail
                case none
    case success
    case fail
    case none
}
class ListenFight_lesson_1_CCell: UICollectionViewCell {
                @IBOutlet weak var label_title: UILabel!
                @IBOutlet weak var image_state: UIImageView!
                @IBOutlet weak var image_cover: UIImageView!
                @IBOutlet weak var view_topHandle: UIView!
                override func awakeFromNib() {
                                super.awakeFromNib()
                                image_state.alpha = 0
                                image_state.transform = .init(scaleX: 0.1, y: 0.1)
                                label_title.isHidden = true
                                image_cover.contentMode = .scaleToFill
                }
    @IBOutlet weak var label_title: UILabel!
    @IBOutlet weak var image_state: UIImageView!
    @IBOutlet weak var image_cover: UIImageView!
    @IBOutlet weak var view_topHandle: UIView!
    override func awakeFromNib() {
        super.awakeFromNib()
        image_state.alpha = 0
        image_state.transform = .init(scaleX: 0.1, y: 0.1)
        label_title.isHidden = true
        image_cover.contentMode = .scaleToFill
    }
                func setState(state:Fight_lessonType){
    func setState(state:Fight_lessonType){
                                switch state {
                                                case .success:
                                                                image_state.image = UIImage(named: "icon_success")
                                                                UIView.animate(withDuration: 0.6, delay: 0, usingSpringWithDamping: 0.3, initialSpringVelocity: 0.4, options: .layoutSubviews) {
                                                                                self.image_state.alpha = 1
                                                                                self.image_state.transform = .init(scaleX: 1, y: 1)
                                                                }
                                                                UIView.animate(withDuration: 0.5, delay: 3.0) {
                                                                                self.image_state.alpha = 0
                                                                                self.image_state.transform = .init(scaleX: 0.1, y: 0.1)
                                                                }
                                                case .fail:
                                                                image_state.image = UIImage(named: "icon_fail")
                                                                UIView.animate(withDuration: 0.6, delay: 0, usingSpringWithDamping: 0.3, initialSpringVelocity: 0.4, options: .layoutSubviews) {
                                                                                self.image_state.alpha = 1
                                                                                self.image_state.transform = .init(scaleX: 1, y: 1)
                                                                                UIView.animate(withDuration: 0.5, delay: 3.0) {
                                                                                                self.image_state.alpha = 0
                                                                                                self.image_state.transform = .init(scaleX: 0.1, y: 0.1)
                                                                                }
                                                                }
                                                case .none:
                                                                image_state.alpha = 0
                                                                image_state.transform = .init(scaleX: 0.1, y: 0.1)
                                }
                }
        switch state {
        case .success:
            image_state.image = UIImage(named: "icon_success")
            UIView.animate(withDuration: 0.6, delay: 0, usingSpringWithDamping: 0.3, initialSpringVelocity: 0.4, options: .layoutSubviews) {
                self.image_state.alpha = 1
                self.image_state.transform = .init(scaleX: 1, y: 1)
            }
            UIView.animate(withDuration: 0.5, delay: 3.0) {
                self.image_state.alpha = 0
                self.image_state.transform = .init(scaleX: 0.1, y: 0.1)
            }
        case .fail:
            image_state.image = UIImage(named: "icon_fail")
            UIView.animate(withDuration: 0.6, delay: 0, usingSpringWithDamping: 0.3, initialSpringVelocity: 0.4, options: .layoutSubviews) {
                self.image_state.alpha = 1
                self.image_state.transform = .init(scaleX: 1, y: 1)
                UIView.animate(withDuration: 0.5, delay: 3.0) {
                    self.image_state.alpha = 0
                    self.image_state.transform = .init(scaleX: 0.1, y: 0.1)
                }
            }
        case .none:
            image_state.alpha = 0
            image_state.transform = .init(scaleX: 0.1, y: 0.1)
        }
    }
                func setListen1SubModel(_ model:Listen1SubModel){
                                image_cover.sd_setImage(with: URL(string: model.img))
                }
    func setListen1SubModel(_ model:Listen1SubModel){
        image_cover.sd_setImage(with: URL(string: model.img))
    }
}
DolphinEnglishLearnStudent/Moudle/Home/Listen/CCell/ListenFight_lesson_1_CCell.xib
@@ -21,13 +21,13 @@
                        <subviews>
                            <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="--" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="rgH-1a-mKt">
                                <rect key="frame" x="0.0" y="0.0" width="435" height="40"/>
                                <color key="backgroundColor" red="0.53725490196078429" green="0.52941176470588236" blue="0.49411764705882355" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                                <fontDescription key="fontDescription" type="system" weight="medium" pointSize="16"/>
                                <color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                                <color key="textColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                                <nil key="highlightedColor"/>
                            </label>
                        </subviews>
                        <color key="backgroundColor" red="0.83137254900000002" green="0.82352941180000006" blue="0.80392156859999997" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                        <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                        <constraints>
                            <constraint firstAttribute="height" constant="40" id="1mb-ik-h6n"/>
                            <constraint firstAttribute="trailing" secondItem="rgH-1a-mKt" secondAttribute="trailing" id="Zjf-E9-9in"/>
DolphinEnglishLearnStudent/Moudle/Home/Listen/CCell/ListenFight_lesson_3_CCell.swift
@@ -10,59 +10,57 @@
class ListenFight_lesson_3_CCell: UICollectionViewCell {
                @IBOutlet weak var img_cover: UIImageView!
                @IBOutlet weak var view_container: UIView!
                @IBOutlet weak var btn_play: UIButton!
                @IBOutlet weak var btn_playing: UIButton!
                @IBOutlet weak var view_playHandle: UIView!
                @IBOutlet weak var img_playing: UIImageView!
                @IBOutlet weak var img_playSuccess: UIImageView!
                private var model:Listen1SubModel!
                private var playAtClouse:((IndexPath)->Void)?
                var indexPath:IndexPath!
    @IBOutlet weak var img_cover: UIImageView!
    @IBOutlet weak var view_container: UIView!
    @IBOutlet weak var btn_play: UIButton!
    @IBOutlet weak var btn_playing: UIButton!
    @IBOutlet weak var view_playHandle: UIView!
    @IBOutlet weak var img_playing: UIImageView!
                override func awakeFromNib() {
    private var model:Listen1SubModel!
    private var playAtClouse:((IndexPath)->Void)?
    var indexPath:IndexPath!
    override func awakeFromNib() {
        super.awakeFromNib()
                                view_container.jq_addShadows(shadowColor: .black.withAlphaComponent(0.31), corner: 8, radius: 3, offset: CGSize(width: 0, height: 1), opacity: 1)
                                layoutIfNeeded()
        layoutIfNeeded()
        btn_playing.setImage(UIImage(named: "icon_play_1"), for: .normal)
        btn_play.setImage(UIImage(named: "icon_play"), for: .normal)
        img_playing.image = UIImage(named: "icon_playing")?.themeGreen
        view_playHandle.backgroundColor = .white
    }
                func setModel(_ model:Listen1SubModel,isplaying:Bool){
                                self.model = model
                                self.btn_play.alpha = (isplaying ? 0:1)
                                self.btn_playing.alpha = (isplaying ? 0:1)
                                self.img_playing.alpha = (isplaying ? 1:0)
                }
    override func layoutSubviews() {
        super.layoutSubviews()
        //        jq_addShadows(shadowColor: .black.withAlphaComponent(0.31), corner: 8, radius: 3, offset: CGSize(width: 0, height: 1), opacity: 1)
        jq_cornerRadius = 8
    }
                func palyVoiceAt(_ clouse:@escaping(IndexPath)->Void){
                                self.playAtClouse = clouse
                }
    func setModel(_ model:Listen1SubModel,isplaying:Bool){
        self.model = model
        self.btn_play.alpha = (isplaying ? 0:1)
        self.btn_playing.alpha = (isplaying ? 0:1)
        self.img_playing.alpha = (isplaying ? 1:0)
    }
                func canClick(_ state:Bool){
                                btn_play.isEnabled = state
                                view_playHandle.backgroundColor = state == true ? UIColor(hexString: "#41A2EB") : .gray
                }
    func palyVoiceAt(_ clouse:@escaping(IndexPath)->Void){
        self.playAtClouse = clouse
    }
                func isPlaying(isplaying:Bool){
                            btn_play.alpha = (isplaying ? 0:1)
                            btn_playing.alpha = (isplaying ? 0:1)
                            img_playing.alpha = (isplaying ? 1:0)
                }
    func canClick(_ state:Bool){
        btn_play.isEnabled = state
        view_playHandle.backgroundColor = .white
    }
//                func playSuccess(){
//                                UIView.animate(withDuration: 0.6, delay: 0, usingSpringWithDamping: 0.3, initialSpringVelocity: 0.4, options: .layoutSubviews) {
//                                                self.img_playSuccess.alpha = 1
//                                                self.img_playSuccess.transform = .init(scaleX: 1, y: 1)
//                                }
//                                UIView.animate(withDuration: 0.5, delay: 3.0) {
//                                                self.img_playSuccess.alpha = 0
//                                                self.img_playSuccess.transform = .init(scaleX: 0.1, y: 0.1)
//                                }
//                }
    func isPlaying(isplaying:Bool){
        btn_play.alpha = (isplaying ? 0:1)
        btn_playing.alpha = (isplaying ? 0:1)
        img_playing.alpha = (isplaying ? 1:0)
    }
                @IBAction func playAction(_ sender: Any) {
                                playAtClouse?(indexPath)
                }
    @IBAction func playAction(_ sender: Any) {
        playAtClouse?(indexPath)
    }
}
DolphinEnglishLearnStudent/Moudle/Home/Listen/CCell/ListenFight_lesson_3_CCell.xib
@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="32700.99.1234" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="23504" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
    <device id="ipad10_9rounded" orientation="portrait" layout="fullscreen" appearance="light"/>
    <dependencies>
        <deployment identifier="iOS"/>
        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22684"/>
        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="23506"/>
        <capability name="System colors in document resources" minToolsVersion="11.0"/>
        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
    </dependencies>
@@ -18,48 +18,48 @@
                <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
                <subviews>
                    <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="vtr-2e-B5C">
                        <rect key="frame" x="0.0" y="0.0" width="159" height="52"/>
                        <rect key="frame" x="0.0" y="0.0" width="630" height="40"/>
                        <subviews>
                            <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="7yc-PU-RgV">
                                <rect key="frame" x="104" y="10" width="32" height="32"/>
                                <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
                                <state key="normal" image="icon_play"/>
                                <connections>
                                    <action selector="playAction:" destination="gTV-IL-0wX" eventType="touchUpInside" id="IWn-Zu-bCh"/>
                                </connections>
                            </button>
                            <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="9Dc-Ns-SMP">
                                <rect key="frame" x="25" y="12.5" width="27" height="27"/>
                                <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
                                <state key="normal" image="icon_play_1"/>
                            </button>
                            <stackView opaque="NO" contentMode="scaleToFill" distribution="fillEqually" spacing="83" translatesAutoresizingMaskIntoConstraints="NO" id="MXy-1Z-w1c">
                                <rect key="frame" x="228.5" y="0.0" width="173" height="40"/>
                                <subviews>
                                    <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="7yc-PU-RgV">
                                        <rect key="frame" x="0.0" y="0.0" width="45" height="40"/>
                                        <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
                                        <state key="normal" image="icon_play"/>
                                        <connections>
                                            <action selector="playAction:" destination="gTV-IL-0wX" eventType="touchUpInside" id="IWn-Zu-bCh"/>
                                        </connections>
                                    </button>
                                    <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="9Dc-Ns-SMP">
                                        <rect key="frame" x="128" y="0.0" width="45" height="40"/>
                                        <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
                                        <state key="normal" image="icon_play_1"/>
                                    </button>
                                </subviews>
                            </stackView>
                            <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="icon_playing" translatesAutoresizingMaskIntoConstraints="NO" id="vMh-x4-Y7a">
                                <rect key="frame" x="57" y="10.5" width="45" height="31"/>
                                <rect key="frame" x="292.5" y="4.5" width="45" height="31"/>
                            </imageView>
                        </subviews>
                        <color key="backgroundColor" red="0.25490196078431371" green="0.63529411764705879" blue="0.92156862745098034" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                        <constraints>
                            <constraint firstItem="9Dc-Ns-SMP" firstAttribute="centerY" secondItem="vtr-2e-B5C" secondAttribute="centerY" id="C8e-Iq-OaB"/>
                            <constraint firstItem="7yc-PU-RgV" firstAttribute="centerY" secondItem="vtr-2e-B5C" secondAttribute="centerY" id="EUe-pu-sAP"/>
                            <constraint firstAttribute="bottom" secondItem="MXy-1Z-w1c" secondAttribute="bottom" id="LoX-sU-g5l"/>
                            <constraint firstItem="vMh-x4-Y7a" firstAttribute="centerX" secondItem="vtr-2e-B5C" secondAttribute="centerX" id="ObG-a0-OcJ"/>
                            <constraint firstAttribute="trailing" secondItem="7yc-PU-RgV" secondAttribute="trailing" constant="23" id="Rms-9S-fkG"/>
                            <constraint firstAttribute="height" constant="52" id="Y2Z-EL-K2n"/>
                            <constraint firstAttribute="width" constant="159" id="oI7-Oh-ubD"/>
                            <constraint firstItem="9Dc-Ns-SMP" firstAttribute="leading" secondItem="vtr-2e-B5C" secondAttribute="leading" constant="25" id="xRi-cc-X9V"/>
                            <constraint firstItem="MXy-1Z-w1c" firstAttribute="top" secondItem="vtr-2e-B5C" secondAttribute="top" id="RGy-nc-L5R"/>
                            <constraint firstAttribute="height" constant="40" id="Y2Z-EL-K2n"/>
                            <constraint firstItem="MXy-1Z-w1c" firstAttribute="centerX" secondItem="vtr-2e-B5C" secondAttribute="centerX" id="v69-Hn-XdH"/>
                            <constraint firstItem="vMh-x4-Y7a" firstAttribute="centerY" secondItem="vtr-2e-B5C" secondAttribute="centerY" id="xql-kz-i8e"/>
                        </constraints>
                        <userDefinedRuntimeAttributes>
                            <userDefinedRuntimeAttribute type="boolean" keyPath="ld_maskToBoundsXIB" value="YES"/>
                            <userDefinedRuntimeAttribute type="number" keyPath="ld_cornerRadiusXIB">
                                <real key="value" value="8"/>
                            </userDefinedRuntimeAttribute>
                        </userDefinedRuntimeAttributes>
                    </view>
                    <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="qxz-6s-e5b">
                        <rect key="frame" x="0.0" y="77" width="630" height="467"/>
                        <rect key="frame" x="0.0" y="40" width="630" height="504"/>
                        <subviews>
                            <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="n5n-eb-5xI">
                                <rect key="frame" x="5" y="5" width="620" height="457"/>
                                <rect key="frame" x="5" y="5" width="620" height="494"/>
                                <color key="backgroundColor" red="0.94509803921568625" green="0.94509803921568625" blue="0.94509803921568625" alpha="0.84999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
                            </imageView>
                        </subviews>
@@ -75,9 +75,10 @@
            </view>
            <constraints>
                <constraint firstAttribute="trailing" secondItem="qxz-6s-e5b" secondAttribute="trailing" id="HMD-Sa-FHB"/>
                <constraint firstAttribute="trailing" secondItem="vtr-2e-B5C" secondAttribute="trailing" id="LSt-bv-htS"/>
                <constraint firstItem="qxz-6s-e5b" firstAttribute="leading" secondItem="gTV-IL-0wX" secondAttribute="leading" id="SLN-pI-I4q"/>
                <constraint firstItem="vtr-2e-B5C" firstAttribute="leading" secondItem="gTV-IL-0wX" secondAttribute="leading" id="enY-sX-Oib"/>
                <constraint firstItem="qxz-6s-e5b" firstAttribute="top" secondItem="vtr-2e-B5C" secondAttribute="bottom" constant="25" id="iLs-4C-NaW"/>
                <constraint firstItem="qxz-6s-e5b" firstAttribute="top" secondItem="vtr-2e-B5C" secondAttribute="bottom" id="iLs-4C-NaW"/>
                <constraint firstItem="vtr-2e-B5C" firstAttribute="top" secondItem="gTV-IL-0wX" secondAttribute="top" id="jbw-ZJ-YrV"/>
                <constraint firstAttribute="bottom" secondItem="qxz-6s-e5b" secondAttribute="bottom" constant="10" id="msT-T9-1c4"/>
            </constraints>
@@ -94,8 +95,8 @@
        </collectionViewCell>
    </objects>
    <resources>
        <image name="icon_play" width="32" height="32"/>
        <image name="icon_play_1" width="27" height="27"/>
        <image name="icon_play" width="45" height="45"/>
        <image name="icon_play_1" width="28.5" height="30"/>
        <image name="icon_playing" width="45" height="31"/>
        <systemColor name="systemBackgroundColor">
            <color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
DolphinEnglishLearnStudent/Moudle/Home/Listen/CCell/ListenFight_lesson_4_CCell.swift
@@ -6,6 +6,7 @@
//
import UIKit
import JQTools
class ListenFight_lesson_4_CCell: UICollectionViewCell {
@@ -23,8 +24,19 @@
                override func awakeFromNib() {
                                super.awakeFromNib()
                                view_container.jq_addShadows(shadowColor: .black.withAlphaComponent(0.31), corner: 8, radius: 3, offset: CGSize(width: 0, height: 1), opacity: 1)
        view_handle.backgroundColor = .white
        btn_handle.setImage(UIImage(named: "icon_question"), for: .normal)
        btn_play.setImage(UIImage(named: "icon_play"), for: .normal)
        btn_voice.setImage(UIImage(named: "icon_play_1"), for: .normal)
        btn_handle.isUserInteractionEnabled = true
                }
    override func layoutSubviews() {
        super.layoutSubviews()
//        jq_addShadows(shadowColor: .black.withAlphaComponent(0.31), corner: 8, radius: 3, offset: CGSize(width: 0, height: 1), opacity: 1)
        jq_cornerRadius = 8
    }
                func setModel(_ m:Listen1SubModel){
                                model = m
@@ -36,18 +48,18 @@
                }
                func playing(){
                                btn_handle.isHidden = true
//                                btn_handle.isHidden = true
                                btn_play.isHidden = true
                                btn_voice.setImage(UIImage(named: "icon_playing"), for: .normal)
        btn_voice.setImage(UIImage(named: "icon_playing"), for: .normal)
                }
                func playEnd(){
                                btn_handle.isHidden = false
//                                btn_handle.isHidden = false
                                btn_play.isHidden = false
                                btn_voice.setImage(UIImage(named: "icon_play_1"), for: .normal)
        btn_voice.setImage(UIImage(named: "icon_play_1"), for: .normal)
                }
                @IBAction func playAction(_ sender: TapBtn) {
    @IBAction func playAction(_ sender: UIButton) {
                                if let m = model{
                                                VoicePlayer.share().playerAt(url: m.correct)
                                                playAtIndexClouse?(indexPath)
DolphinEnglishLearnStudent/Moudle/Home/Listen/CCell/ListenFight_lesson_4_CCell.xib
@@ -1,10 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="32700.99.1234" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="23504" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
    <device id="ipad10_9rounded" orientation="portrait" layout="fullscreen" appearance="light"/>
    <dependencies>
        <deployment identifier="iOS"/>
        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22684"/>
        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="23506"/>
        <capability name="System colors in document resources" minToolsVersion="11.0"/>
        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
    </dependencies>
@@ -19,55 +18,56 @@
                <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
                <subviews>
                    <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Xjz-V8-keG" customClass="TapBtn" customModule="DolphinEnglishLearnStudent" customModuleProvider="target">
                        <rect key="frame" x="0.0" y="0.0" width="159" height="52"/>
                        <rect key="frame" x="0.0" y="0.0" width="597" height="40"/>
                        <subviews>
                            <button opaque="NO" userInteractionEnabled="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="LLy-9v-eQJ">
                                <rect key="frame" x="105" y="10" width="31" height="32"/>
                                <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
                                <state key="normal" image="icon_play"/>
                            </button>
                            <button opaque="NO" userInteractionEnabled="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="kgj-Ss-D90">
                                <rect key="frame" x="66" y="12.5" width="27" height="27"/>
                                <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
                                <state key="normal" image="icon_play_1"/>
                            </button>
                            <button opaque="NO" userInteractionEnabled="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="ZuK-r9-26C">
                                <rect key="frame" x="15" y="9.5" width="33" height="33"/>
                                <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
                                <state key="normal" image="icon_question"/>
                            </button>
                            <stackView opaque="NO" contentMode="scaleToFill" distribution="fillEqually" translatesAutoresizingMaskIntoConstraints="NO" id="EtJ-zc-pEe">
                                <rect key="frame" x="20" y="0.0" width="557" height="40"/>
                                <subviews>
                                    <button opaque="NO" userInteractionEnabled="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="ZuK-r9-26C">
                                        <rect key="frame" x="0.0" y="0.0" width="185.5" height="40"/>
                                        <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
                                        <state key="normal" image="icon_question"/>
                                    </button>
                                    <button opaque="NO" userInteractionEnabled="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="kgj-Ss-D90">
                                        <rect key="frame" x="185.5" y="0.0" width="186" height="40"/>
                                        <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
                                        <state key="normal" image="icon_play_1"/>
                                    </button>
                                    <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="LLy-9v-eQJ">
                                        <rect key="frame" x="371.5" y="0.0" width="185.5" height="40"/>
                                        <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
                                        <state key="normal" image="icon_play"/>
                                        <connections>
                                            <action selector="playAction:" destination="gTV-IL-0wX" eventType="touchUpInside" id="vty-Fu-kuW"/>
                                        </connections>
                                    </button>
                                </subviews>
                            </stackView>
                        </subviews>
                        <color key="backgroundColor" red="0.25490196079999999" green="0.63529411759999999" blue="0.92156862750000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                        <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                        <constraints>
                            <constraint firstAttribute="width" constant="159" id="7Mr-KM-NTE"/>
                            <constraint firstAttribute="trailing" secondItem="LLy-9v-eQJ" secondAttribute="trailing" constant="23" id="9dR-Nl-sPa"/>
                            <constraint firstItem="ZuK-r9-26C" firstAttribute="centerY" secondItem="Xjz-V8-keG" secondAttribute="centerY" id="Erm-17-erv"/>
                            <constraint firstItem="LLy-9v-eQJ" firstAttribute="centerY" secondItem="Xjz-V8-keG" secondAttribute="centerY" id="FB0-fx-81Z"/>
                            <constraint firstItem="LLy-9v-eQJ" firstAttribute="leading" secondItem="kgj-Ss-D90" secondAttribute="trailing" constant="12" id="MoJ-Ne-mlq"/>
                            <constraint firstAttribute="height" constant="52" id="gfp-ph-1NP"/>
                            <constraint firstItem="kgj-Ss-D90" firstAttribute="leading" secondItem="ZuK-r9-26C" secondAttribute="trailing" constant="18" id="qM1-Yp-Nha"/>
                            <constraint firstItem="kgj-Ss-D90" firstAttribute="centerX" secondItem="Xjz-V8-keG" secondAttribute="centerX" id="x1s-RS-WOb"/>
                            <constraint firstItem="kgj-Ss-D90" firstAttribute="centerY" secondItem="Xjz-V8-keG" secondAttribute="centerY" id="zxl-IP-PhN"/>
                            <constraint firstAttribute="bottom" secondItem="EtJ-zc-pEe" secondAttribute="bottom" id="4rh-Rk-UOs"/>
                            <constraint firstItem="EtJ-zc-pEe" firstAttribute="leading" secondItem="Xjz-V8-keG" secondAttribute="leading" constant="20" id="5Nn-r4-YwD"/>
                            <constraint firstItem="EtJ-zc-pEe" firstAttribute="top" secondItem="Xjz-V8-keG" secondAttribute="top" id="Ri7-jh-mGE"/>
                            <constraint firstAttribute="height" constant="40" id="gfp-ph-1NP"/>
                            <constraint firstAttribute="trailing" secondItem="EtJ-zc-pEe" secondAttribute="trailing" constant="20" id="j8s-7c-9su"/>
                        </constraints>
                        <userDefinedRuntimeAttributes>
                            <userDefinedRuntimeAttribute type="boolean" keyPath="ld_maskToBoundsXIB" value="YES"/>
                            <userDefinedRuntimeAttribute type="number" keyPath="ld_cornerRadiusXIB">
                                <real key="value" value="8"/>
                            </userDefinedRuntimeAttribute>
                        </userDefinedRuntimeAttributes>
                        <connections>
                            <action selector="playAction:" destination="gTV-IL-0wX" eventType="touchUpInside" id="8ns-md-QJB"/>
                            <action selector="playAction:" destination="gTV-IL-0wX" eventType="valueChanged" id="7bY-LY-aXz"/>
                        </connections>
                    </view>
                    <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="OJ6-0b-fVC">
                        <rect key="frame" x="0.0" y="62" width="597" height="438"/>
                        <rect key="frame" x="0.0" y="40" width="597" height="460"/>
                        <subviews>
                            <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="XW5-ds-CXG">
                                <rect key="frame" x="5" y="5" width="587" height="428"/>
                                <rect key="frame" x="5" y="5" width="587" height="450"/>
                                <color key="backgroundColor" red="0.94509803920000002" green="0.94509803920000002" blue="0.94509803920000002" alpha="0.84999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
                            </imageView>
                            <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="icon_success" translatesAutoresizingMaskIntoConstraints="NO" id="eyJ-Qy-E0w">
                                <rect key="frame" x="258.5" y="178.5" width="80" height="81"/>
                                <rect key="frame" x="258.5" y="189.5" width="80" height="81"/>
                            </imageView>
                        </subviews>
                        <color key="backgroundColor" systemColor="systemBackgroundColor"/>
@@ -82,13 +82,13 @@
                    </view>
                </subviews>
            </view>
            <viewLayoutGuide key="safeArea" id="ZTg-uK-7eu"/>
            <constraints>
                <constraint firstAttribute="trailing" secondItem="Xjz-V8-keG" secondAttribute="trailing" id="0zx-cJ-mwb"/>
                <constraint firstItem="Xjz-V8-keG" firstAttribute="leading" secondItem="gTV-IL-0wX" secondAttribute="leading" id="3SB-P6-hOR"/>
                <constraint firstItem="Xjz-V8-keG" firstAttribute="top" secondItem="gTV-IL-0wX" secondAttribute="top" id="5Iq-AY-uUK"/>
                <constraint firstItem="OJ6-0b-fVC" firstAttribute="leading" secondItem="gTV-IL-0wX" secondAttribute="leading" id="6p8-30-BD6"/>
                <constraint firstAttribute="trailing" secondItem="OJ6-0b-fVC" secondAttribute="trailing" id="EAu-4C-peY"/>
                <constraint firstItem="OJ6-0b-fVC" firstAttribute="top" secondItem="Xjz-V8-keG" secondAttribute="bottom" constant="10" id="YpO-u8-Uyg"/>
                <constraint firstItem="OJ6-0b-fVC" firstAttribute="top" secondItem="Xjz-V8-keG" secondAttribute="bottom" id="YpO-u8-Uyg"/>
                <constraint firstAttribute="bottom" secondItem="OJ6-0b-fVC" secondAttribute="bottom" constant="10" id="m6e-nJ-ukf"/>
            </constraints>
            <size key="customSize" width="597" height="510"/>
@@ -105,9 +105,9 @@
        </collectionViewCell>
    </objects>
    <resources>
        <image name="icon_play" width="32" height="32"/>
        <image name="icon_play_1" width="27" height="27"/>
        <image name="icon_question" width="33" height="33"/>
        <image name="icon_play" width="45" height="45"/>
        <image name="icon_play_1" width="28.5" height="30"/>
        <image name="icon_question" width="45" height="45"/>
        <image name="icon_success" width="80" height="81"/>
        <systemColor name="systemBackgroundColor">
            <color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
DolphinEnglishLearnStudent/Moudle/Home/Listen/TCell/HomeListen_item_TCell.swift
@@ -8,29 +8,29 @@
import UIKit
class HomeListen_item_TCell: UITableViewCell {
                @IBOutlet weak var view_bg1: UIView!
                @IBOutlet weak var view_bg2: UIView!
                @IBOutlet weak var label_title: UILabel!
                @IBOutlet weak var view_state: UIView!
                @IBOutlet weak var label_state: UILabel!
    @IBOutlet weak var view_bg1: UIView!
    @IBOutlet weak var view_bg2: UIView!
    @IBOutlet weak var label_title: UILabel!
    @IBOutlet weak var view_state: UIView!
    @IBOutlet weak var label_state: UILabel!
    override func awakeFromNib() {
        super.awakeFromNib()
                                backgroundColor = .clear
                                selectionStyle = .none
        backgroundColor = .clear
        selectionStyle = .none
    }
                func setProgress(progress:Int){
                                if progress <= 0{
                                                view_state.backgroundColor = UIColor(hexString: "#F84D31")
                                                label_state.text = "未完成"
                                }else if progress == 100{
                                                view_state.backgroundColor = UIColor(hexString: "#66CFFA")
                                                label_state.text = "已完成"
                                }else if progress > 0 && progress < 100{
                                                view_state.backgroundColor = UIColor(hexString: "#FF8A66")
                                                label_state.text = String(format: "剩余:%ld%%", progress)
                                }
    func setProgress(progress:Int){
        if progress <= 0{
            view_state.backgroundColor = UIColor(hexString: "#F84D31")
            label_state.text = "未完成"
        }else if progress == 100{
            view_state.backgroundColor = UIColor(hexString: "#66CFFA")
            label_state.text = "已完成"
        }else if progress > 0 && progress < 100{
            view_state.backgroundColor = UIColor(hexString: "#FF8A66")
            label_state.text = String(format: "剩余:%ld%%", progress)
        }
                }
    }
}
DolphinEnglishLearnStudent/Moudle/Home/Listen/VC/HomeListenFightVC.swift
@@ -10,767 +10,1023 @@
import RxRelay
let NextLession_Noti = Notification.Name.init("NextLession_Noti")
let ResetLession_Noti = Notification.Name.init("ResetLession_Noti")
//let Reload_Noti = Notification.Name.init("Reload_Noti")
enum ListenType:Int{
                case lesson1 = 1 //自主学习-听音选图
                case lesson2 = 2 //自主学习-看图选音
                case lesson3 = 3 //自主学习-归纳排除
                case lesson4 = 4 //自主学习-有问有答
                case lesson5 = 5 //自主学习-音图相配
                case game1 = 6 //游戏类型-超级听力
                case game2 = 7 //游戏类型-超级记忆
                case story1 = 8 //故事类型-自主故事1-看图配音
                case story2 = 9 //故事类型-自主故事2-框架记忆
    case lesson1 = 1 //自主学习-听音选图
    case lesson2 = 2 //自主学习-看图选音
    case lesson3 = 3 //自主学习-归纳排除
    case lesson4 = 4 //自主学习-有问有答
    case lesson5 = 5 //自主学习-音图相配
    case game1 = 6 //游戏类型-超级听力
    case game2 = 7 //游戏类型-超级记忆
    case story1 = 8 //故事类型-自主故事1-看图配音
    case story2 = 9 //故事类型-自主故事2-框架记忆
                var rawTitle:String{
                                switch self {
                                                case .lesson1:return "自主学习1-听音选图"
                                                case .lesson2:return "自主学习2-看图选音"
                                                case .lesson3:return "自主学习3-归纳排除"
                                                case .lesson4:return "自主学习4-有问有答"
                                                case .lesson5:return "自主学习5-音图相配"
                                                case .game1:return "游戏类型1-超级听力"
                                                case .game2:return "游戏类型2-超级记忆"
                                                case .story1:return "自主故事1-看图配音"
                                                case .story2:return "自主故事2-框架记忆"
                                }
                }
    var rawTitle:String{
        switch self {
        case .lesson1:return "自主学习1-听音选图"
        case .lesson2:return "自主学习2-看图选音"
        case .lesson3:return "自主学习3-归纳排除"
        case .lesson4:return "自主学习4-有问有答"
        case .lesson5:return "自主学习5-音图相配"
        case .game1:return "游戏类型1-超级听力"
        case .game2:return "游戏类型2-超级记忆"
        case .story1:return "自主故事1-看图配音"
        case .story2:return "自主故事2-框架记忆"
        }
    }
}
enum ListenFightLine{
                case before
                case next
                case none
    case before
    case next
    case none
}
//中途退出所需要
class ExitLearnModel{
                var topicsIds = Set<Int>()
    var topicsIds = Set<Int>()
}
class HomeListenFightViewModel{
                /// 当前页数
                var currentPage = BehaviorRelay<Int>(value: 0)
                var subPage = BehaviorRelay<Int>(value: 1) //小题目角标
                var maxPage = BehaviorRelay<Int>(value: 5)
                var listenType = BehaviorRelay<ListenType>(value:.lesson1)
                var times:Int = 0
                var quarter = BehaviorRelay<Int?>(value: 0)
                var week = BehaviorRelay<Int?>(value: 0)
                var day = BehaviorRelay<Int?>(value: 0)
    /// 当前页数
    var currentPage = BehaviorRelay<Int>(value: 0)
    //    var subPage = BehaviorRelay<Int>(value: 1) //小题目角标
    var maxPage = BehaviorRelay<Int>(value: 5)
    var listenType = BehaviorRelay<ListenType>(value:.lesson1)
    var times:Int = 0
    var quarter = BehaviorRelay<Int?>(value: 0)
    var week = BehaviorRelay<Int?>(value: 0)
    var day = BehaviorRelay<Int?>(value: 0)
                //游戏专属,游戏等级
                var gameLevel = BehaviorRelay<Int>(value:0)
    //游戏专属,游戏等级
    var gameLevel = BehaviorRelay<Int>(value:0)
                //回答错误数量
                var correctNum:Int = 0{
                                didSet{
                                                print("回答正确:\(correctNum)")
                                }
                }
    //回答错误数量
    var correctNum:Int = 0{
        didSet{
            print("回答正确:\(correctNum)")
        }
    }
                /// 回答错误数量
                var errorNum:Int = 0{
                                didSet{
                                                print("回答错误:\(correctNum)")
                                }
                }
    /// 回答错误数量
    var errorNum:Int = 0{
        didSet{
            print("回答错误:\(correctNum)")
        }
    }
                //所有回答的 两游戏在用
                var answerItems = Dictionary<Int,Any>() //{page:0,data:String,currectAt:0}
                var answerCount = BehaviorRelay<Int>(value: 1)
    //所有回答的 两游戏在用
    var answerItems = Dictionary<Int,Any>() //{page:0,data:String,currectAt:0}
    var answerCount = BehaviorRelay<Int>(value: 1)
                var answerItems_1 = Dictionary<String,Array<Int>>()
    var answerItems_1 = Dictionary<String,Array<Int>>()
                //回答正确的题
                func insertCorrectAnswer(teamId:String?,answerId:Int){
                                guard teamId != nil else {return}
                                if answerItems_1[teamId!] == nil{
                                                answerItems_1[teamId!] = Array<Int>()
                                }
    //回答正确的题
    func insertCorrectAnswer(teamId:String?,answerId:Int){
        guard teamId != nil else {return}
        if answerItems_1[teamId!] == nil{
            answerItems_1[teamId!] = Array<Int>()
        }
                                answerItems_1[teamId!]!.append(answerId)
                }
        answerItems_1[teamId!]!.append(answerId)
    }
}
class HomeListenFightVC: BaseVC {
                private var viewModel = HomeListenFightViewModel()
                var studyScheduleModel:StudyScheduleModel? //学习进度(上级传递)
                var listenFightLine:ListenFightLine = .none
                var data:Any?
    private var viewModel = HomeListenFightViewModel()
    var studyScheduleModel:StudyScheduleModel? //学习进度(上级传递)
    var listenFightLine:ListenFightLine = .none
    var data:Any?
                var maxPage = 0 //最大页记录
                var teamScheduleModel:TeamScheduleModel? //上次中途退出,答题记录
    var maxPage = 0 //最大页记录
    var teamScheduleModel:TeamScheduleModel? //上次中途退出,答题记录
    var pages = [[ListenSubCardModel]]() //分页显示
                private var notiObject:Dictionary<String,Any>?
    private var notiObject:Dictionary<String,Any>?
                private lazy var label_pageNum:UILabel = {
                                let label = UILabel()
                                label.font = .systemFont(ofSize: 14, weight: .medium)
                                label.textColor = .black.withAlphaComponent(0.81)
                                label.textAlignment = .center
                                label.text = "已完成:0/0"
                                return label
                }()
    private lazy var label_pageNum:UILabel = {
        let label = UILabel()
        label.font = .systemFont(ofSize: 14, weight: .medium)
        label.textColor = .black.withAlphaComponent(0.81)
        label.textAlignment = .center
        label.numberOfLines = 2
        label.text = "已完成:0/0\n正确率:--%"
        return label
    }()
                private lazy var btn_forward:UIButton = {
                                let btn = UIButton(type: .custom)
                                btn.setTitle("上一题", for: .normal)
                                btn.titleLabel?.font = .systemFont(ofSize: 14, weight: .medium)
                                btn.setTitleColor(Config.ThemeColor, for: .normal)
                                btn.jq_borderColor = Config.ThemeColor
                                btn.backgroundColor = .white
                                btn.jq_borderWidth = 1
                                btn.jq_cornerRadius = 4
                                return btn
                }()
    private lazy var btn_forward:UIButton = {
        let btn = UIButton(type: .custom)
        btn.setTitle("上一题", for: .normal)
        btn.titleLabel?.font = .systemFont(ofSize: 14, weight: .medium)
        btn.setTitleColor(Config.ThemeColor, for: .normal)
        btn.jq_borderColor = Config.ThemeColor
        btn.backgroundColor = .white
        btn.jq_borderWidth = 1
        btn.jq_cornerRadius = 4
        return btn
    }()
                private lazy var btn_forward_mini:UIButton = {
                                let btn = UIButton(type: .custom)
                                btn.setTitle("上一小题", for: .normal)
                                btn.titleLabel?.font = .systemFont(ofSize: 14, weight: .medium)
                                btn.setTitleColor(Config.ThemeColor, for: .normal)
                                btn.jq_borderColor = Config.ThemeColor
                                btn.backgroundColor = .white
                                btn.isHidden = true
                                btn.jq_borderWidth = 1
                                btn.jq_cornerRadius = 4
                                return btn
                }()
    private lazy var collection_card:UICollectionView = {
        let layout = UICollectionViewFlowLayout()
        layout.minimumInteritemSpacing = 2
        layout.minimumLineSpacing = 2
        layout.itemSize = CGSize(width: 24, height: 24)
        let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
        collectionView.backgroundColor = .clear
        return collectionView
    }()
                private lazy var btn_next:UIButton = {
                                let btn = UIButton(type: .custom)
                                btn.setTitle("下一题", for: .normal)
                                btn.titleLabel?.font = .systemFont(ofSize: 14, weight: .medium)
                                btn.setTitleColor(Config.ThemeColor, for: .normal)
                                btn.jq_borderColor = Config.ThemeColor
                                btn.backgroundColor = .white
                                btn.jq_borderWidth = 1
                                btn.jq_cornerRadius = 4
                                return btn
                }()
    private lazy var btn_beAgain:UIButton = {
        let btn = UIButton(type: .custom)
        btn.setTitle("重新开始", for: .normal)
        btn.titleLabel?.font = .systemFont(ofSize: 14, weight: .medium)
        btn.setTitleColor(Config.ThemeColor, for: .normal)
        btn.jq_borderColor = Config.ThemeColor
        btn.backgroundColor = .white
        btn.jq_borderWidth = 1
        btn.jq_cornerRadius = 4
        return btn
    }()
                private lazy var btn_exit:UIButton = {
                                let btn = UIButton(type: .custom)
                                btn.setTitle("退出", for: .normal)
                                btn.titleLabel?.font = .systemFont(ofSize: 14, weight: .medium)
                                btn.setTitleColor(.white, for: .normal)
                                btn.backgroundColor = Config.ThemeColor
                                btn.jq_cornerRadius = 4
                                return btn
                }()
    private lazy var btn_continue:UIButton = {
        let btn = UIButton(type: .custom)
        btn.setTitle("继续答题", for: .normal)
        btn.titleLabel?.font = .systemFont(ofSize: 14, weight: .medium)
        btn.setTitleColor(Config.ThemeColor, for: .normal)
        btn.jq_borderColor = Config.ThemeColor
        btn.backgroundColor = .white
        btn.jq_borderWidth = 1
        btn.jq_cornerRadius = 4
        return btn
    }()
                private lazy var pageVC:FFPageViewController = {
                                let vc = FFPageViewController()
                                vc.scrollview.isScrollEnabled = false
                                return vc
                }()
    private lazy var btn_forward_mini:UIButton = {
        let btn = UIButton(type: .custom)
        btn.setTitle("上一小题", for: .normal)
        btn.titleLabel?.font = .systemFont(ofSize: 14, weight: .medium)
        btn.setTitleColor(Config.ThemeColor, for: .normal)
        btn.jq_borderColor = Config.ThemeColor
        btn.backgroundColor = .white
        btn.isHidden = true
        btn.jq_borderWidth = 1
        btn.jq_cornerRadius = 4
        return btn
    }()
                private var timer:Timer!
    private lazy var btn_next:UIButton = {
        let btn = UIButton(type: .custom)
        btn.setTitle("下一题", for: .normal)
        btn.titleLabel?.font = .systemFont(ofSize: 14, weight: .medium)
        btn.setTitleColor(Config.ThemeColor, for: .normal)
        btn.jq_borderColor = Config.ThemeColor
        btn.backgroundColor = .white
        btn.jq_borderWidth = 1
        btn.jq_cornerRadius = 4
        return btn
    }()
    private lazy var btn_exit:UIButton = {
        let btn = UIButton(type: .custom)
        btn.setTitle("退出", for: .normal)
        btn.titleLabel?.font = .systemFont(ofSize: 14, weight: .medium)
        btn.setTitleColor(.white, for: .normal)
        btn.backgroundColor = Config.ThemeColor
        btn.jq_cornerRadius = 4
        return btn
    }()
                init(listenType:ListenType,quarter:Int? = nil,week:Int? = nil,day:Int? = nil) {
                                super.init(nibName: nil, bundle: nil)
                                self.viewModel.listenType.accept(listenType)
                                self.viewModel.week.accept(week)
                                self.viewModel.day.accept(day)
                                self.viewModel.quarter.accept(quarter)
    private lazy var pageVC:FFPageViewController = {
        let vc = FFPageViewController()
        vc.scrollview.isScrollEnabled = false
        return vc
    }()
                                if listenType == .game1 || listenType == .game2{
                                                self.viewModel.maxPage.accept(1)
                                }
                }
    private var timer:Timer!
                required init?(coder: NSCoder) {
                                fatalError("init(coder:) has not been implemented")
                }
                override func viewDidDisappear(_ animated: Bool) {
                                super.viewDidDisappear(animated)
                                sceneDelegate?.suspendTimer()
                                self.navigationController?.interactivePopGestureRecognizer?.isEnabled = true
                }
    init(listenType:ListenType,quarter:Int? = nil,week:Int? = nil,day:Int? = nil) {
        super.init(nibName: nil, bundle: nil)
        self.viewModel.listenType.accept(listenType)
        self.viewModel.week.accept(week)
        self.viewModel.day.accept(day)
        self.viewModel.quarter.accept(quarter)
                override func viewDidLoad() {
                                super.viewDidLoad()
        if listenType == .game1 || listenType == .game2{
            self.viewModel.maxPage.accept(1)
        }
    }
                                self.navigationController?.interactivePopGestureRecognizer?.isEnabled = false
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
                                yy_popBlock = {[weak self] in
                                                self?.quitAction(isPop: true)
                                }
    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        sceneDelegate?.suspendTimer()
        self.navigationController?.interactivePopGestureRecognizer?.isEnabled = true
    }
                                btn_exit.addTarget(self, action: #selector(quitAction), for: .touchUpInside)
                                btn_forward.addTarget(self, action: #selector(beforeAction), for: .touchUpInside)
                                btn_forward_mini.addTarget(self, action: #selector(beforeAction_mini), for: .touchUpInside)
                                btn_next.addTarget(self, action: #selector(nextAction), for: .touchUpInside)
    override func viewDidLoad() {
        super.viewDidLoad()
                                setPages()
                                pageVC.reloadData()
        self.navigationController?.interactivePopGestureRecognizer?.isEnabled = false
                                timer = Timer(fire: .distantPast, interval: 1.0, repeats: true, block: {[weak self] _ in
                                                self?.viewModel.times += 1
                                })
        yy_popBlock = {[weak self] in
            self?.quitAction(isPop: true)
        }
                                timer.fire()
                                RunLoop.current.add(timer, forMode: .common)
        btn_exit.addTarget(self, action: #selector(quitAction), for: .touchUpInside)
        btn_forward.addTarget(self, action: #selector(beforeAction), for: .touchUpInside)
        btn_forward_mini.addTarget(self, action: #selector(beforeAction_mini), for: .touchUpInside)
        btn_next.addTarget(self, action: #selector(nextAction), for: .touchUpInside)
        btn_beAgain.addTarget(self, action: #selector(beAgaionAction), for: .touchUpInside)
        setPages()
        pageVC.reloadData()
                                if let teamSchedule = teamScheduleModel{
                                                viewModel.correctNum = viewModel.correctNum + teamSchedule.correctNumber
                                                viewModel.errorNum = teamSchedule.answerNumber - teamSchedule.correctNumber
                                                maxPage    = teamSchedule.schedule
                                                let ids = (data as! ListenNewModel).data!.id.components(separatedBy: ",")
                                                switch viewModel.listenType.value {
                                                                case .lesson1:
                                                                                let nextPage = floor(Double(maxPage) / 5.0)
                                                                                pageVC.scroll(toPage: Int(nextPage), animation: false)
                                                                                viewModel.answerCount.accept(maxPage)
                                                                                setPages()
        timer = Timer(fire: .distantPast, interval: 1.0, repeats: true, block: {[weak self] _ in
            self?.viewModel.times += 1
        })
                                                                case .lesson2:
                                                                                let maxCount = (data as! ListenNewModel).subjectList.count
                                                                                let page = min((maxPage - 1),maxCount)
                                                                                if pageVC.currentPage != page{
                                                                                                viewModel.currentPage.accept(page)
                                                                                                pageVC.scroll(toPage: page, animation: false)
                                                                                                setPages()
                                                                                }
        timer.fire()
        RunLoop.current.add(timer, forMode: .common)
        setPages()
                                                                case .lesson4:
                                                                                let maxCount = (data as! ListenNewModel).subjectList.count
                                                                                let page = min((maxPage - 1),maxCount)
                                                                                if pageVC.currentPage != page{
                                                                                                viewModel.currentPage.accept(page)
                                                                                                pageVC.scroll(toPage: page, animation: false)
                                                                                                setPages()
                                                                                }
        if let teamSchedule = teamScheduleModel{
            viewModel.correctNum = viewModel.correctNum + teamSchedule.correctNumber
            viewModel.errorNum = teamSchedule.answerNumber - teamSchedule.correctNumber
            maxPage    = teamSchedule.schedule
            switch viewModel.listenType.value{
            case .lesson1:
                                                                case .lesson3,.lesson5:
                                                                                let maxCount = (data as! ListenNewModel).subjectList.count
                                                                                let page = min((maxPage - 1),maxCount)
                                                                                if pageVC.currentPage != page{
                                                                                                viewModel.currentPage.accept(page)
                                                                                                pageVC.scroll(toPage: page, animation: false)
                                                                                                setPages()
                                                                                }
                if let m  = data as? ListenNewModel{
                    self.pages = Array<ListenSubCardModel>.splitArray(m.list, subArraySize: 4)
                    let c = m.list.count / 4
                    let totalW = 24 * c + 2 * (c - 1)
                    collection_card.snp.updateConstraints { make in
                        make.width.equalTo(totalW)
                    }
                }
            case .lesson2,.lesson3,.lesson4,.lesson5:
                if let m  = data as? ListenNewModel{
                    self.pages = Array<ListenSubCardModel>.splitArray(m.list, subArraySize: 1)
                    let totalW = 24 * m.list.count + 2 * (m.list.count - 1)
                    collection_card.snp.updateConstraints { make in
                        make.width.equalTo(totalW)
                    }
                }
            default:break
            }
            switch viewModel.listenType.value {
            case .lesson1:
                collection_card.reloadData()
                let nextPage = floor(Double(maxPage) / 5.0)
                pageVC.scroll(toPage: Int(nextPage), animation: false)
                viewModel.answerCount.accept(maxPage)
                setPages()
                                                                default:break
                                                }
                                }
                }
            case .lesson2:
                collection_card.reloadData()
                let maxCount = (data as! ListenNewModel).subjectList.count
                let page = min((maxPage - 1),maxCount)
                if pageVC.currentPage != page{
                    viewModel.currentPage.accept(page)
                    pageVC.scroll(toPage: page, animation: false)
                    setPages()
                }
                override func setUI() {
                                super.setUI()
                                craeteFootFuncView()
            case .lesson4:
                collection_card.reloadData()
                let maxCount = (data as! ListenNewModel).subjectList.count
                let page = min((maxPage - 1),maxCount)
                if pageVC.currentPage != page{
                    viewModel.currentPage.accept(page)
                    pageVC.scroll(toPage: page, animation: false)
                    setPages()
                }
                                pageVC.delegate = self
                                view.addSubview(pageVC.view)
                                pageVC.view.snp.makeConstraints { make in
                                                make.left.right.equalToSuperview()
                                                make.top.equalTo(self.view.safeAreaLayoutGuide.snp.top)
                                                if self.viewModel.listenType.value == .lesson3 || self.viewModel.listenType.value == .lesson4{
                                                                make.bottom.equalTo(self.label_pageNum.snp.top).offset(-18)
                                                }else{
                                                                make.bottom.equalTo(self.label_pageNum.snp.top).offset(-32)
                                                }
                                }
                }
            case .lesson3,.lesson5:
                collection_card.reloadData()
                let maxCount = (data as! ListenNewModel).subjectList.count
                let page = min((maxPage - 1),maxCount)
                if pageVC.currentPage != page{
                    viewModel.currentPage.accept(page)
                    pageVC.scroll(toPage: page, animation: false)
                    setPages()
                }
                private func showGameLevel(canLevel:Int){
                                ChooseLevelView.show(canLevel: canLevel) {[weak self] level in
                                                guard let weakSelf = self else { return }
                                                weakSelf.viewModel.gameLevel.accept(level)
                                                Services.gameHearing(difficulty: level, quarter: weakSelf.viewModel.quarter.value!, week: weakSelf.viewModel.week.value!).subscribe(onNext: {result in
                                                                GameBeginTipView.show {
                                                                                if let data = result.data{
                                                                                                weakSelf.data = data
                                                                                                (weakSelf.data as! Listen1Model).data?.playNow = true
                                                                                                weakSelf.pageVC.reloadData()
                                                                                }
                                                                }
                                                },onError: {[weak self] _ in
                                                                self?.navigationController?.popViewController(animated: true)
                                                }).disposed(by: weakSelf.disposeBag)
                                } cancelClouse: { [weak self] in
                                                self?.navigationController?.popViewController(animated: true)
                                }
                }
                private func craeteFootFuncView(){
            default:break
            }
        }
    }
    override func setUI() {
        super.setUI()
                                btn_forward.snp.makeConstraints { make in
                                                make.height.equalTo(40)
                                                make.width.equalTo(124)
                                }
        switch viewModel.listenType.value{
        case .lesson1,.lesson2,.lesson3,.lesson4,.lesson5:
            collection_card.delegate = self
            collection_card.dataSource = self
            collection_card.register(CardItemCCell.self, forCellWithReuseIdentifier: "_CardItemCCell")
        default:break
        }
                                btn_exit.snp.makeConstraints { make in
                                                make.height.equalTo(40)
                                                make.width.equalTo(124)
                                }
        craeteFootFuncView()
                                let stackView = UIStackView(arrangedSubviews: [btn_forward,label_pageNum,btn_exit])
                                if viewModel.listenType.value == .story2{
                                                btn_next.snp.makeConstraints { make in
                                                                make.height.equalTo(40)
                                                                make.width.equalTo(124)
                                                }
                                                stackView.insertArrangedSubview(btn_next, at: 2)
                                }
        pageVC.delegate = self
        view.addSubview(pageVC.view)
        pageVC.view.snp.makeConstraints { make in
            make.left.right.equalToSuperview()
            make.top.equalTo(self.view.safeAreaLayoutGuide.snp.top)
            if self.viewModel.listenType.value == .lesson3 || self.viewModel.listenType.value == .lesson4{
                make.bottom.equalTo(self.label_pageNum.snp.top).offset(-52)
            }else{
                make.bottom.equalTo(self.label_pageNum.snp.top).offset(-52)
            }
        }
    }
                                stackView.spacing = 22
                                view.addSubview(stackView)
                                stackView.snp.makeConstraints { make in
                                                make.bottom.equalTo(self.view.safeAreaLayoutGuide.snp.bottom).offset(-22)
                                                make.centerX.equalToSuperview()
                                                make.height.equalTo(40)
                                }
                }
    private func showGameLevel(canLevel:Int){
        ChooseLevelView.show(canLevel: canLevel) {[weak self] level in
            guard let weakSelf = self else { return }
            weakSelf.viewModel.gameLevel.accept(level)
            Services.gameHearing(difficulty: level, quarter: weakSelf.viewModel.quarter.value!, week: weakSelf.viewModel.week.value!).subscribe(onNext: {result in
                GameBeginTipView.show {
                    if let data = result.data{
                        weakSelf.data = data
                        (weakSelf.data as! Listen1Model).data?.playNow = true
                        weakSelf.pageVC.reloadData()
                    }
                }
            },onError: {[weak self] _ in
                self?.navigationController?.popViewController(animated: true)
            }).disposed(by: weakSelf.disposeBag)
        } cancelClouse: { [weak self] in
            self?.navigationController?.popViewController(animated: true)
        }
    }
    private func craeteFootFuncView(){
        view.addSubview(collection_card)
        btn_forward.snp.makeConstraints { make in
            make.height.equalTo(40)
            make.width.equalTo(124)
        }
        btn_beAgain.snp.makeConstraints { make in
            make.height.equalTo(40)
            make.width.equalTo(124)
        }
        btn_continue.snp.makeConstraints { make in
            make.height.equalTo(40)
            make.width.equalTo(124)
        }
        btn_exit.snp.makeConstraints { make in
            make.height.equalTo(40)
            make.width.equalTo(124)
        }
        let stackView = UIStackView(arrangedSubviews: [btn_beAgain,label_pageNum,btn_exit])
        if viewModel.listenType.value == .story2{
            btn_next.snp.makeConstraints { make in
                make.height.equalTo(40)
                make.width.equalTo(124)
            }
            stackView.insertArrangedSubview(btn_next, at: 2)
        }
        stackView.spacing = 22
        view.addSubview(stackView)
        stackView.snp.makeConstraints { make in
            make.bottom.equalTo(self.view.safeAreaLayoutGuide.snp.bottom).offset(-5)
            make.centerX.equalToSuperview()
            make.height.equalTo(40)
        }
        switch viewModel.listenType.value{
        case .lesson1,.lesson2,.lesson3,.lesson4,.lesson5:
            collection_card.snp.makeConstraints { make in
                make.centerX.equalToSuperview()
                make.width.equalTo(10)
                make.bottom.equalTo(stackView.snp.top).offset(-10)
                make.height.equalTo(24)
            }
        default:break
        }
    }
    override func setRx() {
        NotificationCenter.default.rx.notification(NextLession_Noti).take(until: self.rx.deallocated).subscribe(onNext: {[weak self] noti in
            guard let weakSelf = self else { return }
            let nextPage = weakSelf.viewModel.currentPage.value + 1
            var asComplete:Bool = false
            switch weakSelf.viewModel.listenType.value {
            case .lesson1,.lesson2,.lesson3,.lesson4,.lesson5:asComplete = nextPage >= (weakSelf.data as! ListenNewModel).subjectList.count
            case .game1,.game2:asComplete = true
            case .story1,.story2: asComplete = nextPage >= (weakSelf.data as! Listen1Model).storyList.count
            }
            if asComplete{
                switch weakSelf.viewModel.listenType.value {
                case .lesson1,.lesson2,.lesson3,.lesson4,.lesson5:
                    weakSelf.studyComplete()
                case .game1,.game2:
                    weakSelf.notiObject = noti.object as? Dictionary<String,Any>
                    weakSelf.timer.invalidate()
                    if let isComplete = weakSelf.notiObject?["complete"] as? Bool{
                        if isComplete{
                            weakSelf.btn_exit.setTitle("提交", for: .normal)
                        }else{
                            weakSelf.gamesComplete(gameId: weakSelf.notiObject!["gameId"] as! Int,integral: weakSelf.notiObject!["gameIntegral"] as! Int)
                        }
                    }
                case .story1,.story2:
                    if let dict = noti.object as? Dictionary<String,Any>{
                        let type = weakSelf.viewModel.listenType.value == .story1 ? 1:2
                        let accracy = floor(Double(weakSelf.viewModel.correctNum) / Double(weakSelf.viewModel.correctNum + weakSelf.viewModel.errorNum) * 100).int
                        weakSelf.storyComplete(storyId: dict["storyId"] as! Int, accuracy: accracy, studyTime: weakSelf.viewModel.times, type: type, integral: dict["storyIntegral"] as! Int)
                    }
                }
                return
            }
            if weakSelf.viewModel.listenType.value == .story2{
                weakSelf.btn_next.isHidden = (nextPage + 1) == weakSelf.viewModel.maxPage.value
                if weakSelf.btn_next.isHidden{
                    weakSelf.btn_exit.setTitle("完成", for: .normal)
                }
            }
            weakSelf.listenFightLine = .next
            weakSelf.pageVC.scroll(toPage: nextPage, animation: true)
            weakSelf.viewModel.currentPage.accept(nextPage)
        }).disposed(by: disposeBag)
        viewModel.currentPage.subscribe(onNext: {[weak self]currentPage in
            guard let weakSelf = self else { return }
            weakSelf.btn_forward.isHidden = currentPage <= 0
            weakSelf.setPages()
        }).disposed(by: disposeBag)
        viewModel.answerCount.subscribe(onNext: {[weak self] count in
            self?.setPages()
        }).disposed(by: disposeBag)
    }
    private func setPages(){
        switch viewModel.listenType.value{
        case .lesson1:
            let m = data as! ListenNewModel
            label_pageNum.text = "已完成:\(viewModel.currentPage.value + 1)/\(m.subjectList.flatMap({$0}).count / 4)"
            let correctNum = m.list.filter({$0.status == 2}).count //正确
            //                if correctNum > 0 {
            let ratio = Double(correctNum) / Double(m.list.count) * 100.0
            let ratioStr = ratio.jq_formatFloat
            label_pageNum.text = "已完成:\(viewModel.currentPage.value + 1)/\(m.subjectList.flatMap({$0}).count / 4)\n正确率:\(ratioStr)%"
            //                }
            maxPage = viewModel.answerCount.value
            btn_forward.isHidden = viewModel.answerCount.value == 1
        case .lesson3:
            let m = data as! ListenNewModel
            label_pageNum.text = "已完成:\(viewModel.currentPage.value + 1)/\(m.subjectList.count / 6)"
            btn_forward.isHidden = viewModel.currentPage.value == 0
            let page = viewModel.currentPage.value + 1
            maxPage = page
                override func setRx() {
                                NotificationCenter.default.rx.notification(NextLession_Noti).take(until: self.rx.deallocated).subscribe(onNext: {[weak self] noti in
                                                guard let weakSelf = self else { return }
                                                let nextPage = weakSelf.viewModel.currentPage.value + 1
                                                var asComplete:Bool = false
                                                switch weakSelf.viewModel.listenType.value {
                                                                case .lesson1,.lesson2,.lesson3,.lesson4,.lesson5:asComplete = nextPage >= (weakSelf.data as! ListenNewModel).subjectList.count
                                                                case .game1,.game2:asComplete = true
                                                                case .story1,.story2: asComplete = nextPage >= (weakSelf.data as! Listen1Model).storyList.count
                                                }
            let correctNum = m.list.filter({$0.status == 2}).count //正确
            let ratio = Double(correctNum) / Double(m.list.count) * 100.0
            let ratioStr = ratio.jq_formatFloat
            label_pageNum.text = "已完成:\(viewModel.answerCount.value)/\(m.subjectList.flatMap({$0}).count)\n正确率:\(ratioStr)%"
                                                if asComplete{
                                                                switch weakSelf.viewModel.listenType.value {
                                                                                case .lesson1,.lesson2,.lesson3,.lesson4,.lesson5:
                                                                                                weakSelf.studyComplete()
                                                                                case .game1,.game2:
                                                                                                weakSelf.notiObject = noti.object as? Dictionary<String,Any>
                                                                                                weakSelf.timer.invalidate()
        case .lesson2,.lesson5:
            let m = data as! ListenNewModel
            label_pageNum.text = "已完成:\(viewModel.currentPage.value + 1)/\(m.subjectList.count)"
            btn_forward.isHidden = viewModel.currentPage.value == 0
            let page = viewModel.currentPage.value + 1
            maxPage = page
                                                                                                if let isComplete = weakSelf.notiObject?["complete"] as? Bool{
                                                                                                                if isComplete{
                                                                                                                                weakSelf.btn_exit.setTitle("提交", for: .normal)
                                                                                                                }else{
                                                                                                                                weakSelf.gamesComplete(gameId: weakSelf.notiObject!["gameId"] as! Int,integral: weakSelf.notiObject!["gameIntegral"] as! Int)
                                                                                                                }
                                                                                                }
                                                                                case .story1,.story2:
                                                                                                if let dict = noti.object as? Dictionary<String,Any>{
                                                                                                                let type = weakSelf.viewModel.listenType.value == .story1 ? 1:2
                                                                                                                let accracy = floor(Double(weakSelf.viewModel.correctNum) / Double(weakSelf.viewModel.correctNum + weakSelf.viewModel.errorNum) * 100).int
                                                                                                                weakSelf.storyComplete(storyId: dict["storyId"] as! Int, accuracy: accracy, studyTime: weakSelf.viewModel.times, type: type, integral: dict["storyIntegral"] as! Int)
                                                                                                }
                                                                }
                                                                return
                                                }
            let correctNum = m.list.filter({$0.status == 2}).count //正确
            let ratio = Double(correctNum) / Double(m.list.count) * 100.0
            let ratioStr = ratio.jq_formatFloat
            label_pageNum.text = "已完成:\(viewModel.answerCount.value)/\(m.subjectList.flatMap({$0}).count)\n正确率:\(ratioStr)%"
                                                if weakSelf.viewModel.listenType.value == .story2{
                                                                weakSelf.btn_next.isHidden = (nextPage + 1) == weakSelf.viewModel.maxPage.value
                                                                if weakSelf.btn_next.isHidden{
                                                                                weakSelf.btn_exit.setTitle("完成", for: .normal)
                                                                }
                                                }
        case .lesson4:
            let m = data as! ListenNewModel
            //两题为一组:需要/2
            label_pageNum.text = "已完成:\(viewModel.currentPage.value + 1)/\(m.subjectList.count)"
            let page = viewModel.currentPage.value + 1
            //                                                                maxPage = max(page,maxPage)
            maxPage = page
                                                weakSelf.listenFightLine = .next
                                                weakSelf.pageVC.scroll(toPage: nextPage, animation: true)
                                                weakSelf.viewModel.currentPage.accept(nextPage)
                                }).disposed(by: disposeBag)
            let correctNum = m.list.filter({$0.status == 2}).count //正确
            let ratio = Double(correctNum) / Double(m.list.count) * 100.0
            let ratioStr = ratio.jq_formatFloat
            label_pageNum.text = "已完成:\(viewModel.answerCount.value)/\(m.subjectList.flatMap({$0}).count)\n正确率:\(ratioStr)%"
        case .game1,.game2:
            btn_forward.isHidden = true
            label_pageNum.isHidden = true
                                viewModel.currentPage.subscribe(onNext: {[weak self]currentPage in
                                                guard let weakSelf = self else { return }
                                                weakSelf.btn_forward.isHidden = currentPage <= 0
                                                weakSelf.setPages()
                                }).disposed(by: disposeBag)
            if viewModel.listenType.value == .game1{
                showGameLevel(canLevel: studyScheduleModel?.gameDifficulty ?? 0)
            }
        case .story1:
            if viewModel.listenType.value == .story2{
                btn_next.isHidden = (viewModel.currentPage.value + 1) == viewModel.maxPage.value
                if btn_next.isHidden{
                    btn_exit.setTitle("完成", for: .normal)
                }
            }
            fallthrough
        case .story2:
            let count = (data as! Listen1Model).storyList.count
            viewModel.maxPage.accept(count)
            label_pageNum.text = "已完成:\(viewModel.currentPage.value + 1)/\(count)"
        }
    }
                                viewModel.answerCount.subscribe(onNext: {[weak self] count in
                                                self?.setPages()
                                }).disposed(by: disposeBag)
                }
                private func setPages(){
                                switch viewModel.listenType.value{
                                                case .lesson1:
                                                                label_pageNum.text = "已完成:\(viewModel.answerCount.value)/\((data as! ListenNewModel).subjectList.flatMap({$0}).count)"
//                                                                maxPage = max(viewModel.answerCount.value,maxPage)
                                                                maxPage = viewModel.answerCount.value
                                                                btn_forward.isHidden = viewModel.answerCount.value == 1
                                                case .lesson2,.lesson3,.lesson5:
                                                                label_pageNum.text = "已完成:\(viewModel.currentPage.value + 1)/\((data as! ListenNewModel).subjectList.count)"
                                                                btn_forward.isHidden = viewModel.currentPage.value == 0
                                                                let page = viewModel.currentPage.value + 1
//                                                                maxPage = max(page,maxPage)
                                                                maxPage = page
                                                case .lesson4:
                                                                //两题为一组:需要/2
                                                                label_pageNum.text = "已完成:\(viewModel.currentPage.value + 1)/\((data as! ListenNewModel).subjectList.count)"
                                                                let page = viewModel.currentPage.value + 1
//                                                                maxPage = max(page,maxPage)
                                                                maxPage = page
                                                case .game1,.game2:
                                                                btn_forward.isHidden = true
                                                                label_pageNum.isHidden = true
    /// 学习类完成
    /// - Parameter ignorePush: 是否忽略跳转(未完成答题 :true)
    private func studyComplete(){
        let ids:String = viewModel.answerItems_1.keys.sorted().joined(separator: ",")
                                                                if viewModel.listenType.value == .game1{
                                                                                showGameLevel(canLevel: studyScheduleModel?.gameDifficulty ?? 0)
                                                                }
                                                case .story1:
                                                                if viewModel.listenType.value == .story2{
                                                                                btn_next.isHidden = (viewModel.currentPage.value + 1) == viewModel.maxPage.value
                                                                                if btn_next.isHidden{
                                                                                                btn_exit.setTitle("完成", for: .normal)
                                                                                }
                                                                }
                                                                fallthrough
                                                case .story2:
                                                                let count = (data as! Listen1Model).storyList.count
                                                                viewModel.maxPage.accept(count)
                                                                label_pageNum.text = "已完成:\(viewModel.currentPage.value + 1)/\(count)"
                                }
                }
        //正确率
        let accracy = floor(Double(viewModel.correctNum) / Double(viewModel.correctNum + viewModel.errorNum) * 100).int
        Services.completeLearing(type: viewModel.listenType.value.rawValue, studyTime: viewModel.times, studyIds: ids, quarter: viewModel.quarter.value!, week: viewModel.week.value!, day: viewModel.day.value!, accracy: accracy).subscribe(onNext: {data in
            NotificationCenter.default.post(name: Refresh_ListenSchedule_Noti, object: nil)
            NotificationCenter.default.post(name: StudyCompleteCoinUpdate_Noti, object: data.data ?? 0)
        }).disposed(by: disposeBag)
                /// 学习类完成
                /// - Parameter ignorePush: 是否忽略跳转(未完成答题 :true)
                private func studyComplete(){
                                let ids:String = viewModel.answerItems_1.keys.sorted().joined(separator: ",")
        timer.invalidate()
                                //正确率
                                let accracy = floor(Double(viewModel.correctNum) / Double(viewModel.correctNum + viewModel.errorNum) * 100).int
        let vc = HomeStudyCompleteVC(viewModel: viewModel,studyScheduleModel: studyScheduleModel!)
        vc.title = viewModel.listenType.value.rawTitle
        vc.viewModel = viewModel
        push(vc: vc)
    }
                                Services.completeLearing(type: viewModel.listenType.value.rawValue, studyTime: viewModel.times, studyIds: ids, quarter: viewModel.quarter.value!, week: viewModel.week.value!, day: viewModel.day.value!, accracy: accracy).subscribe(onNext: {data in
                                                NotificationCenter.default.post(name: Refresh_ListenSchedule_Noti, object: nil)
                                                NotificationCenter.default.post(name: StudyCompleteCoinUpdate_Noti, object: data.data ?? 0)
                                }).disposed(by: disposeBag)
    //游戏类完成
    private func gamesComplete(gameId:Int,integral:Int){
                                timer.invalidate()
        var name = ""
        var accuracy:Int = 0
        var totalNum:Double = 0
                                let vc = HomeStudyCompleteVC(viewModel: viewModel,studyScheduleModel: studyScheduleModel!)
                                vc.title = viewModel.listenType.value.rawTitle
                                vc.viewModel = viewModel
                                push(vc: vc)
                }
        if viewModel.listenType.value == .game1{
            name = "超级听力"
            totalNum =  Double(viewModel.correctNum + viewModel.errorNum)
            if totalNum > 0{
                accuracy = floor(Double(viewModel.correctNum) / totalNum * 100).int
            }
        }else{
            name = "超级记忆"
            let v = viewModel.answerItems.first?.value as! Listen1Model
            //11887:完成答题页,总题目、错误题目 数量计算逻辑错误(只要是没有答对的题目就算错误题目,不管是否答,相当于错误题目就是总题目减去正确题目)
            totalNum = Double(v.photoList.count)
            if totalNum > 0 && viewModel.correctNum > 0 && viewModel.errorNum > 0{
                accuracy = floor(Double(viewModel.correctNum) / Double(totalNum) * 100).int
            }
            viewModel.errorNum = Int(totalNum) - viewModel.correctNum
        }
                //游戏类完成
                private func gamesComplete(gameId:Int,integral:Int){
        Services.completeGames(gameId: gameId, gameName: name, difficulty: viewModel.gameLevel.value, accuracy: accuracy, useTime: viewModel.times).subscribe(onNext: {data in
            NotificationCenter.default.post(name: Refresh_ListenSchedule_Noti, object: nil)
            NotificationCenter.default.post(name: StudyCompleteCoinUpdate_Noti, object: data.data ?? 0)
        }).disposed(by: disposeBag)
                                var name = ""
                                var accuracy:Int = 0
                                var totalNum:Double = 0
        timer.invalidate()
                                if viewModel.listenType.value == .game1{
                                                name = "超级听力"
                                                totalNum =  Double(viewModel.correctNum + viewModel.errorNum)
                                                if totalNum > 0{
                                                                accuracy = floor(Double(viewModel.correctNum) / totalNum * 100).int
                                                }
                                }else{
                                                name = "超级记忆"
                                                let v = viewModel.answerItems.first?.value as! Listen1Model
                                                //11887:完成答题页,总题目、错误题目 数量计算逻辑错误(只要是没有答对的题目就算错误题目,不管是否答,相当于错误题目就是总题目减去正确题目)
                                                totalNum = Double(v.photoList.count)
                                                if totalNum > 0 && viewModel.correctNum > 0 && viewModel.errorNum > 0{
                                                                accuracy = floor(Double(viewModel.correctNum) / Double(totalNum) * 100).int
                                                }
                                                viewModel.errorNum = Int(totalNum) - viewModel.correctNum
                                }
        let vc = HomeStudyCompleteVC(totalNum:totalNum.int,viewModel: viewModel,studyScheduleModel: studyScheduleModel!)
        vc.title = viewModel.listenType.value.rawTitle
        vc.viewModel = viewModel
        push(vc: vc)
    }
                                Services.completeGames(gameId: gameId, gameName: name, difficulty: viewModel.gameLevel.value, accuracy: accuracy, useTime: viewModel.times).subscribe(onNext: {data in
                                                NotificationCenter.default.post(name: Refresh_ListenSchedule_Noti, object: nil)
                                                NotificationCenter.default.post(name: StudyCompleteCoinUpdate_Noti, object: data.data ?? 0)
                                }).disposed(by: disposeBag)
    private func storyComplete(storyId:Int,accuracy:Int,studyTime:Int,type:Int,integral:Int){
        timer.invalidate()
        Services.completeStory(storyId: storyId, accuracy: accuracy, studyTime: studyTime, type: type).subscribe(onNext: {data in
            NotificationCenter.default.post(name: Refresh_ListenSchedule_Noti, object: nil)
            NotificationCenter.default.post(name: StudyCompleteCoinUpdate_Noti, object: data.data ?? 0)
        }).disposed(by: disposeBag)
                                timer.invalidate()
        let vc = HomeStudyCompleteVC(viewModel: viewModel,studyScheduleModel: studyScheduleModel!)
        vc.title = viewModel.listenType.value.rawTitle
        vc.viewModel = viewModel
        push(vc: vc)
    }
                                let vc = HomeStudyCompleteVC(totalNum:totalNum.int,viewModel: viewModel,studyScheduleModel: studyScheduleModel!)
                                vc.title = viewModel.listenType.value.rawTitle
                                vc.viewModel = viewModel
                                push(vc: vc)
                }
    deinit{
        timer.invalidate()
    }
                private func storyComplete(storyId:Int,accuracy:Int,studyTime:Int,type:Int,integral:Int){
                                timer.invalidate()
                                Services.completeStory(storyId: storyId, accuracy: accuracy, studyTime: studyTime, type: type).subscribe(onNext: {data in
                                                NotificationCenter.default.post(name: Refresh_ListenSchedule_Noti, object: nil)
                                                NotificationCenter.default.post(name: StudyCompleteCoinUpdate_Noti, object: data.data ?? 0)
                                }).disposed(by: disposeBag)
    @objc func quitAction(isPop:Bool = false){
        if btn_exit.titleLabel?.text == "完成"{
            if viewModel.listenType.value == .story2{
                                let vc = HomeStudyCompleteVC(viewModel: viewModel,studyScheduleModel: studyScheduleModel!)
                                vc.title = viewModel.listenType.value.rawTitle
                                vc.viewModel = viewModel
                                push(vc: vc)
                }
                if isPop{
                    CommonAlertView.show(content: "未完成全部答题,确认退出吗?") {[weak self] () in
                        guard let weakSelf = self else { return }
                        for vc in weakSelf.navigationController?.viewControllers ?? []{
                            if vc.isKind(of: HomeListenVC.self){
                                weakSelf.navigationController?.popToViewController(vc, animated: true)
                                NotificationCenter.default.post(name: Refresh_ListenSchedule_Noti, object: nil)
                                break
                            }
                        }
                    }
                    return
                }
                deinit{
                                timer.invalidate()
                }
                guard (pageVC.currentController as! HomeListenStory_2_VC).isPlayEnd else {
                    alert(msg: "请听完");return
                }
                @objc func quitAction(isPop:Bool = false){
                                if btn_exit.titleLabel?.text == "完成"{
                                                if viewModel.listenType.value == .story2{
                let v = data as! Listen1Model
                let accuracy = 100
                storyComplete(storyId: v.data!.id, accuracy: accuracy, studyTime: viewModel.times, type: viewModel.listenType.value == .story1 ? 1:2, integral: v.data!.integral)
            }
        }else if btn_exit.titleLabel?.text == "提交"{
                                                                if isPop{
                                                                                CommonAlertView.show(content: "未完成全部答题,确认退出吗?") {[weak self] () in
                                                                                                guard let weakSelf = self else { return }
                                                                                                for vc in weakSelf.navigationController?.viewControllers ?? []{
                                                                                                                if vc.isKind(of: HomeListenVC.self){
                                                                                                                                weakSelf.navigationController?.popToViewController(vc, animated: true)
                                                                                                                                NotificationCenter.default.post(name: Refresh_ListenSchedule_Noti, object: nil)
                                                                                                                                break
                                                                                                                }
                                                                                                }
                                                                                }
                                                                                return
                                                                }
            if isPop{
                CommonAlertView.show(content: "未完成全部答题,确认退出吗?") {[weak self] () in
                    guard let weakSelf = self else { return }
                    for vc in weakSelf.navigationController?.viewControllers ?? []{
                        if vc.isKind(of: HomeListenVC.self){
                            weakSelf.navigationController?.popToViewController(vc, animated: true)
                            NotificationCenter.default.post(name: Refresh_ListenSchedule_Noti, object: nil)
                            break
                        }
                    }
                }
                return
            }
                                                                guard (pageVC.currentController as! HomeListenStory_2_VC).isPlayEnd else {
                                                                                alert(msg: "请听完");return
                                                                }
            if viewModel.listenType.value == .game1 || viewModel.listenType.value == .game2{
                if let dict = notiObject{
                    gamesComplete(gameId: dict["gameId"] as! Int,integral: dict["gameIntegral"] as! Int)
                }
            }
        } else{
            CommonAlertView.show(content: "未完成全部答题,确认退出吗?") {[weak self] () in
                guard let weakSelf = self else { return }
                                                                let v = data as! Listen1Model
                                                                let accuracy = 100
                                                                storyComplete(storyId: v.data!.id, accuracy: accuracy, studyTime: viewModel.times, type: viewModel.listenType.value == .story1 ? 1:2, integral: v.data!.integral)
                                                }
                                }else if btn_exit.titleLabel?.text == "提交"{
                let temIds = [String]()
                let topicIds = [String]()
                                                if isPop{
                                                                CommonAlertView.show(content: "未完成全部答题,确认退出吗?") {[weak self] () in
                                                                                guard let weakSelf = self else { return }
                                                                                for vc in weakSelf.navigationController?.viewControllers ?? []{
                                                                                                if vc.isKind(of: HomeListenVC.self){
                                                                                                                weakSelf.navigationController?.popToViewController(vc, animated: true)
                                                                                                                NotificationCenter.default.post(name: Refresh_ListenSchedule_Noti, object: nil)
                                                                                                                break
                                                                                                }
                                                                                }
                                                                }
                                                                return
                                                }
                                                if viewModel.listenType.value == .game1 || viewModel.listenType.value == .game2{
                                                                if let dict = notiObject{
                                                                                gamesComplete(gameId: dict["gameId"] as! Int,integral: dict["gameIntegral"] as! Int)
                                                                }
                                                }
                                } else{
                                                CommonAlertView.show(content: "未完成全部答题,确认退出吗?") {[weak self] () in
                                                                guard let weakSelf = self else { return }
                switch weakSelf.viewModel.listenType.value{
                case .lesson1,.lesson2,.lesson3,.lesson4,.lesson5:
                    let totalNum = weakSelf.viewModel.correctNum + weakSelf.viewModel.errorNum
                    Services.exitLearning(type:weakSelf.viewModel.listenType.value.rawValue,quarter: weakSelf.viewModel.quarter.value!,week: weakSelf.viewModel.week.value!, day: weakSelf.viewModel.day.value!, teamIds: temIds, topicIds: topicIds,answerNumber: totalNum,correctNumber:weakSelf.viewModel.correctNum,studyTime:weakSelf.viewModel.times,schedule: weakSelf.maxPage).subscribe(onNext: { data in
                                                                let temIds = [String]()
                                                                let topicIds = [String]()
                        NotificationCenter.default.post(name: MeUserInfoUpdate_Noti, object: nil)
                    }).disposed(by: weakSelf.disposeBag)
                case .game1,.game2,.story1,.story2:
                    Services.exitGameOrStory(studyTime: weakSelf.viewModel.times).subscribe(onNext: { _ in
                                                                switch weakSelf.viewModel.listenType.value{
                                                                                case .lesson1,.lesson2,.lesson3,.lesson4,.lesson5:
                                                                                                let totalNum = weakSelf.viewModel.correctNum + weakSelf.viewModel.errorNum
                                                                                                Services.exitLearning(type:weakSelf.viewModel.listenType.value.rawValue,quarter: weakSelf.viewModel.quarter.value!,week: weakSelf.viewModel.week.value!, day: weakSelf.viewModel.day.value!, teamIds: temIds, topicIds: topicIds,answerNumber: totalNum,correctNumber:weakSelf.viewModel.correctNum,studyTime:weakSelf.viewModel.times,schedule: weakSelf.maxPage).subscribe(onNext: { data in
                    }).disposed(by: weakSelf.disposeBag)
                }
                for vc in weakSelf.navigationController?.viewControllers ?? []{
                    if vc.isKind(of: HomeListenVC.self){
                        weakSelf.navigationController?.popToViewController(vc, animated: true)
                        NotificationCenter.default.post(name: Refresh_ListenSchedule_Noti, object: nil)
                        break
                    }
                }
            }
        }
    }
                                                                                                                NotificationCenter.default.post(name: MeUserInfoUpdate_Noti, object: nil)
    @objc func beAgaionAction(){
        CommonAlertView.show(content: "是否重新开始答题?确认后将清空当前答题进度") {[unowned self] in
            let day = self.viewModel.day.value ?? 0
            let week = self.viewModel.week.value ?? 0
            let type = self.viewModel.listenType.value.rawValue
            Services.restart(day: day, type: type, week: week).subscribe(onNext: {[unowned self]_ in
                self.pageVC.scroll(toPage: 0, animation: true)
                if let m = (self.data as? ListenNewModel){
                    for v in m.list{
                        v.status = 1
                    }
                    self.restore()
                    self.setPages()
                    self.collection_card.reloadData()
                    NotificationCenter.default.post(name: ResetLession_Noti, object: nil)
                }
            }).disposed(by: disposeBag)
        }
    }
                                                                                                }).disposed(by: weakSelf.disposeBag)
                                                                                case .game1,.game2,.story1,.story2:
                                                                                                Services.exitGameOrStory(studyTime: weakSelf.viewModel.times).subscribe(onNext: { _ in
    @objc func nextAction(){
        listenFightLine = .next
        if viewModel.listenType.value == .story2{
                                                                                                }).disposed(by: weakSelf.disposeBag)
                                                                }
                                                                for vc in weakSelf.navigationController?.viewControllers ?? []{
                                                                                if vc.isKind(of: HomeListenVC.self){
                                                                                                weakSelf.navigationController?.popToViewController(vc, animated: true)
                                                                                                NotificationCenter.default.post(name: Refresh_ListenSchedule_Noti, object: nil)
                                                                                                break
                                                                                }
                                                                }
                                                }
                                }
                }
            guard (pageVC.currentController as! HomeListenStory_2_VC).isPlayEnd else {
                alert(msg: "请听完");return
            }
                @objc func nextAction(){
                                listenFightLine = .next
                                if viewModel.listenType.value == .story2{
            let v = data as! Listen1Model
            var dict = Dictionary<String,Any>()
            dict["storyId"] = v.data?.id ?? 0
            dict["storyIntegral"] = v.data?.lookIntegral ?? 0
            NotificationCenter.default.post(name: NextLession_Noti, object: data)
        }
    }
                                                guard (pageVC.currentController as! HomeListenStory_2_VC).isPlayEnd else {
                                                                alert(msg: "请听完");return
                                                }
    @objc func beforeAction_mini(){
        if viewModel.listenType.value == .lesson1{
            if let vc = pageVC.currentController as? HomeListenFight_lesson_1_VC{
                print("---->进入")
                vc.tobefore()
            }
        }
    }
                                                let v = data as! Listen1Model
                                                var dict = Dictionary<String,Any>()
                                                dict["storyId"] = v.data?.id ?? 0
                                                dict["storyIntegral"] = v.data?.lookIntegral ?? 0
                                                NotificationCenter.default.post(name: NextLession_Noti, object: data)
                                }
                }
    @objc func beforeAction(){
                @objc func beforeAction_mini(){
                                if viewModel.listenType.value == .lesson1{
                                                if let vc = pageVC.currentController as? HomeListenFight_lesson_1_VC{
                                                                print("---->进入")
                                                                vc.tobefore()
                                                }
                                }
                }
        listenFightLine = .before
        let beforePage = max(0, viewModel.currentPage.value - 1)
        switch viewModel.listenType.value {
        case .lesson1:
            if !(pageVC.currentController as! HomeListenFight_lesson_1_VC).isListen{
                alert(msg: "请听完");return
            }
                @objc func beforeAction(){
            let temp = viewModel.answerCount.value - 1
            viewModel.answerCount.accept(max(1,temp))
        case .lesson2:
            let temp = (beforePage * 4) + 1
            viewModel.answerCount.accept(max(1,temp))
        default:break
        }
                                listenFightLine = .before
                                let beforePage = max(0, viewModel.currentPage.value - 1)
                                switch viewModel.listenType.value {
                                                case .lesson1:
                                                                if !(pageVC.currentController as! HomeListenFight_lesson_1_VC).isListen{
                                                                                alert(msg: "请听完");return
                                                                }
                                                                let temp = viewModel.answerCount.value - 1
                                                                viewModel.answerCount.accept(max(1,temp))
                                                case .lesson2:
                                                                let temp = (beforePage * 4) + 1
                                                                viewModel.answerCount.accept(max(1,temp))
                                                default:break
                                }
        pageVC.scroll(toPage: beforePage, animation: true)
        viewModel.currentPage.accept(beforePage)
                                pageVC.scroll(toPage: beforePage, animation: true)
                                viewModel.currentPage.accept(beforePage)
        if viewModel.listenType.value == .story2{
            guard (pageVC.currentController as! HomeListenStory_2_VC).isPlayEnd else {
                alert(msg: "请听完");return
            }
        }
        if viewModel.listenType.value == .lesson1{
            let currentVC = pageVC.currentController as! HomeListenFight_lesson_1_VC
            //                                                if (viewModel.answerCount.value - 1 ) % 4 != 0 || viewModel.answerCount.value <= 4{
            currentVC.tobefore();return
            //                                                }
        }
                                if viewModel.listenType.value == .story2{
                                                guard (pageVC.currentController as! HomeListenStory_2_VC).isPlayEnd else {
                                                                alert(msg: "请听完");return
                                                }
                                }
        if viewModel.listenType.value == .lesson3{
            (pageVC.currentController as! HomeListenFight_lesson_3_VC).restore()
        }
                                if viewModel.listenType.value == .lesson1{
                                                let currentVC = pageVC.currentController as! HomeListenFight_lesson_1_VC
                                                //                                                if (viewModel.answerCount.value - 1 ) % 4 != 0 || viewModel.answerCount.value <= 4{
                                                currentVC.tobefore();return
                                                //                                                }
                                }
        if viewModel.listenType.value == .story2{
            btn_next.isHidden = false
        }
                                if viewModel.listenType.value == .lesson3{
                                                (pageVC.currentController as! HomeListenFight_lesson_3_VC).restore()
                                }
        btn_exit.setTitle("退出", for: .normal)
    }
                                if viewModel.listenType.value == .story2{
                                                btn_next.isHidden = false
                                }
    private func restore(){
        if let vc = pageVC.currentController as? HomeListenFight_lesson_1_VC{
            vc.restore()
        }
        if let vc = pageVC.currentController as? HomeListenFight_lesson_2_VC{
            vc.restore()
        }
        if let vc = pageVC.currentController as? HomeListenFight_lesson_3_VC{
            vc.restore()
        }
        if let vc = pageVC.currentController as? HomeListenFight_lesson_4_VC{
            vc.restore()
        }
        if let vc = pageVC.currentController as? HomeListenFight_lesson_5_VC{
            vc.restore()
        }
                                btn_exit.setTitle("退出", for: .normal)
                }
        if let vc = pageVC.currentController as? HomeListenStory_1_VC{
            vc.restore()
        }
        if let vc = pageVC.currentController as? HomeListenStory_2_VC{
            vc.restore()
        }
    }
}
extension HomeListenFightVC:FFPageViewControllerDelegate{
                func totalPagesOfpageViewController(_ pageViewConteoller: FFPageViewController) -> UInt {
    func totalPagesOfpageViewController(_ pageViewConteoller: FFPageViewController) -> UInt {
                                switch viewModel.listenType.value {
                                                case .lesson1,.lesson2,.lesson3,.lesson4,.lesson5:return UInt((data as! ListenNewModel).subjectList.count)
                                                case .story1,.story2:
                                                                return UInt((data as! Listen1Model).storyList.count)
                                                default:break
                                }
        switch viewModel.listenType.value {
        case .lesson1,.lesson2,.lesson3,.lesson4,.lesson5:return UInt((data as! ListenNewModel).subjectList.count)
        case .story1,.story2:
            return UInt((data as! Listen1Model).storyList.count)
        default:break
        }
                                //超级听力,只有一页
                                if viewModel.listenType.value == .game1 || viewModel.listenType.value == .game2{
                                                return 1
                                }
                                return UInt(viewModel.maxPage.value)
                }
        //超级听力,只有一页
        if viewModel.listenType.value == .game1 || viewModel.listenType.value == .game2{
            return 1
        }
        return UInt(viewModel.maxPage.value)
    }
                func pageViewController(_ pageViewController: FFPageViewController, currentPageChanged currentPage: Int) {
    func pageViewController(_ pageViewController: FFPageViewController, currentPageChanged currentPage: Int) {
                                if listenFightLine == .before{
                                                if let vc = pageViewController.currentController as? HomeListenFight_lesson_1_VC{
                                                                vc.restore()
                                                }
                                                if let vc = pageViewController.currentController as? HomeListenFight_lesson_2_VC{
                                                                vc.restore()
                                                }
                                                if let vc = pageViewController.currentController as? HomeListenFight_lesson_3_VC{
                                                                vc.restore()
                                                }
                                                if let vc = pageViewController.currentController as? HomeListenFight_lesson_4_VC{
                                                                vc.restore()
                                                }
                                                if let vc = pageViewController.currentController as? HomeListenFight_lesson_5_VC{
                                                                vc.restore()
                                                }
        if listenFightLine == .before{
            restore()
        }
    }
                                                if let vc = pageViewController.currentController as? HomeListenStory_1_VC{
                                                                vc.restore()
                                                }
                                                if let vc = pageViewController.currentController as? HomeListenStory_2_VC{
                                                                vc.restore()
                                                }
                                }
                }
    func pageViewController(_ pageViewConteoller: FFPageViewController, controllerForPage page: Int) -> UIViewController {
        if viewModel.listenType.value == .lesson1{
            let vc = HomeListenFight_lesson_1_VC(page: page,listenNewModel:data as! ListenNewModel)
            vc.teamScheduleModel = teamScheduleModel
            vc.rootViewModel = viewModel
            vc.handleClouseAction {[unowned self] in
                self.setPages()
                self.collection_card.reloadData()
            }
            return vc
        }
                func pageViewController(_ pageViewConteoller: FFPageViewController, controllerForPage page: Int) -> UIViewController {
                                if viewModel.listenType.value == .lesson1{
                                                let vc = HomeListenFight_lesson_1_VC(page: page,listenNewModel:data as! ListenNewModel)
                                                vc.teamScheduleModel = teamScheduleModel
                                                vc.rootViewModel = viewModel
                                                return vc
                                }
        if viewModel.listenType.value == .lesson2{
            let vc = HomeListenFight_lesson_2_VC(page: page,listenNewModel:data as! ListenNewModel)
            vc.teamScheduleModel = teamScheduleModel
            vc.rootViewModel = viewModel
            vc.handleClouseAction {[unowned self] in
                self.setPages()
                self.collection_card.reloadData()
            }
            return vc
        }
                                if viewModel.listenType.value == .lesson2{
                                                let vc = HomeListenFight_lesson_2_VC(page: page,listenNewModel:data as! ListenNewModel)
                                                vc.teamScheduleModel = teamScheduleModel
                                                vc.rootViewModel = viewModel
                                                return vc
                                }
        if viewModel.listenType.value == .lesson3{
            let vc = HomeListenFight_lesson_3_VC(page: page, listenNewModel: data as! ListenNewModel)
            vc.teamScheduleModel = teamScheduleModel
            vc.rootViewModel = viewModel
            vc.handleClouseAction {[unowned self] in
                self.setPages()
                self.collection_card.reloadData()
            }
            return vc
        }
                                if viewModel.listenType.value == .lesson3{
                                                let vc = HomeListenFight_lesson_3_VC(page: page, listenNewModel: data as! ListenNewModel)
                                                vc.teamScheduleModel = teamScheduleModel
                                                vc.rootViewModel = viewModel
                                                return vc
                                }
        if viewModel.listenType.value == .lesson4{
            let vc = HomeListenFight_lesson_4_VC(page: page, listenNewModel: data as! ListenNewModel)
            vc.teamScheduleModel = teamScheduleModel
            vc.rootViewModel = viewModel
            vc.handleClouseAction {[unowned self] in
                self.setPages()
                self.collection_card.reloadData()
            }
            return vc
        }
                                if viewModel.listenType.value == .lesson4{
                                                let vc = HomeListenFight_lesson_4_VC(page: page, listenNewModel: data as! ListenNewModel)
                                                vc.teamScheduleModel = teamScheduleModel
                                                vc.rootViewModel = viewModel
                                                return vc
                                }
        if viewModel.listenType.value == .lesson5{
            let vc = HomeListenFight_lesson_5_VC(page: page, listenNewModel: data as! ListenNewModel)
            vc.teamScheduleModel = teamScheduleModel
            vc.rootViewModel = viewModel
            vc.handleClouseAction {[unowned self] in
                self.setPages()
                self.collection_card.reloadData()
            }
            return vc
        }
                                if viewModel.listenType.value == .lesson5{
                                                let vc = HomeListenFight_lesson_5_VC(page: page, listenNewModel: data as! ListenNewModel)
                                                vc.teamScheduleModel = teamScheduleModel
                                                vc.rootViewModel = viewModel
                                                return vc
                                }
        if viewModel.listenType.value == .game1{
            if data == nil{return UIViewController()}
            let vc = HomeListenGame_1_VC(listen1Model: data as! Listen1Model)
            vc.rootViewModel = viewModel
            return vc
        }
                                if viewModel.listenType.value == .game1{
                                                if data == nil{return UIViewController()}
                                                let vc = HomeListenGame_1_VC(listen1Model: data as! Listen1Model)
                                                vc.rootViewModel = viewModel
                                                return vc
                                }
        if viewModel.listenType.value == .game2{
            let vc = HomeListenGame_2_VC(listen1Model: data as! Listen1Model)
            vc.rootViewModel = viewModel
            return vc
        }
                                if viewModel.listenType.value == .game2{
                                                let vc = HomeListenGame_2_VC(listen1Model: data as! Listen1Model)
                                                vc.rootViewModel = viewModel
                                                return vc
                                }
        if viewModel.listenType.value == .story1{
            let vc = HomeListenStory_1_VC(page: page, listen1Model: data as! Listen1Model)
            vc.rootViewModel = viewModel
            return vc
        }
                                if viewModel.listenType.value == .story1{
                                                let vc = HomeListenStory_1_VC(page: page, listen1Model: data as! Listen1Model)
                                                vc.rootViewModel = viewModel
                                                return vc
                                }
        if viewModel.listenType.value == .story2{
            let vc = HomeListenStory_2_VC(page: page, listen1Model: data as! Listen1Model)
            vc.rootViewModel = viewModel
            return vc
        }
                                if viewModel.listenType.value == .story2{
                                                let vc = HomeListenStory_2_VC(page: page, listen1Model: data as! Listen1Model)
                                                vc.rootViewModel = viewModel
                                                return vc
                                }
                                let vc = UIViewController()
                                return vc
                }
        let vc = UIViewController()
        return vc
    }
}
extension HomeListenFightVC:UICollectionViewDelegate{
    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        if let m = data as? ListenNewModel{
            guard m.list[indexPath.row].status != 1 else{return}
            guard pageVC.currentPage != indexPath.row else {return}
            pageVC.scroll(toPage: indexPath.row, animation: true)
            //todo
        }
    }
}
extension HomeListenFightVC:UICollectionViewDataSource{
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        if let m = data as? ListenNewModel{
            if viewModel.listenType.value == .lesson1{
                return pages.count
            }
            return pages.count
        }
        return 0
    }
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "_CardItemCCell", for: indexPath) as! CardItemCCell
        if let m = data as? ListenNewModel{
            let model = pages[indexPath.row]
            cell.titleL.text = "\(indexPath.row + 1)"
            if viewModel.listenType.value == .lesson1{
                if model.filter({$0.status == 2}).count == 4{
                    cell.titleL.textColor = UIColor(hexString: "#52C41A")
                    cell.titleL.jq_borderColor = UIColor(hexString: "#52C41A")
                }else if model.filter({$0.status == 3}).count >= 1 {
                    cell.titleL.textColor = UIColor(hexString: "#FF4D4F")
                    cell.titleL.jq_borderColor = UIColor(hexString: "#52C41A")
                }else{
                    cell.titleL.textColor = .black.withAlphaComponent(0.25)
                    cell.titleL.jq_borderColor = .black.withAlphaComponent(0.25)
                }
            }else{
                //状态1灰色未答题 2绿色正确 3红色错误
                switch model.first!.status{
                case 1:
                    cell.titleL.textColor = .black.withAlphaComponent(0.25)
                    cell.titleL.jq_borderColor = .black.withAlphaComponent(0.25)
                case 2:
                    cell.titleL.textColor = UIColor(hexString: "#52C41A")
                    cell.titleL.jq_borderColor = UIColor(hexString: "#52C41A")
                case 3:
                    cell.titleL.textColor = UIColor(hexString: "#FF4D4F")
                    cell.titleL.jq_borderColor = UIColor(hexString: "#52C41A")
                default:break
                }
            }
        }
        return cell
    }
}
class CardItemCCell:UICollectionViewCell{
    var titleL:UILabel!
    override init(frame: CGRect) {
        super.init(frame: frame)
        titleL = UILabel()
        titleL.text = "1"
        titleL.backgroundColor = .white
        titleL.jq_borderColor = .black.withAlphaComponent(0.15)
        titleL.jq_borderWidth = 0.5
        titleL.font = .systemFont(ofSize: 13,weight: .semibold)
        titleL.textAlignment = .center
        contentView.addSubview(titleL)
        titleL.snp.makeConstraints { make in
            make.edges.equalToSuperview()
        }
    }
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}
DolphinEnglishLearnStudent/Moudle/Home/Listen/VC/HomeListenFight_lesson_2_VC.swift
@@ -9,383 +9,435 @@
import QMUIKit
class HomeListenFight_lesson_2_VC: BaseVC {
                private var viewModel = FightAnswerViewModel()
                private var listenNewModel:ListenNewModel!
                private var page:Int!
                var rootViewModel:HomeListenFightViewModel!
                var teamScheduleModel:TeamScheduleModel?
                private var tempViews = [StudyHandleView]()
                private var playedIndex = Set<Int>() //已经播放过的view
                private var voicePlayer = VoicePlayer.share()
                private var isAnsterModel = Set<Listen1SubModel>()
    private var viewModel = FightAnswerViewModel()
    private var listenNewModel:ListenNewModel!
    private var page:Int!
    var rootViewModel:HomeListenFightViewModel!
    var teamScheduleModel:TeamScheduleModel?
    private var tempViews = [StudyHandleView]()
    private var playedIndex = Set<Int>() //已经播放过的view
    private var voicePlayer = VoicePlayer.share()
    private var isAnsterModel = Set<Listen1SubModel>()
    private var isOpen:Bool = false //是否展示标题文本
                private lazy var stackView:UIStackView = {
                                let stackView = UIStackView()
                                stackView.spacing = 78
                                stackView.distribution = .equalSpacing
                                return stackView
                }()
                private lazy var collectionView:UICollectionView = {
                                let flowLayout = UICollectionViewFlowLayout()
                                let w = (JQ_ScreenW - 194 * 2 - 25) / 2
                                flowLayout.itemSize = CGSize(width: w, height: w * 0.745)
                                flowLayout.minimumLineSpacing = 25
                                flowLayout.minimumInteritemSpacing = 25
                                flowLayout.scrollDirection = .vertical
                                let collection = UICollectionView(frame: .zero, collectionViewLayout: flowLayout)
                                collection.register(UINib(nibName: "ListenFight_lesson_1_CCell", bundle: nil), forCellWithReuseIdentifier: "_ListenFight_lesson_1_CCell")
                                collection.isScrollEnabled = false
                                return collection
                }()
                required init(page:Int,listenNewModel:ListenNewModel){
                                super.init(nibName: nil, bundle: nil)
                                self.page = page
                                self.listenNewModel = listenNewModel
                }
                required init?(coder: NSCoder) {
                                fatalError("init(coder:) has not been implemented")
                }
                override func viewDidLoad() {
                                super.viewDidLoad()
                                navigationItem.titleView = UIView()
    private var studieds = 0
    private var handleClouse:(()->Void)?
    private lazy var stackView:UIStackView = {
        let stackView = UIStackView()
        stackView.spacing = 78
        stackView.distribution = .equalSpacing
        return stackView
    }()
    private lazy var collectionView:UICollectionView = {
        let flowLayout = UICollectionViewFlowLayout()
        let w = (JQ_ScreenW - 194 * 2 - 25) / 2
        flowLayout.itemSize = CGSize(width: w, height: w * 0.745)
        flowLayout.minimumLineSpacing = 25
        flowLayout.minimumInteritemSpacing = 25
        flowLayout.scrollDirection = .vertical
        let collection = UICollectionView(frame: .zero, collectionViewLayout: flowLayout)
        collection.register(UINib(nibName: "ListenFight_lesson_1_CCell", bundle: nil), forCellWithReuseIdentifier: "_ListenFight_lesson_1_CCell")
        collection.isScrollEnabled = false
        return collection
    }()
    required init(page:Int,listenNewModel:ListenNewModel){
        super.init(nibName: nil, bundle: nil)
        self.page = page
        self.listenNewModel = listenNewModel
    }
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    override func viewDidLoad() {
        super.viewDidLoad()
        navigationItem.titleView = UIView()
        Services.getIsOpen().subscribe(onNext: {data in
            self.isOpen = data.data ?? false
            self.collectionView.reloadData()
        }).disposed(by: disposeBag)
                                //回传记录,始终保持答题进度
                                if let team = teamScheduleModel{
                                                for teamId in team.teamIds{
                                                                for v in listenNewModel.subjectList[page]{
                                                                                if team.topicIds.contains(v.id){
                                                                                                rootViewModel.insertCorrectAnswer(teamId: "\(teamId)", answerId: v.id)
                                                                                }
                                                                }
                                                }
                                }
                }
                override func viewDidAppear(_ animated: Bool) {
                                super.viewDidAppear(animated)
                                VoicePlayer.share().delegate = self
                }
                override func viewDidDisappear(_ animated: Bool) {
                                super.viewDidDisappear(animated)
                                VoicePlayer.share().delegate = nil
                                VoicePlayer.share().playerInterrupt()
                }
                func restore(){
                                playedIndex.removeAll()
                                tempViews.removeAll()
                                for subView in view.subviews{
                                                if subView is StudyHandleView{
                                                                subView.removeFromSuperview()
                                                }
                                }
                                setUI()
                                collectionView.reloadData()
                }
                override func viewDidLayoutSubviews() {
                                super.viewDidLayoutSubviews()
                                let flowLayout = self.collectionView.collectionViewLayout as! UICollectionViewFlowLayout
                                let w = (self.collectionView.size.width - flowLayout.minimumLineSpacing) / 2
                                let h = (self.collectionView.size.height - flowLayout.minimumInteritemSpacing) / 2
                                if flowLayout.itemSize.width != w || flowLayout.itemSize.height != h{
                                                flowLayout.itemSize = CGSize(width: w, height: h)
                                                collectionView.reloadData()
                                }
                }
                override func setUI() {
                                super.setUI()
                                viewModel.selectIndex.accept(IndexPath(row: 0, section: 0))
                                if !view.subviews.contains(collectionView){
                                                collectionView.delegate = self
                                                collectionView.dataSource = self
                                                collectionView.backgroundColor = UIColor(hexStr: "#C3BFB3")
                                                view.addSubview(collectionView)
                                }
                                collectionView.snp.makeConstraints { make in
                                                make.top.equalTo(self.view.safeAreaLayoutGuide.snp.top).offset(101)
                                                make.left.equalTo(194)
                                                make.right.equalTo(-194)
                                                make.bottom.equalToSuperview()
                                }
                                if !view.subviews.contains(stackView){
                                                view.addSubview(stackView)
                                }
                                stackView.snp.makeConstraints { make in
                                                make.top.equalTo(self.view.safeAreaLayoutGuide.snp.top).offset(24)
                                                make.centerX.equalToSuperview()
                                                make.height.equalTo(52)
                                                make.width.greaterThanOrEqualTo(100)
                                }
                                addStackView()
                }
                private func addStackView(){
                                for subV in stackView.arrangedSubviews{
                                                subV.removeFromSuperview()
                                }
                                tempViews.removeAll()
                                for index in 0...2{
                                                let handleView = StudyHandleView.jq_loadNibView()
                                                handleView.listenType = .lesson2
                                                handleView.view_choose.alpha = 0
                                                handleView.btn_choose.tag = 10 + index
                                                handleView.tag = 20 + index
                                                let row = viewModel.selectIndex.value!.row
                                                let model = listenNewModel.subjectList[page][row]
                                                if index == 0{
                                                                handleView.vioceSoundUrl = model.correct
                                                }
                                                if index == 1{
                                                                handleView.vioceSoundUrl = model.error.components(separatedBy: ",").first
                                                }
                                                if index == 2{
                                                                handleView.vioceSoundUrl = model.error.components(separatedBy: ",").last
                                                }
                                                handleView.playAt {[weak self] index in
                                                                self?.playedIndex.insert(index)
                                                }
                                                handleView.chooseClouse {[weak self] btn in
                                                                guard let weakSelf = self else { return }
                                                                if weakSelf.playedIndex.count != 3{
                                                                                handleView.btn_choose.isSelected = false
                                                                                alertError(msg: "请听完");return
                                                                }
                                                                handleView.view_choose.alpha = 1
                                                                var lessionType:Fight_lessonType = .none
                                                                if handleView.vioceSoundUrl == weakSelf.listenNewModel.subjectList[weakSelf.page][row].correct{
                                                                                lessionType = .success
                                                                                weakSelf.voicePlayer.playSuccessVoice()
                                                                                let teamId = weakSelf.listenNewModel.data?.id.components(separatedBy: ",")[weakSelf.page]
                                                                                let answerId = weakSelf.listenNewModel.subjectList[weakSelf.page][row].id
                                                                                weakSelf.rootViewModel.insertCorrectAnswer(teamId: teamId, answerId: answerId)
                                                                }else{
                                                                                lessionType = .fail
                                                                                // 重置按钮至最初样式
                                                                                weakSelf.playedIndex.removeAll()
                                                                                for sub in weakSelf.stackView.arrangedSubviews as! [StudyHandleView]{
                                                                                                sub.btn_pay.isEnabled = true
                                                                                                sub.btn_choose.isEnabled = true
                                                                                                sub.resetView()
                                                                                                sub.view_choose.alpha = 0
                                                                                }
                                                                                weakSelf.voicePlayer.playFailVoice()
                                                                }
                                                                switch lessionType {
                                                                                case .success:
                                                                                                weakSelf.rootViewModel.correctNum += 1
                                                                                                handleView.btn_choose.isEnabled = false
                                                                                                handleView.btn_state.setImage(UIImage(named: "icon_success_small"), for: .normal)
                                                                                                UIView.animate(withDuration: 0.5) {
                                                                                                                handleView.btn_state.alpha = 1
                                                                                                }
                                                                                                let i = ceil(handleView.x / 300).int
                                                                                                if let cell = self?.collectionView.dequeueReusableCell(withReuseIdentifier: "_ListenFight_lesson_1_CCell", for: self!.viewModel.selectIndex.value!) as? ListenFight_lesson_1_CCell{
                                                                                                                self?.viewModel.answerType.accept(.success)
                                                                                                                self?.answerSuccess(cell,index: i)
                                                                                                }
                                                                                case .fail:
                                                                                                weakSelf.rootViewModel.errorNum += 1
                                                                                                handleView.btn_state.setImage(UIImage(named: "icon_waring_small"), for: .normal)
                                                                                                UIView.animate(withDuration: 0.5) {
                                                                                                                handleView.btn_state.alpha = 1
                                                                                                }
                                                                                                UIView.animate(withDuration: 0.5, delay: 1.2, options: .layoutSubviews) {
                                                                                                                handleView.btn_pay.alpha = 1
                                                                                                                handleView.btn_voice.alpha = 1
                                                                                                                handleView.btn_state.alpha = 0
                                                                                                } completion: { _ in
                                                                                                                handleView.btn_choose.isSelected = false
                                                                                                }
                                                                                default:
                                                                                                handleView.btn_state.setImage(nil, for: .normal)
                                                                }
                                                }
                                                handleView.snp.makeConstraints { make in
                                                                make.height.equalTo(52)
                                                                make.width.greaterThanOrEqualTo(221)
                                                }
                                                tempViews.append(handleView)
                                }
                                tempViews.shuffle() //乱序
                                stackView.addArrangedSubviews(tempViews)
                }
                private func answerSuccess(_ cell:ListenFight_lesson_1_CCell,index:Int){
                                let studyHandleView = stackView.arrangedSubviews[index] as! StudyHandleView
                                let bounds = studyHandleView.convert(studyHandleView.bounds, to: self.view)
                                //copy试图放在上面进行覆盖
                                let copyHandleView = studyHandleView.copyView()
                                copyHandleView.view_choose.isHidden = true
                                copyHandleView.listenType = .lesson2
                                copyHandleView.isplaying()
                                copyHandleView.vioceSoundUrl = listenNewModel.subjectList[page][self.viewModel.selectIndex.value!.row].correct
                                copyHandleView.jq_cornerRadius = 0
                                view.addSubview(copyHandleView)
                                view.layoutIfNeeded()
                                copyHandleView.snp.makeConstraints { make in
                                                make.top.equalToSuperview().offset(bounds.origin.y)
                                                make.left.equalToSuperview().offset(bounds.origin.x)
                                                make.width.equalTo(159)
                                                make.height.equalTo(52)
                                }
                                view.layoutIfNeeded()
                                let v = cell.view_topHandle.convert(cell.bounds, to: self.view)
                                UIView.animate(withDuration: 0.6) {
                                                copyHandleView.snp.updateConstraints { make in
                                                                make.top.equalTo(self.view).offset(v.origin.y + UIDevice.jq_safeEdges.top + 101 + 50)
                                                                make.left.equalToSuperview().offset(v.origin.x + 194)
                                                                make.width.equalTo(v.size.width - 10)
                                                                make.height.equalTo(40)
                                                }
                                                self.view.layoutIfNeeded()
                                }completion: { complete in
                                                if complete{
                                                                self.voicePlayer.playerAt(url: self.listenNewModel.subjectList[self.page][self.viewModel.selectIndex.value!.row].correct)
                                                }
                                }
                }
                private func resetStackView(){
                                playedIndex.removeAll()
                                let newRow = viewModel.selectIndex.value!.row+1
                                if newRow >= listenNewModel.subjectList[page].count{ //防止坐标越界
                                                NotificationCenter.default.post(name: NextLession_Noti, object: nil);return
                                }
                                collectionView.reloadData()
                                viewModel.selectIndex.accept(IndexPath(row: newRow, section: 0))
                                addStackView()
                }
                override func setRx() {}
        //回传记录,始终保持答题进度
        if let team = teamScheduleModel{
            for teamId in team.teamIds{
                for v in listenNewModel.subjectList[page]{
                    if team.topicIds.contains(v.id){
                        rootViewModel.insertCorrectAnswer(teamId: "\(teamId)", answerId: v.id)
                    }
                }
            }
        }
    }
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        VoicePlayer.share().delegate = self
    }
    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        VoicePlayer.share().delegate = nil
        VoicePlayer.share().playerInterrupt()
    }
    func restore(){
        playedIndex.removeAll()
        tempViews.removeAll()
        for subView in view.subviews{
            if subView is StudyHandleView{
                subView.removeFromSuperview()
            }
        }
        setUI()
        collectionView.reloadData()
    }
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        let flowLayout = self.collectionView.collectionViewLayout as! UICollectionViewFlowLayout
        let w = (self.collectionView.size.width - flowLayout.minimumLineSpacing) / 2
        let h = (self.collectionView.size.height - flowLayout.minimumInteritemSpacing) / 2
        if flowLayout.itemSize.width != w || flowLayout.itemSize.height != h{
            flowLayout.itemSize = CGSize(width: w, height: h)
            collectionView.reloadData()
        }
    }
    override func setUI() {
        super.setUI()
        viewModel.selectIndex.accept(IndexPath(row: 0, section: 0))
        if !view.subviews.contains(collectionView){
            collectionView.delegate = self
            collectionView.dataSource = self
            collectionView.backgroundColor = UIColor(hexStr: "#C3BFB3")
            view.addSubview(collectionView)
        }
        collectionView.snp.makeConstraints { make in
            make.top.equalTo(self.view.safeAreaLayoutGuide.snp.top).offset(101)
            make.left.equalTo(194)
            make.right.equalTo(-194)
            make.bottom.equalToSuperview()
        }
        if !view.subviews.contains(stackView){
            view.addSubview(stackView)
        }
        stackView.snp.makeConstraints { make in
            make.top.equalTo(self.view.safeAreaLayoutGuide.snp.top).offset(24)
            make.centerX.equalToSuperview()
            make.height.equalTo(40)
            make.width.greaterThanOrEqualTo(125)
        }
        addStackView()
    }
    private func addStackView(){
        for subV in stackView.arrangedSubviews{
            subV.removeFromSuperview()
        }
        tempViews.removeAll()
        for index in 0...2{
            let handleView = StudyHandleView.jq_loadNibView()
            handleView.listenType = .lesson2
            handleView.view_choose.alpha = 0
            handleView.btn_choose.tag = 10 + index
            handleView.tag = 20 + index
            let row = viewModel.selectIndex.value!.row
            let model = listenNewModel.subjectList[page][row]
            if index == 0{
                handleView.vioceSoundUrl = model.correct
            }
            if index == 1{
                handleView.vioceSoundUrl = model.error.components(separatedBy: ",").first
            }
            if index == 2{
                handleView.vioceSoundUrl = model.error.components(separatedBy: ",").last
            }
            handleView.playAt {[weak self] index in
                self?.playedIndex.insert(index)
            }
            handleView.chooseClouse {[weak self] btn in
                guard let weakSelf = self else { return }
                if weakSelf.playedIndex.count < 3{
                    handleView.btn_choose.isSelected = false
                    alertError(msg: "请听完");return
                }
                handleView.view_choose.alpha = 1
                var lessionType:Fight_lessonType = .none
                if handleView.vioceSoundUrl == weakSelf.listenNewModel.subjectList[weakSelf.page][row].correct{
                    lessionType = .success
                    weakSelf.voicePlayer.playSuccessVoice()
                    let teamId = weakSelf.listenNewModel.data?.id.components(separatedBy: ",")[weakSelf.page]
                    let answerId = weakSelf.listenNewModel.subjectList[weakSelf.page][row].id
                    weakSelf.rootViewModel.insertCorrectAnswer(teamId: teamId, answerId: answerId)
                }else{
                    lessionType = .fail
                    // 重置按钮至最初样式
                    weakSelf.playedIndex.removeAll()
                    for sub in weakSelf.stackView.arrangedSubviews as! [StudyHandleView]{
                        sub.btn_pay.isEnabled = true
                        sub.btn_choose.isEnabled = true
                        sub.resetView()
                        sub.view_choose.alpha = 0
                    }
                    weakSelf.voicePlayer.playFailVoice()
                }
                switch lessionType {
                case .success:
                    weakSelf.rootViewModel.correctNum += 1
                    handleView.btn_choose.isEnabled = false
                    handleView.btn_state.setImage(UIImage(named: "icon_success_small"), for: .normal)
                    UIView.animate(withDuration: 0.5) {
                        handleView.btn_state.alpha = 1
                    }
                    let i = ceil(handleView.x / 300).int
                    if let cell = self?.collectionView.cellForItem(at: self!.viewModel.selectIndex.value!) as? ListenFight_lesson_1_CCell{
                        self?.viewModel.answerType.accept(.success)
                        self?.answerSuccess(cell,index: i)
                        if let data = self?.listenNewModel.list[self!.page]{
                            Services.answerQuestion(id: data.id, status: 2).subscribe(onNext: {_ in
                                self?.handleClouse?()
                            }).disposed(by: weakSelf.disposeBag)
                        }
                    }
                case .fail:
                    weakSelf.rootViewModel.errorNum += 1
                    handleView.btn_state.setImage(UIImage(named: "icon_waring_small"), for: .normal)
                    UIView.animate(withDuration: 0.5) {
                        handleView.btn_state.alpha = 1
                    }
                    UIView.animate(withDuration: 0.5, delay: 1.2, options: .layoutSubviews) {
                        handleView.btn_pay.alpha = 1
                        handleView.btn_voice.alpha = 1
                        handleView.btn_state.alpha = 0
                    } completion: { _ in
                        handleView.btn_choose.isSelected = false
                    }
                    if let data = self?.listenNewModel.list[self!.page]{
                        Services.answerQuestion(id: data.id, status: 3).subscribe(onNext: {_ in
                            self?.handleClouse?()
                        }).disposed(by: weakSelf.disposeBag)
                    }
                default:
                    handleView.btn_state.setImage(nil, for: .normal)
                }
            }
            handleView.snp.makeConstraints { make in
                make.height.equalTo(52)
                make.width.greaterThanOrEqualTo(221)
            }
            tempViews.append(handleView)
        }
        tempViews.shuffle() //乱序
        stackView.addArrangedSubviews(tempViews)
        DispatchQueue.main.asyncAfter(deadline: .now()+1) {
            let temp = self.tempViews.first
            temp?.isplaying()
            self.voicePlayer.playerAt(url: temp?.vioceSoundUrl ?? "")
            self.playedIndex.insert(0)
            self.collectionView.reloadData()
        }
    }
    private func answerSuccess(_ cell:ListenFight_lesson_1_CCell,index:Int){
        studieds += 1
        let studyHandleView = stackView.arrangedSubviews[index] as! StudyHandleView
        let bounds = studyHandleView.convert(studyHandleView.bounds, to: self.view)
        //copy试图放在上面进行覆盖
        let copyHandleView = studyHandleView.copyView()
        copyHandleView.view_choose.isHidden = true
        copyHandleView.listenType = .lesson2
        copyHandleView.isplaying()
        copyHandleView.vioceSoundUrl = listenNewModel.subjectList[page][self.viewModel.selectIndex.value!.row].correct
        copyHandleView.jq_cornerRadius = 0
        view.addSubview(copyHandleView)
        view.layoutIfNeeded()
        copyHandleView.snp.makeConstraints { make in
            make.top.equalToSuperview().offset(bounds.origin.y)
            make.left.equalToSuperview().offset(bounds.origin.x)
            make.width.equalTo(159)
            make.height.equalTo(52)
        }
        view.layoutIfNeeded()
        let v = cell.view_topHandle.convert(cell.bounds, to: self.view)
        UIView.animate(withDuration: 0.6) {
            copyHandleView.snp.updateConstraints { make in
                make.top.equalTo(self.view).offset(v.origin.y)
                make.left.equalToSuperview().offset(v.origin.x)
                make.width.equalTo(v.size.width - 10)
                make.height.equalTo(40)
            }
            self.view.layoutIfNeeded()
        }completion: { complete in
            if complete{
                self.voicePlayer.playerAt(url: self.listenNewModel.subjectList[self.page][self.viewModel.selectIndex.value!.row].correct)
            }
        }
    }
    private func resetStackView(){
        playedIndex.removeAll()
        let newRow = viewModel.selectIndex.value!.row+1
        if newRow >= listenNewModel.subjectList[page].count{ //防止坐标越界
            NotificationCenter.default.post(name: NextLession_Noti, object: nil);return
        }
        collectionView.reloadData()
        viewModel.selectIndex.accept(IndexPath(row: newRow, section: 0))
        addStackView()
    }
    override func setRx() {
        NotificationCenter.default.rx.notification(ResetLession_Noti).take(until: self.rx.deallocated).subscribe(onNext: {[weak self]_ in
            self?.restore()
            self?.viewDidLoad()
        }).disposed(by: disposeBag)
    }
    func handleClouseAction(clouse:@escaping ()->Void){
        self.handleClouse = clouse
    }
}
extension HomeListenFight_lesson_2_VC:UICollectionViewDelegate{
                func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
                                viewModel.selectIndex.accept(indexPath)
                }
    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        viewModel.selectIndex.accept(indexPath)
    }
}
extension HomeListenFight_lesson_2_VC:UICollectionViewDataSource{
                func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
                                let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "_ListenFight_lesson_1_CCell", for: indexPath) as! ListenFight_lesson_1_CCell
                                cell.jq_addShadows(shadowColor: .black.withAlphaComponent(0.31), corner: 5, radius: 5, offset: CGSize(width: 0, height: 1), opacity: 1)
                                cell.backgroundColor = .white
            if viewModel.selectIndex.value?.row == indexPath.row && isOpen{
                cell.label_title.isHidden = false
            }else{
                cell.label_title.isHidden = true
            }
                                cell.setListen1SubModel(listenNewModel.subjectList[page][indexPath.row])
                                cell.label_title.text = listenNewModel.subjectList[page][indexPath.row].name
                                return cell
                }
                func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
                                return listenNewModel.subjectList[page].count
                }
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "_ListenFight_lesson_1_CCell", for: indexPath) as! ListenFight_lesson_1_CCell
        cell.jq_addShadows(shadowColor: .black.withAlphaComponent(0.31), corner: 5, radius: 5, offset: CGSize(width: 0, height: 1), opacity: 1)
        if indexPath.row == studieds{
            cell.backgroundColor = .orange
        }else{
            cell.backgroundColor = .white
        }
        //            if viewModel.selectIndex.value?.row == indexPath.row && isOpen{
        //                cell.label_title.isHidden = false
        //            }else{
        cell.label_title.isHidden = true
        //            }
        cell.setListen1SubModel(listenNewModel.subjectList[page][indexPath.row])
        cell.label_title.text = listenNewModel.subjectList[page][indexPath.row].name
        return cell
    }
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return listenNewModel.subjectList[page].count
    }
    func numberOfSections(in collectionView: UICollectionView) -> Int {
        return 1
    }
}
extension HomeListenFight_lesson_2_VC:VoicePlayerDelegate{
                func playing() {
                                view.isUserInteractionEnabled = false
                                print("正在播放")
                                //正在播放中,其他播放按钮先禁止
                                for sub in stackView.arrangedSubviews as! [StudyHandleView]{
                                                sub.btn_pay.isEnabled = false
                                                sub.btn_choose.isEnabled = false
                                }
                }
                func playComplete() {
                                view.isUserInteractionEnabled = true
                                //对已经播放过的View,进行刷新
                                for sub in stackView.arrangedSubviews as! [StudyHandleView]{
                                                sub.btn_pay.isEnabled = true
                                                sub.btn_choose.isEnabled = true
                                                if playedIndex.contains(sub.tag){
                                                                sub.resetView()
                                                                sub.view_choose.alpha = playedIndex.contains(sub.tag) ? 1:0
                                                }
                                }
                                for sub in view.subviews{
                                                if let v = sub as? StudyHandleView{
                                                                v.resetView()
                                                }
                                }
                                if viewModel.answerType.value == .success{
                                                let v = rootViewModel.answerCount.value
                                                rootViewModel.answerCount.accept(v + 1)
                                                viewModel.answerType.accept(.none)
                                                for sub in stackView.arrangedSubviews as! [StudyHandleView]{
                                                                sub.btn_pay.isEnabled = false
                                                                sub.btn_choose.isEnabled = false
                                                }
                                                DispatchQueue.main.asyncAfter(deadline: .now()+2) {
                                                                self.resetStackView()
                                                }
                                }
                }
    func playing() {
        view.isUserInteractionEnabled = false
        print("正在播放")
        //正在播放中,其他播放按钮先禁止
        for sub in stackView.arrangedSubviews as! [StudyHandleView]{
            sub.btn_pay.isEnabled = false
            sub.btn_choose.isEnabled = false
        }
    }
    func playComplete() {
        collectionView.reloadData()
        view.isUserInteractionEnabled = true
        //对已经播放过的View,进行刷新
        for sub in stackView.arrangedSubviews as! [StudyHandleView]{
            sub.btn_pay.isEnabled = true
            sub.btn_choose.isEnabled = true
            if playedIndex.contains(sub.tag){
                sub.resetView()
                sub.view_choose.alpha = playedIndex.contains(sub.tag) ? 1:0
            }
        }
        for sub in view.subviews{
            if let v = sub as? StudyHandleView{
                v.resetView()
            }
        }
        for v in tempViews{
            if v.isplayend{
                v.resetView()
            }
        }
        for (i,v) in tempViews.enumerated(){
            if v.isplayend == false{
                self.playedIndex.insert(i)
                v.isplaying()
                voicePlayer.playerAt(url: v.vioceSoundUrl)
                break
            }
        }
        if viewModel.answerType.value == .success{
            let v = rootViewModel.answerCount.value
            rootViewModel.answerCount.accept(v + 1)
            viewModel.answerType.accept(.none)
            for sub in stackView.arrangedSubviews as! [StudyHandleView]{
                sub.btn_pay.isEnabled = false
                sub.btn_choose.isEnabled = false
            }
            DispatchQueue.main.asyncAfter(deadline: .now()+2) {
                self.resetStackView()
            }
        }
    }
}
DolphinEnglishLearnStudent/Moudle/Home/Listen/VC/HomeListenFight_lesson_3_VC.swift
@@ -10,482 +10,530 @@
class HomeListenFight_lesson_3_VC: BaseVC {
                private var viewModel = FightAnswerViewModel()
    private var viewModel = FightAnswerViewModel()
                private lazy var collectionView:UICollectionView = {
                                let flowLayout = UICollectionViewFlowLayout()
                                let w = (JQ_ScreenW - 97 - 54 - 36) / 3.0
                                flowLayout.itemSize = CGSize(width: w, height: w * 0.947)
                                flowLayout.minimumInteritemSpacing = 18
                                flowLayout.scrollDirection = .vertical
                                let collection = UICollectionView(frame: .zero, collectionViewLayout: flowLayout)
                                collection.register(UINib(nibName: "ListenFight_lesson_3_CCell", bundle: nil), forCellWithReuseIdentifier: "_ListenFight_lesson_3_CCell")
                                collection.register(UICollectionReusableView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "header")
                                collection.isScrollEnabled = false
                                return collection
                }()
    private lazy var collectionView:UICollectionView = {
        let flowLayout = UICollectionViewFlowLayout()
        let w = (JQ_ScreenW - 97 - 54 - 36) / 3.0
        flowLayout.itemSize = CGSize(width: w, height: w * 0.947)
        flowLayout.minimumInteritemSpacing = 18
        flowLayout.scrollDirection = .vertical
        let collection = UICollectionView(frame: .zero, collectionViewLayout: flowLayout)
        collection.register(UINib(nibName: "ListenFight_lesson_3_CCell", bundle: nil), forCellWithReuseIdentifier: "_ListenFight_lesson_3_CCell")
        collection.register(UICollectionReusableView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "header")
        collection.isScrollEnabled = false
        return collection
    }()
                private lazy var stackView:UIStackView = {
                                let sta = UIStackView()
                                sta.spacing = 5
                                sta.distribution = .equalSpacing
                                sta.axis = .horizontal
                                return sta
                }()
    private lazy var stackView:UIStackView = {
        let sta = UIStackView()
        sta.spacing = 5
        sta.distribution = .equalSpacing
        sta.axis = .horizontal
        return sta
    }()
                private var listenNewModel:ListenNewModel!
                private var page:Int!
                private var answterCount:Int = 0 //回答计数,用于确定角标
                var rootViewModel:HomeListenFightViewModel!
                var teamScheduleModel:TeamScheduleModel?
                private var voicePlayer = VoicePlayer.share()
                private var playIndex = Set<IndexPath>() //顺序播放
                private var isPlayingIndex:IndexPath? //正在播放中
                private var islisten:Bool = false
    private var listenNewModel:ListenNewModel!
    private var page:Int!
    private var answterCount:Int = 0 //回答计数,用于确定角标
    var rootViewModel:HomeListenFightViewModel!
    var teamScheduleModel:TeamScheduleModel?
    private var voicePlayer = VoicePlayer.share()
    private var playIndex = Set<IndexPath>() //顺序播放
    private var currentPlayIndex = IndexPath(row: 0, section: 0)
    private var isPlayingIndex:IndexPath? //正在播放中
    private var islisten:Bool = false
    private var handleClouse:(()->Void)?
                required init(page:Int,listenNewModel:ListenNewModel){
                                super.init(nibName: nil, bundle: nil)
                                self.page = page
                                self.listenNewModel = listenNewModel
                }
    required init(page:Int,listenNewModel:ListenNewModel){
        super.init(nibName: nil, bundle: nil)
        self.page = page
        self.listenNewModel = listenNewModel
    }
                required init?(coder: NSCoder) {
                                fatalError("init(coder:) has not been implemented")
                }
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
                override func viewDidAppear(_ animated: Bool) {
                                super.viewDidAppear(animated)
                                voicePlayer.delegate = self
                }
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        voicePlayer.delegate = self
    }
                override func viewDidDisappear(_ animated: Bool) {
                                super.viewDidDisappear(animated)
                                voicePlayer.delegate = nil
                                VoicePlayer.share().playerInterrupt()
                }
    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        voicePlayer.delegate = nil
        VoicePlayer.share().playerInterrupt()
    }
                override func viewDidLoad() {
                                super.viewDidLoad()
                                navigationItem.titleView = UIView()
                                playIndex.insert(IndexPath(row: 0, section: 0))
                                listenNewModel.subjectList[page][0].isQuestion = 1
                                listenNewModel.subjectList[page][1].isQuestion = 1
                                listenNewModel.subjectList[page][3].isQuestion = 1
    override func viewDidLoad() {
        super.viewDidLoad()
        navigationItem.titleView = UIView()
        playIndex.insert(IndexPath(row: 0, section: 0))
        listenNewModel.subjectList[page][0].isQuestion = 1
        listenNewModel.subjectList[page][1].isQuestion = 1
        listenNewModel.subjectList[page][3].isQuestion = 1
        DispatchQueue.main.asyncAfter(deadline: .now()+1) {
            self.isPlayingIndex = IndexPath(row: 0, section: 0)
            self.voicePlayer.playerAt(url: self.listenNewModel.subjectList[self.page][self.currentPlayIndex.row].correct)
            self.collectionView.reloadData()
        }
                                //回传记录,始终保持答题进度
                                if let team = teamScheduleModel{
                                                for teamId in team.teamIds{
                                                                for v in listenNewModel.subjectList[page]{
                                                                                if team.topicIds.contains(v.id){
                                                                                                rootViewModel.insertCorrectAnswer(teamId: "\(teamId)", answerId: v.id)
                                                                                }
                                                                }
                                                }
                                }
                }
        //回传记录,始终保持答题进度
        if let team = teamScheduleModel{
            for teamId in team.teamIds{
                for v in listenNewModel.subjectList[page]{
                    if team.topicIds.contains(v.id){
                        rootViewModel.insertCorrectAnswer(teamId: "\(teamId)", answerId: v.id)
                    }
                }
            }
        }
    }
                func restore(){
                                playIndex.removeAll()
                                playIndex.insert(IndexPath(row: 0, section: 0))
                                answterCount = 0
                                for subView in view.subviews{
                                                if subView is Lesson_3_AnswerView{
                                                                subView.removeFromSuperview()
                                                }
                                }
                                setUI()
                                collectionView.reloadData()
    func restore(){
        playIndex.removeAll()
        playIndex.insert(IndexPath(row: 0, section: 0))
        answterCount = 0
        for subView in view.subviews{
            if subView is Lesson_3_AnswerView{
                subView.removeFromSuperview()
            }
        }
        setUI()
        collectionView.reloadData()
                }
    }
                override func setUI() {
                                super.setUI()
                                if !view.subviews.contains(collectionView){
                                                collectionView.delegate = self
                                                collectionView.dataSource = self
                                                collectionView.showsVerticalScrollIndicator = false
                                                collectionView.contentInset = UIEdgeInsets(top: 33, left: 0, bottom: 0, right: 0)
                                                collectionView.backgroundColor = .clear
                                                view.addSubview(collectionView)
                                }
    override func setUI() {
        super.setUI()
        if !view.subviews.contains(collectionView){
            collectionView.delegate = self
            collectionView.dataSource = self
            collectionView.showsVerticalScrollIndicator = false
            collectionView.contentInset = UIEdgeInsets(top: 33, left: 0, bottom: 0, right: 0)
            collectionView.backgroundColor = .clear
            view.addSubview(collectionView)
        }
                                collectionView.snp.makeConstraints { make in
                                                make.top.equalTo(self.view.safeAreaLayoutGuide.snp.top).offset(0)
                                                make.left.equalTo(97)
                                                make.right.equalTo(-54)
                                                make.bottom.equalToSuperview()
                                }
                }
        collectionView.snp.makeConstraints { make in
            make.top.equalTo(self.view.safeAreaLayoutGuide.snp.top).offset(0)
            make.left.equalTo(97)
            make.right.equalTo(-54)
            make.bottom.equalToSuperview()
        }
    }
                override func setRx() {
    override func setRx() {
        NotificationCenter.default.rx.notification(ResetLession_Noti).take(until: self.rx.deallocated).subscribe(onNext: {[weak self]_ in
            self?.restore()
            self?.viewDidLoad()
        }).disposed(by: disposeBag)
    }
                }
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        let flowLayout = collectionView.collectionViewLayout as! UICollectionViewFlowLayout
                override func viewDidLayoutSubviews() {
                                super.viewDidLayoutSubviews()
                                let flowLayout = collectionView.collectionViewLayout as! UICollectionViewFlowLayout
        let w = (collectionView.size.width - flowLayout.minimumLineSpacing) / 3.1
        let h = (collectionView.size.height - flowLayout.minimumInteritemSpacing) / 2 - 40
        if flowLayout.itemSize.width != w || flowLayout.itemSize.height != h{
            flowLayout.itemSize = CGSize(width: w, height: h)
            collectionView.reloadData()
        }
    }
                                let w = (collectionView.size.width - flowLayout.minimumLineSpacing) / 3.1
                                let h = (collectionView.size.height - flowLayout.minimumInteritemSpacing) / 2 - 40
                                if flowLayout.itemSize.width != w || flowLayout.itemSize.height != h{
                                                flowLayout.itemSize = CGSize(width: w, height: h)
                                                collectionView.reloadData()
                                }
                }
                private func setAnswerStackView(force:Bool = false){
    private func setAnswerStackView(force:Bool = false){
                                for v in stackView.arrangedSubviews{
                                                v.removeFromSuperview()
                                }
        for v in stackView.arrangedSubviews{
            v.removeFromSuperview()
        }
                                                var tempImageArray = [String]()
                                                tempImageArray.append(listenNewModel.subjectList[page][2].img)
                                                tempImageArray.append(listenNewModel.subjectList[page][4].img)
                                                tempImageArray.append(listenNewModel.subjectList[page][5].img)
        var tempImageArray = [String]()
        tempImageArray.append(listenNewModel.subjectList[page][2].img)
        tempImageArray.append(listenNewModel.subjectList[page][4].img)
        tempImageArray.append(listenNewModel.subjectList[page][5].img)
                                                view.addSubview(stackView)
                                                stackView.snp.makeConstraints { make in
                                                                make.right.equalToSuperview().offset(-82)
                                                                make.centerY.equalToSuperview().offset(10)
                                                                make.height.equalTo(52)
                                                }
        view.addSubview(stackView)
        stackView.snp.makeConstraints { make in
            make.right.equalToSuperview().offset(-82)
            make.centerY.equalToSuperview().offset(10)
            make.height.equalTo(52)
        }
                                                var tempAnswerViews = [Lesson_3_AnswerView]()
                                                for i in 0...2{
                                                                let answerView = Lesson_3_AnswerView.jq_loadNibView()
                                                                answerView.alpha = 0
                                                                answerView.btn_choose.addTarget(self, action: #selector(chooseAnswerAction), for: .touchUpInside)
                                                                answerView.btn_fullscreen.addTarget(self, action: #selector(fullscreenAction), for: .touchUpInside)
                                                                answerView.img_cover.contentMode = .scaleToFill
                                                                answerView.btn_choose.tag = 10+i
                                                                answerView.btn_fullscreen.tag = 20+i
                                                                answerView.imageUrl = tempImageArray[i]
                                                                answerView.img_cover.sd_setImage(with: URL(string: tempImageArray[i]))
                                                                answerView.snp.makeConstraints { make in
                                                                                make.width.equalTo(85)
                                                                                make.height.equalTo(52)
                                                                }
        var tempAnswerViews = [Lesson_3_AnswerView]()
        for i in 0...2{
            let answerView = Lesson_3_AnswerView.jq_loadNibView()
            answerView.alpha = 0
            answerView.btn_choose.addTarget(self, action: #selector(chooseAnswerAction), for: .touchUpInside)
            answerView.btn_fullscreen.addTarget(self, action: #selector(fullscreenAction), for: .touchUpInside)
            answerView.img_cover.contentMode = .scaleToFill
            answerView.btn_choose.tag = 10+i
            answerView.btn_fullscreen.tag = 20+i
            answerView.imageUrl = tempImageArray[i]
            answerView.img_cover.sd_setImage(with: URL(string: tempImageArray[i]))
            answerView.snp.makeConstraints { make in
                make.width.equalTo(85)
                make.height.equalTo(52)
            }
                                                                UIView.animate(withDuration: 0.05 + Double(i)) {
                                                                                answerView.alpha = 1
                                                                }
                                                                tempAnswerViews.append(answerView)
                                                }
                                                tempAnswerViews.shuffle()
                                                stackView.addArrangedSubviews(tempAnswerViews)
                }
            //            UIView.animate(withDuration: 0.05 + Double(i)) {
            answerView.alpha = 1
            //            }
            tempAnswerViews.append(answerView)
        }
        tempAnswerViews.shuffle()
        stackView.addArrangedSubviews(tempAnswerViews)
    }
                @objc private func chooseAnswerAction(btn:UIButton){
    func handleClouseAction(clouse:@escaping ()->Void){
        self.handleClouse = clouse
    }
                                guard viewModel.selectIndex.value != nil else {return}
    @objc private func chooseAnswerAction(btn:UIButton){
                                if !islisten{
                                                alertError(msg: "请先听题");return
                                }
        guard viewModel.selectIndex.value != nil else {return}
                                if answterCount == 0 && !playIndex.contains(IndexPath(row: 2, section: 0)){
                                                alertError(msg: "请先听题");return
                                }
        if !islisten{
            alertError(msg: "请先听题");return
        }
                                if answterCount == 1 && !playIndex.contains(IndexPath(row: 1, section: 1)){
                                                alertError(msg: "请先听题");return
                                }
        if answterCount == 0 && !playIndex.contains(IndexPath(row: 2, section: 0)){
            alertError(msg: "请先听题");return
        }
                                if answterCount == 2 && !playIndex.contains(IndexPath(row: 2, section: 1)){
                                                alertError(msg: "请先听题");return
                                }
        if answterCount == 1 && !playIndex.contains(IndexPath(row: 1, section: 1)){
            alertError(msg: "请先听题");return
        }
                                var subV:Lesson_3_AnswerView?
        if answterCount == 2 && !playIndex.contains(IndexPath(row: 2, section: 1)){
            alertError(msg: "请先听题");return
        }
                                for (_,v) in (stackView.arrangedSubviews as! [Lesson_3_AnswerView]).enumerated(){
                                                if v.btn_choose.tag == btn.tag{
                                                                subV = v;break
                                                }
                                }
        var subV:Lesson_3_AnswerView?
                                var answerType:Fight_lessonType = .none
        for (_,v) in (stackView.arrangedSubviews as! [Lesson_3_AnswerView]).enumerated(){
            if v.btn_choose.tag == btn.tag{
                subV = v;break
            }
        }
                                var valueIndex:Int! //图片的角标
                                if viewModel.selectIndex.value?.section == 0{
                                                valueIndex = viewModel.selectIndex.value!.row
                                }
                                if viewModel.selectIndex.value?.section == 1{
                                                valueIndex = viewModel.selectIndex.value!.row + 3
                                }
        var answerType:Fight_lessonType = .none
                                if subV?.imageUrl == listenNewModel.subjectList[page][valueIndex].img{
                                                listenNewModel.subjectList[page][valueIndex].isAnster = true
                                                answerType = .success
                                                subV?.alpha = 0
                                                voicePlayer.playSuccessVoice()
                                }else{
                                                answerType = .fail
                                                voicePlayer.playFailVoice()
                                }
        var valueIndex:Int! //图片的角标
        if viewModel.selectIndex.value?.section == 0{
            valueIndex = viewModel.selectIndex.value!.row
        }
        if viewModel.selectIndex.value?.section == 1{
            valueIndex = viewModel.selectIndex.value!.row + 3
        }
                                switch answerType {
                                                case .success:
                                                                rootViewModel.correctNum += 1
                                                                viewModel.answerType.accept(.success)
                                                                let copyViewFrame = subV?.convert(subV!.bounds, to: self.view)
                                                                let copyView = subV?.copyView()
                                                                copyView?.frame = copyViewFrame!
                                                                copyView?.img_cover.contentMode = .scaleToFill
                                                                copyView?.img_cover.image = subV?.img_cover.image
                                                                self.view.addSubview(copyView!)
                                                                copyView?.layoutIfNeeded()
        if subV?.imageUrl == listenNewModel.subjectList[page][valueIndex].img{
            listenNewModel.subjectList[page][valueIndex].isAnster = true
            answerType = .success
            subV?.alpha = 0
            voicePlayer.playSuccessVoice()
            currentPlayIndex = IndexPath(row: 0, section: 1)
            self.voicePlayer.playerAt(url: self.listenNewModel.subjectList[self.page][currentPlayIndex.row + 3].correct)
                                                                var ansterIndePath:IndexPath?
                                                                if viewModel.selectIndex.value?.section == 0{
                                                                                ansterIndePath = IndexPath(row: 2, section: 0)
                                                                                playIndex.insert(IndexPath(row: 0, section: 1)) //下一个准备播放
                                                                }
            let model = listenNewModel.list[self.page]
            model.status = 2
            Services.answerQuestion(id: model.id, status: 2).subscribe(onNext: {_ in
                self.handleClouse?()
            }).disposed(by: self.disposeBag)
                                                                if viewModel.selectIndex.value?.section == 1 && (viewModel.selectIndex.value?.row == 0 || viewModel.selectIndex.value?.row == 1){
                                                                                ansterIndePath = IndexPath(row: 1, section: 1)
                                                                                playIndex.insert(IndexPath(row: 2, section: 1)) //下一个准备播放
                                                                }
        }else{
            answerType = .fail
            voicePlayer.playFailVoice()
                                                                if viewModel.selectIndex.value?.section == 1 && viewModel.selectIndex.value?.row == 2{
                                                                                ansterIndePath = IndexPath(row: 2, section: 1)
                                                                }
            let model = listenNewModel.list[self.page]
            model.status = 3
            Services.answerQuestion(id: model.id, status: 3).subscribe(onNext: {_ in
                self.handleClouse?()
            }).disposed(by: self.disposeBag)
        }
                                                                guard ansterIndePath != nil else {return}
        switch answerType {
        case .success:
            rootViewModel.correctNum += 1
            viewModel.answerType.accept(.success)
            let copyViewFrame = subV?.convert(subV!.bounds, to: self.view)
            let copyView = subV?.copyView()
            copyView?.frame = copyViewFrame!
            copyView?.img_cover.contentMode = .scaleToFill
            copyView?.img_cover.image = subV?.img_cover.image
            self.view.addSubview(copyView!)
            copyView?.layoutIfNeeded()
                                                                if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "_ListenFight_lesson_3_CCell", for: ansterIndePath!) as? ListenFight_lesson_3_CCell{
                                                                                var newFrame = cell.img_cover.convert(cell.img_cover.bounds, to: self.view)
                                                                                newFrame.origin.x += 0
                                                                                newFrame.origin.y += 0
            var ansterIndePath:IndexPath?
            if viewModel.selectIndex.value?.section == 0{
                ansterIndePath = IndexPath(row: 2, section: 0)
                playIndex.insert(IndexPath(row: 0, section: 1)) //下一个准备播放
            }
                                                                                let successImage = UIImageView(image: UIImage(named: "icon_success"))
                                                                                successImage.bounds = CGRect(x: 0, y: 0, width: 75, height: 75)
                                                                                successImage.center = newFrame.center
                                                                                successImage.transform = .init(scaleX: 0.1, y: 0.1)
                                                                                successImage.alpha = 0
                                                                                successImage.layoutIfNeeded()
                                                                                self.view.addSubview(successImage)
                                                                                UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 0.4, initialSpringVelocity: 0.4) {
                                                                                                successImage.transform = .init(scaleX: 1.0, y: 1.0)
                                                                                                successImage.alpha = 1
                                                                                }
            if viewModel.selectIndex.value?.section == 1 && (viewModel.selectIndex.value?.row == 0 || viewModel.selectIndex.value?.row == 1){
                ansterIndePath = IndexPath(row: 1, section: 1)
                playIndex.insert(IndexPath(row: 2, section: 1)) //下一个准备播放
            }
                                                                                UIView.animate(withDuration: 0.5, delay: 3) {
                                                                                                successImage.transform = .init(scaleX: 0.1, y: 0.1)
                                                                                                successImage.alpha = 0
                                                                                }completion: { _ in
                                                                                                successImage.removeFromSuperview()
                                                                                }
            if viewModel.selectIndex.value?.section == 1 && viewModel.selectIndex.value?.row == 2{
                ansterIndePath = IndexPath(row: 2, section: 1)
            }
                                                                                UIView.animate(withDuration: 0.4) {
                                                                                                copyView?.frame = newFrame
                                                                                } completion: { _ in
                                                                                                self.answterCount += 1
            guard ansterIndePath != nil else {return}
                                                                                                let cell = self.collectionView.cellForItem(at: self.viewModel.selectIndex.value!) as! ListenFight_lesson_3_CCell
                                                                                                self.isPlayingIndex = self.viewModel.selectIndex.value
                                                                                                cell.isPlaying(isplaying: true)
            if let cell = collectionView.cellForItem(at: ansterIndePath!) as? ListenFight_lesson_3_CCell{
                var newFrame = cell.img_cover.convert(cell.img_cover.bounds, to: self.view)
                newFrame.origin.x += 0
                newFrame.origin.y += 0
                                                                                                self.voicePlayer.playerAt(url: self.listenNewModel.subjectList[self.page][valueIndex].correct)
                let successImage = UIImageView(image: UIImage(named: "icon_success"))
                successImage.bounds = CGRect(x: 0, y: 0, width: 75, height: 75)
                successImage.center = newFrame.center
                successImage.transform = .init(scaleX: 0.1, y: 0.1)
                successImage.alpha = 0
                successImage.layoutIfNeeded()
                self.view.addSubview(successImage)
                UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 0.4, initialSpringVelocity: 0.4) {
                    successImage.transform = .init(scaleX: 1.0, y: 1.0)
                    successImage.alpha = 1
                }
                                                                                                let teamId = self.listenNewModel.data?.id.components(separatedBy: ",")[self.page]
                                                                                                let answerId = self.listenNewModel.subjectList[self.page][valueIndex].id
                                                                                                self.rootViewModel.insertCorrectAnswer(teamId: teamId, answerId: answerId)
                UIView.animate(withDuration: 0.5, delay: 3) {
                    successImage.transform = .init(scaleX: 0.1, y: 0.1)
                    successImage.alpha = 0
                }completion: { _ in
                    successImage.removeFromSuperview()
                }
                                                                                                for v in self.stackView.arrangedSubviews{
                                                                                                                v.removeFromSuperview()
                                                                                                }
                UIView.animate(withDuration: 0.4) {
                    copyView?.frame = newFrame
                } completion: { _ in
                    self.answterCount += 1
                                                                                                DispatchQueue.main.asyncAfter(deadline: .now()+1.0) {
                                                                                                                self.viewModel.selectIndex.accept(nil)
                                                                                                                let v = self.rootViewModel.answerCount.value + 1
                                                                                                                self.rootViewModel.answerCount.accept(v)
                                                                                                                self.collectionView.reloadData()
                                                                                                }
                                                                                }
                                                                }
                    let cell = self.collectionView.cellForItem(at: self.viewModel.selectIndex.value!) as! ListenFight_lesson_3_CCell
                    self.isPlayingIndex = self.viewModel.selectIndex.value
                    cell.isPlaying(isplaying: true)
                                                case .fail:
                                                                rootViewModel.errorNum += 1
                                                                viewModel.answerType.accept(.fail)
                                                                UIView.animate(withDuration: 0.4) {
                                                                                subV?.img_state.alpha = 1
                                                                                subV?.btn_fullscreen.alpha = 0
                                                                }completion: { _ in
                                                                                DispatchQueue.main.asyncAfter(deadline: .now()+1.5) {
                                                                                                self.islisten  = false
                                                                                                for v in self.stackView.arrangedSubviews{
                                                                                                                v.removeFromSuperview()
                                                                                                }
                                                                                }
                                                                }
                                                default:break
                                }
                }
                    self.voicePlayer.playerAt(url: self.listenNewModel.subjectList[self.page][valueIndex].correct)
                @objc private func fullscreenAction(btn:UIButton){
                                var answerView:Lesson_3_AnswerView!
                                for (_,v) in (stackView.arrangedSubviews as! [Lesson_3_AnswerView]).enumerated(){
                                                if v.btn_fullscreen.tag == btn.tag{
                                                                answerView = v;break
                                                }
                                }
                    let teamId = self.listenNewModel.data?.id.components(separatedBy: ",")[self.page]
                    let answerId = self.listenNewModel.subjectList[self.page][valueIndex].id
                    self.rootViewModel.insertCorrectAnswer(teamId: teamId, answerId: answerId)
                    for v in self.stackView.arrangedSubviews{
                        v.removeFromSuperview()
                    }
                    DispatchQueue.main.asyncAfter(deadline: .now()+1.0) {
                        //                                                                                                                self.viewModel.selectIndex.accept(nil)
                        let v = self.rootViewModel.answerCount.value + 1
                        self.rootViewModel.answerCount.accept(v)
                        self.collectionView.reloadData()
                    }
                }
            }
        case .fail:
            rootViewModel.errorNum += 1
            viewModel.answerType.accept(.fail)
            UIView.animate(withDuration: 0.4) {
                subV?.img_state.alpha = 1
                subV?.btn_fullscreen.alpha = 0
            }completion: { _ in
                DispatchQueue.main.asyncAfter(deadline: .now()+1.5) {
                    self.islisten  = false
                    for v in self.stackView.arrangedSubviews{
                        v.removeFromSuperview()
                    }
                }
            }
        default:break
        }
    }
    @objc private func fullscreenAction(btn:UIButton){
        var answerView:Lesson_3_AnswerView!
        for (_,v) in (stackView.arrangedSubviews as! [Lesson_3_AnswerView]).enumerated(){
            if v.btn_fullscreen.tag == btn.tag{
                answerView = v;break
            }
        }
                                let lantern = Lantern()
                                lantern.numberOfItems = { () in
                                                return 1
                                }
        let lantern = Lantern()
        lantern.numberOfItems = { () in
            return 1
        }
                                lantern.cellClassAtIndex = { _ in
                                                LanternImageCell.self
                                }
        lantern.cellClassAtIndex = { _ in
            LanternImageCell.self
        }
                                lantern.transitionAnimator = LanternZoomAnimator(previousView: { index -> UIView? in
                                                return answerView.img_cover
                                })
        lantern.transitionAnimator = LanternZoomAnimator(previousView: { index -> UIView? in
            return answerView.img_cover
        })
                                // UIPageIndicator样式的页码指示器
                                lantern.pageIndicator = LanternDefaultPageIndicator()
        // UIPageIndicator样式的页码指示器
        lantern.pageIndicator = LanternDefaultPageIndicator()
                                lantern.pageIndex = 0
        lantern.pageIndex = 0
                                lantern.reloadCellAtIndex = { context in
                                                let lanternCell = context.cell as? LanternImageCell
                                                lanternCell?.imageView.image = answerView.img_cover.image
                                }
                                //不要使用push
                                lantern.show()
                }
        lantern.reloadCellAtIndex = { context in
            let lanternCell = context.cell as? LanternImageCell
            lanternCell?.imageView.image = answerView.img_cover.image
        }
        //不要使用push
        lantern.show()
    }
}
extension HomeListenFight_lesson_3_VC:UICollectionViewDelegate{
                func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
                }
    }
}
extension HomeListenFight_lesson_3_VC:UICollectionViewDelegateFlowLayout{
                func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
                                if section == 0{
                                                return CGSize.zero
                                }
                                return CGSizeMake(JQ_ScreenW, 65)
                }
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
        if section == 0{
            return CGSize.zero
        }
        return CGSizeMake(JQ_ScreenW, 65)
    }
}
extension HomeListenFight_lesson_3_VC:UICollectionViewDataSource{
                func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
                                let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "_ListenFight_lesson_3_CCell", for: indexPath) as! ListenFight_lesson_3_CCell
                                cell.backgroundColor = .clear
                                cell.indexPath = indexPath
                                cell.contentView.backgroundColor = .clear
                                cell.canClick(playIndex.contains(indexPath))
                                cell.palyVoiceAt {[weak self] index in
                                                guard let weakSelf = self else { return }
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "_ListenFight_lesson_3_CCell", for: indexPath) as! ListenFight_lesson_3_CCell
        cell.indexPath = indexPath
        cell.canClick(playIndex.contains(indexPath))
        cell.palyVoiceAt {[weak self] index in
            guard let weakSelf = self else { return }
                                                weakSelf.isPlayingIndex = index
                                                weakSelf.viewModel.selectIndex.accept(index)
            weakSelf.isPlayingIndex = index
            weakSelf.viewModel.selectIndex.accept(index)
                                                weakSelf.voicePlayer.playerEnd()
            weakSelf.voicePlayer.playerEnd()
                                                if indexPath.section == 1{
                                                                weakSelf.voicePlayer.playerAt(url:  weakSelf.listenNewModel.subjectList[weakSelf.page][indexPath.row + 3].correct)
                                                }else{
                                                                weakSelf.voicePlayer.playerAt(url:  weakSelf.listenNewModel.subjectList[weakSelf.page][indexPath.row].correct)
                                                }
            if indexPath.section == 1{
                weakSelf.voicePlayer.playerAt(url:  weakSelf.listenNewModel.subjectList[weakSelf.page][indexPath.row + 3].correct)
            }else{
                weakSelf.voicePlayer.playerAt(url:  weakSelf.listenNewModel.subjectList[weakSelf.page][indexPath.row].correct)
            }
                                                if index == IndexPath(row: 2, section: 0) || index == IndexPath(row: 1, section: 1) || index == IndexPath(row: 2, section: 1){
            if index == IndexPath(row: 2, section: 0) || index == IndexPath(row: 1, section: 1) || index == IndexPath(row: 2, section: 1){
                                                                var model:Listen1SubModel?
                                                                if indexPath.section == 0{
                                                                                model = weakSelf.listenNewModel.subjectList[weakSelf.page][indexPath.row]
                                                                }
                var model:Listen1SubModel?
                if indexPath.section == 0{
                    model = weakSelf.listenNewModel.subjectList[weakSelf.page][indexPath.row]
                }
                                                                if indexPath.section == 1{
                                                                                model = weakSelf.listenNewModel.subjectList[weakSelf.page][indexPath.row + 3]
                                                                }
                if indexPath.section == 1{
                    model = weakSelf.listenNewModel.subjectList[weakSelf.page][indexPath.row + 3]
                }
                                                                if model?.isAnster == false{
                                                                                //点击答案,就显示
                                                                                weakSelf.setAnswerStackView()
                                                                }
                                                }
                if model?.isAnster == false{
                    //点击答案,就显示
                    weakSelf.setAnswerStackView()
                }
            }
                                                collectionView.reloadItems(at: [index])
                                }
            collectionView.reloadItems(at: [index])
        }
                                if indexPath.section == 0{
                                                let model = listenNewModel.subjectList[page][indexPath.row]
                                                if indexPath.row != 2{
                                                                cell.img_cover.sd_setImage(with: URL(string: model.img))
                                                }else{
                                                                cell.img_cover.image = nil
                                                }
                                                cell.setModel(model,isplaying: isPlayingIndex == indexPath)
                                }
        if indexPath.section == 0{
            let model = listenNewModel.subjectList[page][indexPath.row]
            if indexPath.row != 2{
                cell.img_cover.sd_setImage(with: URL(string: model.img))
            }else{
                cell.img_cover.image = nil
            }
            cell.setModel(model,isplaying: isPlayingIndex == indexPath)
        }
                                if indexPath.section == 1{
                                                let model = listenNewModel.subjectList[page][indexPath.row + 3]
                                                if indexPath.row == 0{
                                                                cell.img_cover.sd_setImage(with: URL(string: model.img))
                                                }else{
                                                                cell.img_cover.image = nil
                                                }
                                                cell.setModel(model,isplaying: isPlayingIndex == indexPath)
                                }
                                return cell
                }
        if indexPath.section == 1{
            let model = listenNewModel.subjectList[page][indexPath.row + 3]
            if indexPath.row == 0{
                cell.img_cover.sd_setImage(with: URL(string: model.img))
            }else{
                cell.img_cover.image = nil
            }
            cell.setModel(model,isplaying: isPlayingIndex == indexPath)
        }
        return cell
    }
                func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
                                let reusableView = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "header", for: indexPath)
                                return reusableView
    func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
        let reusableView = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "header", for: indexPath)
        return reusableView
                }
    }
                func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
                                return 3
                }
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 3
    }
                func numberOfSections(in collectionView: UICollectionView) -> Int {
                                return 2
                }
    func numberOfSections(in collectionView: UICollectionView) -> Int {
        return 2
    }
}
extension HomeListenFight_lesson_3_VC:VoicePlayerDelegate{
                func playComplete() {
                                view.isUserInteractionEnabled = true
                                isPlayingIndex =  nil
                                islisten  = true
                                var nextRow = (viewModel.selectIndex.value?.row ?? 0) + 1
                                var section = (viewModel.selectIndex.value?.section ?? 0) + 0
    func playComplete() {
        view.isUserInteractionEnabled = true
        isPlayingIndex =  nil
        islisten  = true
        var nextRow = (viewModel.selectIndex.value?.row ?? 0) + 1
        var section = (viewModel.selectIndex.value?.section ?? 0) + 0
                                if nextRow >= 3{
                                                nextRow = 0;section = 1
                                }
        if nextRow >= 3{
            nextRow = 0;section = 1
        }
        currentPlayIndex.row+=1
        if currentPlayIndex.row < 3{
                                if self.answterCount == 3{
                                                NotificationCenter.default.post(name: NextLession_Noti, object: nil)
                                                return
                                }
            var row = currentPlayIndex.row
            if currentPlayIndex.section == 1{
                row += 3
            }
                                if (viewModel.selectIndex.value?.section == 0 && viewModel.selectIndex.value?.row == 2) || (viewModel.selectIndex.value?.section == 1 && viewModel.selectIndex.value?.row == 1){
                                                collectionView.reloadData()
                                                return
                                }
                                playIndex.insert(IndexPath(row: nextRow, section: section)) //下一个准备播放
                                collectionView.reloadData()
                }
            self.voicePlayer.playerAt(url: self.listenNewModel.subjectList[self.page][row].correct)
            self.isPlayingIndex = currentPlayIndex
            self.viewModel.selectIndex.accept(currentPlayIndex)
            self.playIndex.insert(currentPlayIndex)
        }
                func playing() {
                                islisten = false
                                view.isUserInteractionEnabled = false
                }
        if currentPlayIndex.row == 2{
            self.isPlayingIndex = currentPlayIndex
            self.viewModel.selectIndex.accept(currentPlayIndex)
            self.playIndex.insert(currentPlayIndex)
            self.setAnswerStackView()
        }
        if self.answterCount == 2{
            NotificationCenter.default.post(name: NextLession_Noti, object: nil)
            return
        }
        if (viewModel.selectIndex.value?.section == 0 && viewModel.selectIndex.value?.row == 2) || (viewModel.selectIndex.value?.section == 1 && viewModel.selectIndex.value?.row == 1){
            collectionView.reloadData()
            return
        }
        playIndex.insert(IndexPath(row: nextRow, section: section)) //下一个准备播放
        collectionView.reloadData()
    }
    func playing() {
        islisten = false
        view.isUserInteractionEnabled = false
    }
}
DolphinEnglishLearnStudent/Moudle/Home/Listen/VC/HomeListenFight_lesson_4_VC.swift
@@ -6,441 +6,502 @@
//
import UIKit
import JQTools
class HomeListenFight_lesson_4_VC: BaseVC {
                private var viewModel = FightAnswerViewModel()
                private var listenNewModel:ListenNewModel!
                private var page:Int!
                var rootViewModel:HomeListenFightViewModel!
                var teamScheduleModel:TeamScheduleModel?
                private var answerIndex:IndexPath? //答案的Index
                private var answerIndexs = Set<IndexPath>() //回答过的Index集合
                private var filterItems = [[Listen1SubModel]]() //此类型特殊,需要数据清理
                private lazy var stackView:UIStackView = {
                                let sta = UIStackView()
                                sta.spacing = 41
                                sta.distribution = .equalSpacing
                                sta.axis = .vertical
                                return sta
                }()
                private lazy var collectionView:UICollectionView = {
                                let flowLayout = UICollectionViewFlowLayout()
                                let w = (JQ_ScreenW - 164 * 2 - 62) / 2.0
                                flowLayout.itemSize = CGSize(width: w, height: w * 0.842)
                                flowLayout.minimumInteritemSpacing = 0
                                flowLayout.scrollDirection = .vertical
                                let collection = UICollectionView(frame: .zero, collectionViewLayout: flowLayout)
                                collection.contentInset = UIEdgeInsets(top: 33, left: 0, bottom: 0, right: 0)
                                collection.register(UINib(nibName: "ListenFight_lesson_4_CCell", bundle: nil), forCellWithReuseIdentifier: "_ListenFight_lesson_4_CCell")
                                collection.register(UICollectionReusableView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "header")
                                collection.isScrollEnabled = false
                                return collection
                }()
                private var voicePlayer = VoicePlayer.share()
                required init(page:Int,listenNewModel:ListenNewModel){
                                super.init(nibName: nil, bundle: nil)
                                self.page = page
                                self.listenNewModel = listenNewModel
                                let temp1 = Array(listenNewModel.subjectList[page][0...1])
                                let temp2 = Array(listenNewModel.subjectList[page][2...3])
                                filterItems.append(temp1)
                                filterItems.append(temp2)
                }
                required init?(coder: NSCoder) {
                                fatalError("init(coder:) has not been implemented")
                }
                func restore(){
                                answerIndexs.removeAll()
                                answerIndex = nil
                                for subView in view.subviews{
                                                if subView is Lesson_4_AnswerView{
                                                                subView.removeFromSuperview()
                                                }
                                }
                                setUI()
                                collectionView.reloadData()
                }
    private var viewModel = FightAnswerViewModel()
    private var listenNewModel:ListenNewModel!
    private var page:Int!
    var rootViewModel:HomeListenFightViewModel!
    var teamScheduleModel:TeamScheduleModel?
    private var answerIndex:IndexPath? //答案的Index
    private var answerIndexs = Set<IndexPath>() //回答过的Index集合
    private var filterItems = [[Listen1SubModel]]() //此类型特殊,需要数据清理
    private var handleClouse:(()->Void)?
    private lazy var stackView:UIStackView = {
        let sta = UIStackView()
        sta.spacing = 41
        sta.distribution = .equalSpacing
        sta.axis = .vertical
        return sta
    }()
    private lazy var collectionView:UICollectionView = {
        let flowLayout = UICollectionViewFlowLayout()
        let w = (JQ_ScreenW - 164 * 2 - 62) / 2.0
        flowLayout.minimumLineSpacing = 20
        flowLayout.minimumInteritemSpacing = 0
        flowLayout.itemSize = CGSize(width: w, height: w * 0.702)
        flowLayout.scrollDirection = .vertical
        let collection = UICollectionView(frame: .zero, collectionViewLayout: flowLayout)
        collection.contentInset = UIEdgeInsets(top: 33, left: 0, bottom: 0, right: 0)
        collection.register(UINib(nibName: "ListenFight_lesson_4_CCell", bundle: nil), forCellWithReuseIdentifier: "_ListenFight_lesson_4_CCell")
        collection.register(UICollectionReusableView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "header")
        collection.isScrollEnabled = false
        return collection
    }()
    private var voicePlayer = VoicePlayer.share()
    required init(page:Int,listenNewModel:ListenNewModel){
        super.init(nibName: nil, bundle: nil)
        self.page = page
        self.listenNewModel = listenNewModel
        let temp1 = Array(listenNewModel.subjectList[page][0...1])
        let temp2 = Array(listenNewModel.subjectList[page][2...3])
        filterItems.append(temp1)
        filterItems.append(temp2)
    }
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    func restore(){
        answerIndexs.removeAll()
        answerIndex = nil
        for subView in view.subviews{
            if subView is Lesson_4_AnswerView{
                subView.removeFromSuperview()
            }
        }
        setUI()
        collectionView.reloadData()
    }
    override func viewDidLoad() {
        super.viewDidLoad()
                                //回传记录,始终保持答题进度
                                if let team = teamScheduleModel{
                                                for teamId in team.teamIds{
                                                                for v in listenNewModel.subjectList[page]{
                                                                                if team.topicIds.contains(v.id){
                                                                                                rootViewModel.insertCorrectAnswer(teamId: "\(teamId)", answerId: v.id)
                                                                                }
                                                                }
                                                }
                                }
        //回传记录,始终保持答题进度
        if let team = teamScheduleModel{
            for teamId in team.teamIds{
                for v in listenNewModel.subjectList[page]{
                    if team.topicIds.contains(v.id){
                        rootViewModel.insertCorrectAnswer(teamId: "\(teamId)", answerId: v.id)
                    }
                }
            }
        }
        DispatchQueue.main.asyncAfter(deadline: .now()+1) {
            var index:IndexPath!
            if self.filterItems[0][0].isQuestion == 1{
                index = IndexPath(row: 0, section: 0)
            }else{
                index = IndexPath(row: 1, section: 0)
            }
            let m = self.filterItems[index.section][index.row]
            self.viewModel.selectIndex.accept(index)
            UIView.animate(withDuration: 0.5) {
                self.collectionView.snp.remakeConstraints { make in
                    make.left.equalTo(76)
                    make.top.equalTo(self.view.safeAreaLayoutGuide.snp.top).offset(0)
                    make.width.equalTo(JQ_ScreenW - 164 * 2)
                    make.bottom.equalToSuperview()
                }
                self.view.layoutIfNeeded()
            }completion: { _ in
                self.setAnswerStackView()
                VoicePlayer.share().playerAt(url: m.correct)
            }
        }
    }
                override func viewDidAppear(_ animated: Bool) {
                                super.viewDidAppear(animated)
                                voicePlayer.delegate = self
                }
                override func viewDidDisappear(_ animated: Bool) {
                                super.viewDidDisappear(animated)
                                voicePlayer.delegate = nil
                                VoicePlayer.share().playerInterrupt()
                }
                override func setUI() {
                                super.setUI()
                                if !view.subviews.contains(collectionView){
                                                collectionView.delegate = self
                                                collectionView.dataSource = self
                                                collectionView.showsVerticalScrollIndicator = false
                                                collectionView.backgroundColor = .clear
                                                view.addSubview(collectionView)
                                }
                                collectionView.snp.makeConstraints { make in
                                                make.top.equalTo(self.view.safeAreaLayoutGuide.snp.top).offset(0)
                                                make.left.equalTo(164)
                                                make.width.equalTo(JQ_ScreenW - 164 * 2)
                                                make.bottom.equalToSuperview()
                                }
                }
                override func viewDidLayoutSubviews() {
                                super.viewDidLayoutSubviews()
                                let flowLayout = collectionView.collectionViewLayout as! UICollectionViewFlowLayout
                                let w = (collectionView.size.width - flowLayout.minimumLineSpacing) / 2
                                let h = (collectionView.size.height - flowLayout.minimumInteritemSpacing) / 2.1
                                if flowLayout.itemSize.width != w || flowLayout.itemSize.height != h{
                                                flowLayout.itemSize = CGSize(width: w, height: h)
                                                collectionView.reloadData()
                                }
                }
                private func setAnswerStackView(){
                                guard let selectIndex = viewModel.selectIndex.value else{
                                                alertError(msg: "请先听题");return
                                }
                                for v in stackView.subviews{
                                                v.removeFromSuperview()
                                }
                                if !view.subviews.contains(stackView){
                                                view.addSubview(stackView)
                                                stackView.snp.makeConstraints { make in
                                                                make.right.equalToSuperview().offset(-14)
                                                                make.centerY.equalToSuperview()
                                                }
                                }
                                let answerModel = filterItems[selectIndex.section].filter({$0.isQuestion == 0}).first!
                                let answerId = answerModel.id
                                var tempVoiceArray = [String]()
                                for v in filterItems[selectIndex.section]{
                                                if v.id == answerId{
                                                                tempVoiceArray.append(v.correct)
                                                                tempVoiceArray.append(v.error.components(separatedBy: ",").first ?? "")
                                                                tempVoiceArray.append(v.error.components(separatedBy: ",").last ?? "")
                                                }
                                }
                                tempVoiceArray.shuffle()
                                for i in 0...2{
                                                let answerView = Lesson_4_AnswerView.jq_loadNibView()
                                                answerView.btn_choose.tag = 10 + i
                                                answerView.tag = 20 + i
                                                answerView.voiceUrl = tempVoiceArray[i]
                                                answerView.btn_isAnswer.setImage(viewModel.selectIndex.value?.row == 1 ? UIImage(named: "icon_question"):UIImage(named: "icon_answer"), for: .normal)
                                                answerView.btn_choose.addTarget(self, action: #selector(answerAction), for: .touchUpInside)
                                                answerView.alpha = 0
                                                answerView.snp.makeConstraints { make in
                                                                make.width.equalTo(221)
                                                                make.height.equalTo(52)
                                                }
                                                answerView.btn_choose.isEnabled = false
                                                answerView.playAt { index in
                                                                answerView.btn_choose.isEnabled = true
                                                }
                                                UIView.animate(withDuration: 0.05 + Double(i)) {
                                                                answerView.alpha = 1
                                                }
                                                stackView.insertArrangedSubview(answerView, at: 0)
                                }
                }
                @objc func answerAction(btn:UIButton){
                                var islistenDone:Int = 0
                                for v in stackView.arrangedSubviews as! [Lesson_4_AnswerView]{
                                                if v.btn_choose.isEnabled == true{
                                                                islistenDone += 1
                                                }
                                }
                                if islistenDone != 3{
                                                alertError(msg: "请先听完");return
                                }
                                guard let selectIndex = viewModel.selectIndex.value else{
                                                alertError(msg: "请先听题");return
                                }
                                var tempSubV:Lesson_4_AnswerView?
                                for subV in stackView.arrangedSubviews{
                                                if let s = subV as? Lesson_4_AnswerView{
                                                                s.btn_choose.isSelected = btn.tag == s.btn_choose.tag
                                                                if s.btn_choose.isSelected{tempSubV = s}
                                                }
                                }
                                var answerModel:Listen1SubModel?
                                for (index,v) in filterItems[selectIndex.section].enumerated(){
                                                let m = filterItems[selectIndex.section].filter({$0.isQuestion == 0}).first
                                                if v.id == m?.id{
                                                                answerModel = v
                                                                answerIndex = IndexPath(row: index, section: viewModel.selectIndex.value!.section)
                                                }
                                }
                                var answerType:Fight_lessonType = .none
                                if tempSubV?.voiceUrl == answerModel?.correct{
                                                answerType = .success
                                                voicePlayer.playSuccessVoice()
                                                var teamId:String = ""
                                                var answerId:Int = 0
                                                if self.viewModel.selectIndex.value?.section == 1{
                                                                teamId = self.listenNewModel.data?.id.components(separatedBy: ",")[self.page + 1] ?? ""
                                                                answerId = answerModel!.id
                                                }else{
                                                                teamId = self.listenNewModel.data?.id.components(separatedBy: ",")[self.page] ?? ""
                                                                answerId = answerModel!.id
                                                }
                                                self.rootViewModel.insertCorrectAnswer(teamId: teamId, answerId: answerId)
                                                DispatchQueue.main.asyncAfter(deadline: .now()+2) {
                                                                self.voicePlayer.playerAt(url: tempSubV!.voiceUrl)
                                                }
                                                //防止重复答题造成计数错误的问题
                                                if !answerIndexs.contains(answerIndex!){
                                                                rootViewModel.correctNum += 1
                                                                let v = rootViewModel.answerCount.value + 1
                                                                rootViewModel.answerCount.accept(v)
                                                }
                                                //正确才记录回答
                                                answerIndexs.insert(answerIndex!)
                                }else{
                                                answerType = .fail
                                                voicePlayer.playFailVoice()
                                                rootViewModel.errorNum += 1
                                }
                                voicePlayer.playerEnd()
                                switch answerType {
                                                case .success:
                                                                DispatchQueue.main.asyncAfter(deadline: .now()+0.4) {
                                                                                if let copyView = tempSubV?.copyView(){
                                                                                                tempSubV?.alpha = 0
                                                                                                let newRect = tempSubV!.view_handle.convert(tempSubV!.bounds, to: self.view)
                                                                                                copyView.frame = CGRect(origin: newRect.origin, size: CGSize(width: 159, height: 52))
                                                                                                copyView.view_state.isHidden = true
                                                                                                copyView.isCopy = true
                                                                                                copyView.isPlaying()
                                                                                                copyView.btn_isAnswer.setImage(self.viewModel.selectIndex.value?.row == 1 ? UIImage(named: "icon_question"):UIImage(named: "icon_answer"), for: .normal)
                                                                                                copyView.img_play.alpha = 1
                                                                                                copyView.voiceUrl = tempSubV!.voiceUrl
                                                                                                self.view.addSubview(copyView)
                                                                                                self.view.layoutIfNeeded()
                                                                                                //获取Cell的顶部试图
                                                                                                if let cell = self.collectionView.dequeueReusableCell(withReuseIdentifier: "_ListenFight_lesson_4_CCell", for: self.answerIndex!) as? ListenFight_lesson_4_CCell{
                                                                                                                var newRect1 = cell.convert(cell.bounds, to: self.collectionView)
                                                                                                                newRect1.origin.x += 76
                                                                                                                newRect1.origin.y += 33
                                                                                                                self.collectionView.reloadData()
                                                                                                                UIView.animate(withDuration: 0.4) {
                                                                                                                                copyView.frame = CGRect(origin: newRect1.origin, size: CGSize(width: 159, height: 52))
                                                                                                                } completion: { _ in
                                                                                                                                self.viewModel.selectIndex.accept(nil)
                                                                                                                                for v in self.stackView.subviews{
                                                                                                                                                v.removeFromSuperview()
                                                                                                                                }
                                                                                                                                self.stackView.layoutIfNeeded()
                                                                                                                }
                                                                                                }
                                                                                }
                                                                }
                                                case .fail:
                                                                self.view.isUserInteractionEnabled = false
                                                                UIView.animate(withDuration: 0.4) {
                                                                                tempSubV?.img_state.alpha = 1
                                                                }completion: { _ in
                                                                                UIView.animate(withDuration: 0.4, delay: 2.0) {
                                                                                                tempSubV?.img_state.alpha = 0
                                                                                }
                                                                                DispatchQueue.main.asyncAfter(deadline: .now()+2) {
                                                                                                self.setAnswerStackView()
                                                                                                self.view.isUserInteractionEnabled = true
                                                                                }
                                                                }
                                                case .none:
                                                                break
                                }
                }
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        voicePlayer.delegate = self
    }
    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        voicePlayer.delegate = nil
        VoicePlayer.share().playerInterrupt()
    }
    override func setUI() {
        super.setUI()
        if !view.subviews.contains(collectionView){
            collectionView.delegate = self
            collectionView.dataSource = self
            collectionView.showsVerticalScrollIndicator = false
            collectionView.backgroundColor = .clear
            view.addSubview(collectionView)
        }
        collectionView.snp.makeConstraints { make in
            make.top.equalTo(self.view.safeAreaLayoutGuide.snp.top).offset(0)
            make.left.equalTo(164)
            make.width.equalTo(JQ_ScreenW - 164 * 2)
            make.bottom.equalToSuperview()
        }
    }
    override func setRx() {
        NotificationCenter.default.rx.notification(ResetLession_Noti).take(until: self.rx.deallocated).subscribe(onNext: {[weak self]_ in
            self?.restore()
            self?.viewDidLoad()
        }).disposed(by: disposeBag)
    }
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        let flowLayout = collectionView.collectionViewLayout as! UICollectionViewFlowLayout
        flowLayout.minimumLineSpacing = 20
        flowLayout.minimumInteritemSpacing = 20
        let w = (collectionView.size.width - flowLayout.minimumLineSpacing) / 2
        let h = (collectionView.size.height - flowLayout.minimumInteritemSpacing) / 2.1
        if flowLayout.itemSize.width != w || flowLayout.itemSize.height != h{
            flowLayout.itemSize = CGSize(width: w, height: h - 20)
            collectionView.reloadData()
        }
    }
    private func setAnswerStackView(){
        guard let selectIndex = viewModel.selectIndex.value else{
            alertError(msg: "请先听题");return
        }
        for v in stackView.subviews{
            v.removeFromSuperview()
        }
        if !view.subviews.contains(stackView){
            view.addSubview(stackView)
            stackView.snp.makeConstraints { make in
                make.right.equalToSuperview().offset(-14)
                make.centerY.equalToSuperview()
            }
        }
        let answerModel = filterItems[selectIndex.section].filter({$0.isQuestion == 0}).first!
        let answerId = answerModel.id
        var tempVoiceArray = [String]()
        for v in filterItems[selectIndex.section]{
            if v.id == answerId{
                tempVoiceArray.append(v.correct)
                tempVoiceArray.append(v.error.components(separatedBy: ",").first ?? "")
                tempVoiceArray.append(v.error.components(separatedBy: ",").last ?? "")
            }
        }
        tempVoiceArray.shuffle()
        for i in 0...2{
            let answerView = Lesson_4_AnswerView.jq_loadNibView()
            answerView.btn_choose.tag = 10 + i
            answerView.tag = 20 + i
            answerView.voiceUrl = tempVoiceArray[i]
            answerView.btn_isAnswer.setImage(viewModel.selectIndex.value?.row == 1 ? UIImage(named: "icon_question"):UIImage(named: "icon_answer"), for: .normal)
            answerView.btn_choose.addTarget(self, action: #selector(answerAction), for: .touchUpInside)
            answerView.alpha = 0
            answerView.snp.makeConstraints { make in
                make.width.equalTo(221)
                make.height.equalTo(52)
            }
            answerView.btn_choose.isEnabled = false
            answerView.playAt { index in
                answerView.btn_choose.isEnabled = true
            }
            //            UIView.animate(withDuration: 0.05 + Double(i)) {
            answerView.alpha = 1
            //            }
            stackView.insertArrangedSubview(answerView, at: 0)
        }
    }
    func handleClouseAction(clouse:@escaping ()->Void){
        self.handleClouse = clouse
    }
    @objc func answerAction(btn:UIButton){
        var islistenDone:Int = 0
        for v in stackView.arrangedSubviews as! [Lesson_4_AnswerView]{
            if v.btn_choose.isEnabled == true{
                islistenDone += 1
            }
        }
        if islistenDone != 3{
            alertError(msg: "请先听完");return
        }
        guard let selectIndex = viewModel.selectIndex.value else{
            alertError(msg: "请先听题");return
        }
        var tempSubV:Lesson_4_AnswerView?
        for subV in stackView.arrangedSubviews{
            if let s = subV as? Lesson_4_AnswerView{
                s.btn_choose.isSelected = btn.tag == s.btn_choose.tag
                if s.btn_choose.isSelected{tempSubV = s}
            }
        }
        var answerModel:Listen1SubModel?
        for (index,v) in filterItems[selectIndex.section].enumerated(){
            let m = filterItems[selectIndex.section].filter({$0.isQuestion == 0}).first
            if v.id == m?.id{
                answerModel = v
                answerIndex = IndexPath(row: index, section: viewModel.selectIndex.value!.section)
            }
        }
        var answerType:Fight_lessonType = .none
        if tempSubV?.voiceUrl == answerModel?.correct{
            answerType = .success
            voicePlayer.playSuccessVoice()
            let model = listenNewModel.list[self.page]
            model.status = 2
            Services.answerQuestion(id: model.id, status: 2).subscribe(onNext: {_ in
                self.handleClouse?()
            }).disposed(by: self.disposeBag)
            var teamId:String = ""
            var answerId:Int = 0
            if self.viewModel.selectIndex.value?.section == 1{
                teamId = self.listenNewModel.data?.id.components(separatedBy: ",")[self.page + 1] ?? ""
                answerId = answerModel!.id
            }else{
                teamId = self.listenNewModel.data?.id.components(separatedBy: ",")[self.page] ?? ""
                answerId = answerModel!.id
            }
            self.rootViewModel.insertCorrectAnswer(teamId: teamId, answerId: answerId)
            DispatchQueue.main.asyncAfter(deadline: .now()+2) {
                self.voicePlayer.playerAt(url: tempSubV!.voiceUrl)
            }
            //防止重复答题造成计数错误的问题
            if !answerIndexs.contains(answerIndex!){
                rootViewModel.correctNum += 1
                let v = rootViewModel.answerCount.value + 1
                rootViewModel.answerCount.accept(v)
            }
            //正确才记录回答
            answerIndexs.insert(answerIndex!)
        }else{
            answerType = .fail
            voicePlayer.playFailVoice()
            rootViewModel.errorNum += 1
            let model = listenNewModel.list[self.page]
            model.status = 3
            Services.answerQuestion(id: model.id, status: 3).subscribe(onNext: {_ in
                self.handleClouse?()
            }).disposed(by: self.disposeBag)
        }
        voicePlayer.playerEnd()
        switch answerType {
        case .success:
            DispatchQueue.main.asyncAfter(deadline: .now()+0.4) {
                if let copyView = tempSubV?.copyView(){
                    tempSubV?.alpha = 0
                    let newRect = tempSubV!.view_handle.convert(tempSubV!.bounds, to: self.view)
                    copyView.frame = CGRect(origin: newRect.origin, size: CGSize(width: 159, height: 40))
                    copyView.view_state.isHidden = true
                    copyView.isCopy = true
                    copyView.isPlaying()
                    copyView.btn_isAnswer.setImage(self.viewModel.selectIndex.value?.row == 1 ? UIImage(named: "icon_question"):UIImage(named: "icon_answer"), for: .normal)
                    copyView.img_play.alpha = 1
                    copyView.voiceUrl = tempSubV!.voiceUrl
                    self.view.addSubview(copyView)
                    self.view.layoutIfNeeded()
                    //获取Cell的顶部试图
                    if let cell = self.collectionView.cellForItem(at: self.answerIndex!) as? ListenFight_lesson_4_CCell{
                        var newRect1 = cell.convert(cell.bounds, to: self.collectionView)
                        newRect1.origin.x += 76
                        newRect1.origin.y += 33
                        self.collectionView.reloadData()
                        UIView.animate(withDuration: 0.4) {
                            copyView.frame = CGRect(origin: newRect1.origin, size: CGSize(width: newRect1.width, height: 40))
                        } completion: { _ in
                            self.viewModel.selectIndex.accept(nil)
                            for v in self.stackView.subviews{
                                v.removeFromSuperview()
                            }
                            self.stackView.layoutIfNeeded()
                        }
                    }
                }
            }
        case .fail:
            self.view.isUserInteractionEnabled = false
            UIView.animate(withDuration: 0.4) {
                tempSubV?.img_state.alpha = 1
            }completion: { _ in
                UIView.animate(withDuration: 0.4, delay: 2.0) {
                    tempSubV?.img_state.alpha = 0
                }
                DispatchQueue.main.asyncAfter(deadline: .now()+2) {
                    self.setAnswerStackView()
                    self.view.isUserInteractionEnabled = true
                }
            }
        case .none:
            break
        }
    }
}
extension HomeListenFight_lesson_4_VC:UICollectionViewDelegate{
}
extension HomeListenFight_lesson_4_VC:UICollectionViewDelegateFlowLayout{
                func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
                                return CGSize.zero
                }
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
        return CGSizeMake(JQ_ScreenW, 20)
    }
}
extension HomeListenFight_lesson_4_VC:UICollectionViewDataSource{
                func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
                                let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "_ListenFight_lesson_4_CCell", for: indexPath) as! ListenFight_lesson_4_CCell
                                cell.backgroundColor = .clear
                                cell.contentView.backgroundColor = .clear
                                cell.indexPath = indexPath
                                cell.playAtIndex {[weak self] index in
                                                guard let weakSelf = self else { return }
                                                weakSelf.viewModel.selectIndex.accept(index)
                                                UIView.animate(withDuration: 0.5) {
                                                                weakSelf.collectionView.snp.remakeConstraints { make in
                                                                                make.left.equalTo(76)
                                                                                make.top.equalTo(weakSelf.view.safeAreaLayoutGuide.snp.top).offset(0)
                                                                                make.width.equalTo(JQ_ScreenW - 164 * 2)
                                                                                make.bottom.equalToSuperview()
                                                                }
                                                                weakSelf.view.layoutIfNeeded()
                                                }completion: { _ in
                                                                weakSelf.setAnswerStackView()
                                                }
                                }
                                let m = filterItems[indexPath.section][indexPath.row]
                                if indexPath == answerIndex{
                                                UIView.animate(withDuration: 0.4, delay: 0, usingSpringWithDamping: 0.4, initialSpringVelocity: 0.4,options: .layoutSubviews) {
                                                                cell.img_state.alpha = 1
                                                                cell.img_state.transform = .init(scaleX: 1.0, y: 1.0)
                                                }
                                                UIView.animate(withDuration: 0.4, delay: 3.0) {
                                                                cell.img_state.alpha = 0
                                                                cell.img_state.transform = .init(scaleX: 0.1, y: 0.1)
                                                }
                                }else{
                                                cell.img_state.alpha = 0
                                                cell.img_state.transform = .init(scaleX: 0.1, y: 0.1)
                                }
                                if indexPath.row == 1{
                                                cell.btn_handle.setImage(UIImage(named: "icon_answer"), for: .normal)
                                                cell.view_handle.isHidden = true
                                }else{
                                                cell.btn_handle.setImage(UIImage(named: "icon_question"), for: .normal)
                                                cell.view_handle.isHidden = true
                                }
                                //问题
                                if filterItems[indexPath.section][indexPath.row].isQuestion == 0{
                                                cell.view_handle.isHidden = true
                                }else{
                                                cell.view_handle.isHidden = false
                                }
                                cell.setModel(m)
                                if answerIndexs.count == 0 && indexPath.section == 1{
                                                cell.view_handle.backgroundColor = .gray.withAlphaComponent(0.5)
                                                cell.view_handle.isEnabled = false
                                }else{
                                                cell.view_handle.backgroundColor = UIColor(hexString: "#41A2EB")
                                                cell.view_handle.isEnabled = true
                                }
                                return cell
                }
                func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
                                let reusableView = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "header", for: indexPath)
                                return reusableView
                }
                func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
                                return filterItems[section].count
                }
                func numberOfSections(in collectionView: UICollectionView) -> Int {
                                return filterItems.count
                }
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "_ListenFight_lesson_4_CCell", for: indexPath) as! ListenFight_lesson_4_CCell
        cell.backgroundColor = .white
        cell.contentView.backgroundColor = .white
        cell.indexPath = indexPath
        cell.playAtIndex {[weak self] index in
            guard let weakSelf = self else { return }
            weakSelf.viewModel.selectIndex.accept(index)
            UIView.animate(withDuration: 0.5) {
                weakSelf.collectionView.snp.remakeConstraints { make in
                    make.left.equalTo(76)
                    make.top.equalTo(weakSelf.view.safeAreaLayoutGuide.snp.top).offset(0)
                    make.width.equalTo(JQ_ScreenW - 164 * 2)
                    make.bottom.equalToSuperview()
                }
                weakSelf.view.layoutIfNeeded()
            }completion: { _ in
                weakSelf.setAnswerStackView()
            }
        }
        let m = filterItems[indexPath.section][indexPath.row]
        if indexPath == answerIndex{
            UIView.animate(withDuration: 0.4, delay: 0, usingSpringWithDamping: 0.4, initialSpringVelocity: 0.4,options: .layoutSubviews) {
                cell.img_state.alpha = 1
                cell.img_state.transform = .init(scaleX: 1.0, y: 1.0)
            }
            UIView.animate(withDuration: 0.4, delay: 3.0) {
                cell.img_state.alpha = 0
                cell.img_state.transform = .init(scaleX: 0.1, y: 0.1)
            }
        }else{
            cell.img_state.alpha = 0
            cell.img_state.transform = .init(scaleX: 0.1, y: 0.1)
        }
        if indexPath.row == 1{
            cell.btn_handle.setImage(UIImage(named: "icon_answer"), for: .normal)
            cell.view_handle.isHidden = true
        }else{
            cell.btn_handle.setImage(UIImage(named: "icon_question"), for: .normal)
            cell.view_handle.isHidden = true
        }
        //问题
        if filterItems[indexPath.section][indexPath.row].isQuestion == 0{
            cell.view_handle.isHidden = true
        }else{
            cell.view_handle.isHidden = false
        }
        cell.setModel(m)
        if answerIndexs.count == 0 && indexPath.section == 1{
            cell.view_handle.backgroundColor = UIColor(hexString: "#D4D2CD")
            cell.view_handle.isEnabled = false
        }else{
            cell.view_handle.backgroundColor = .white
            cell.view_handle.isEnabled = true
        }
        return cell
    }
    func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
        let reusableView = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "header", for: indexPath)
        return reusableView
    }
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return filterItems[section].count
    }
    func numberOfSections(in collectionView: UICollectionView) -> Int {
        return filterItems.count
    }
}
extension HomeListenFight_lesson_4_VC:VoicePlayerDelegate{
                func playComplete() {
                                self.view.isUserInteractionEnabled = true
                                for subV in view.subviews {
                                                if let s = subV as? Lesson_4_AnswerView{
                                                                s.playEnd()
                                                                s.img_play.isHidden = false
                                                                s.img_play.alpha = 1
                                                }
                                }
                                for subV in stackView.arrangedSubviews{
                                                if let s = subV as? Lesson_4_AnswerView{
                                                                s.playEnd()
                                                }
                                }
                                if let indexPath = viewModel.selectIndex.value ,let cell = collectionView.cellForItem(at: indexPath) as? ListenFight_lesson_4_CCell{
                                                cell.playEnd()
                                }
                                //回答完成,下一答题
                                if answerIndexs.count == 2{
                                                voicePlayer.playerEnd()
                                                NotificationCenter.default.post(name: NextLession_Noti, object: nil)
                                                return
                                }
                }
                func playing() {
                                self.view.isUserInteractionEnabled = false
                }
    func playComplete() {
        self.view.isUserInteractionEnabled = true
        for subV in view.subviews {
            if let s = subV as? Lesson_4_AnswerView{
                s.playEnd()
                s.img_play.isHidden = false
                s.img_play.alpha = 1
            }
        }
        let temp = stackView.subviews as! [Lesson_4_AnswerView]
        for subV in temp.reversed() {
            if subV.isplayend == false{
                voicePlayer.playerAt(url: subV.voiceUrl ?? "")
                subV.isPlaying()
                subV.btn_choose.isEnabled = true
                break
            }else{
                subV.playEnd()
            }
        }
        if let indexPath = viewModel.selectIndex.value ,let cell = collectionView.cellForItem(at: indexPath) as? ListenFight_lesson_4_CCell{
            cell.playEnd()
        }
        //回答完成,下一答题
        if answerIndexs.count == 2{
            voicePlayer.playerEnd()
            NotificationCenter.default.post(name: NextLession_Noti, object: nil)
            return
        }
    }
    func playing() {
        self.view.isUserInteractionEnabled = false
    }
}
DolphinEnglishLearnStudent/Moudle/Home/Listen/VC/HomeListenFight_lesson_5_VC.swift
@@ -9,354 +9,352 @@
class HomeListenFight_lesson_5_VC: BaseVC {
                private var viewModel = FightAnswerViewModel()
                private var listenNewModel:ListenNewModel!
                private var page:Int!
                private var answterCount:Int = 0 //回答计数,用于确定角标
                private var playVoiceAt:Int? //播放声音的View
                private var playVoiceRealAt:Int? //播放声音的View -被乱序后,真实Index
                var rootViewModel:HomeListenFightViewModel!
                var teamScheduleModel:TeamScheduleModel?
                private var voicePlayer = VoicePlayer.share()
                private var isListen:Bool = false
    private var viewModel = FightAnswerViewModel()
    private var listenNewModel:ListenNewModel!
    private var page:Int!
    private var answterCount:Int = 0 //回答计数,用于确定角标
    private var playVoiceAt:Int? //播放声音的View
    private var playVoiceRealAt:Int? //播放声音的View -被乱序后,真实Index
    var rootViewModel:HomeListenFightViewModel!
    var teamScheduleModel:TeamScheduleModel?
    private var voicePlayer = VoicePlayer.share()
    private var isListen:Bool = false
    private var playingTags = Set<Int>() //已经播放过的TAG
    private var handleClouse:(()->Void)?
                private lazy var collectionView:UICollectionView = {
                                let flowLayout = UICollectionViewFlowLayout()
                                let w = (JQ_ScreenW - 189 * 2 - 18) / 2.0
                                flowLayout.itemSize = CGSize(width: w, height: w * 0.70)
                                flowLayout.minimumInteritemSpacing = 15
                                flowLayout.minimumLineSpacing = 15
                                flowLayout.scrollDirection = .vertical
                                let collection = UICollectionView(frame: .zero, collectionViewLayout: flowLayout)
                                collection.register(UINib(nibName: "ListenFight_lesson_1_CCell", bundle: nil), forCellWithReuseIdentifier: "_ListenFight_lesson_1_CCell")
                                collection.register(UICollectionReusableView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "header")
                                collection.isScrollEnabled = false
                                return collection
                }()
    private lazy var collectionView:UICollectionView = {
        let flowLayout = UICollectionViewFlowLayout()
        let w = (JQ_ScreenW - 189 * 2 - 18) / 2.0
        flowLayout.itemSize = CGSize(width: w, height: w * 0.70)
        flowLayout.minimumInteritemSpacing = 15
        flowLayout.minimumLineSpacing = 15
        flowLayout.scrollDirection = .vertical
        let collection = UICollectionView(frame: .zero, collectionViewLayout: flowLayout)
        collection.register(UINib(nibName: "ListenFight_lesson_1_CCell", bundle: nil), forCellWithReuseIdentifier: "_ListenFight_lesson_1_CCell")
        collection.register(UICollectionReusableView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "header")
        collection.isScrollEnabled = false
        return collection
    }()
                private lazy var stackView:UIStackView = {
                                let sta = UIStackView()
                                sta.spacing = 89
                                sta.distribution = .equalSpacing
                                sta.axis = .horizontal
                                return sta
                }()
                private lazy var label_hint:UILabel = {
                                let label = UILabel()
                                label.font = .systemFont(ofSize: 18, weight: .medium)
                                label.textColor = .black
                                label.text = "语音对应内容"
                                label.textAlignment = .center
                                return label
                }()
                required init(page:Int,listenNewModel:ListenNewModel){
                                super.init(nibName: nil, bundle: nil)
                                self.page = page
                                self.listenNewModel = listenNewModel
//                                self.listen1Model.subjectList.shuffle()
                }
                required init?(coder: NSCoder) {
                                fatalError("init(coder:) has not been implemented")
                }
    private lazy var stackView:UIStackView = {
        let sta = UIStackView()
        sta.spacing = 49
        sta.distribution = .equalSpacing
        sta.axis = .horizontal
        return sta
    }()
                override func viewDidLoad() {
                                super.viewDidLoad()
    required init(page:Int,listenNewModel:ListenNewModel){
        super.init(nibName: nil, bundle: nil)
        self.page = page
        self.listenNewModel = listenNewModel
        //                                self.listen1Model.subjectList.shuffle()
    }
                                collectionView.reloadData()
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
                                //回传记录,始终保持答题进度
                                if let team = teamScheduleModel{
                                                for teamId in team.teamIds{
                                                                for v in listenNewModel.subjectList[page]{
                                                                                if team.topicIds.contains(v.id){
                                                                                                rootViewModel.insertCorrectAnswer(teamId: "\(teamId)", answerId: v.id)
                                                                                }
                                                                }
                                                }
                                }
                }
    override func viewDidLoad() {
        super.viewDidLoad()
        collectionView.reloadData()
                override func viewDidAppear(_ animated: Bool) {
                                super.viewDidAppear(animated)
                                voicePlayer.delegate = self
                }
        //回传记录,始终保持答题进度
        if let team = teamScheduleModel{
            for teamId in team.teamIds{
                for v in listenNewModel.subjectList[page]{
                    if team.topicIds.contains(v.id){
                        rootViewModel.insertCorrectAnswer(teamId: "\(teamId)", answerId: v.id)
                    }
                }
            }
        }
    }
                override func viewDidDisappear(_ animated: Bool) {
                                super.viewDidDisappear(animated)
                                voicePlayer.delegate = nil
                                VoicePlayer.share().playerInterrupt()
                }
                override func setUI() {
                                super.setUI()
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        voicePlayer.delegate = self
    }
                                view.addSubview(stackView)
                                stackView.snp.makeConstraints { make in
                                                make.top.equalTo(self.view.safeAreaLayoutGuide.snp.top).offset(19)
                                                make.centerX.equalToSuperview()
                                                make.height.equalTo(52)
                                }
    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        voicePlayer.delegate = nil
        VoicePlayer.share().playerInterrupt()
    }
                                view.addSubview(label_hint)
                                label_hint.snp.makeConstraints { make in
                                                make.left.right.equalToSuperview()
                                                make.top.equalTo(stackView.snp.bottom).offset(6)
                                                make.height.equalTo(0)
                                }
    override func setUI() {
        super.setUI()
                                collectionView.delegate = self
                                collectionView.dataSource = self
                                collectionView.showsVerticalScrollIndicator = false
                                collectionView.backgroundColor = .clear
                                view.addSubview(collectionView)
                                collectionView.snp.makeConstraints { make in
                                                make.top.equalTo(self.label_hint.snp.bottom).offset(11)
                                                make.left.equalTo(189)
                                                make.width.equalTo(JQ_ScreenW - 189 * 2)
                                                make.bottom.equalToSuperview()
                                }
        view.addSubview(stackView)
        stackView.snp.makeConstraints { make in
            make.top.equalTo(self.view.safeAreaLayoutGuide.snp.top).offset(19)
            make.centerX.equalToSuperview()
            make.height.equalTo(52)
        }
                                showHintText(false)
        collectionView.delegate = self
        collectionView.dataSource = self
        collectionView.showsVerticalScrollIndicator = false
        collectionView.backgroundColor = .clear
        view.addSubview(collectionView)
        collectionView.snp.makeConstraints { make in
            make.top.equalToSuperview().offset(91)
            make.left.equalTo(189)
            make.width.equalTo(JQ_ScreenW - 189 * 2)
            make.bottom.equalToSuperview()
        }
                                setAnswerStackView()
                }
        setAnswerStackView()
    }
                override func viewDidLayoutSubviews() {
                                super.viewDidLayoutSubviews()
                                let flowLayout = collectionView.collectionViewLayout as! UICollectionViewFlowLayout
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        let flowLayout = collectionView.collectionViewLayout as! UICollectionViewFlowLayout
                                let w = (collectionView.size.width - flowLayout.minimumLineSpacing) / 2
                                let h = (collectionView.size.height - flowLayout.minimumInteritemSpacing) / 2.0
                                if flowLayout.itemSize.width != w || flowLayout.itemSize.height != h{
                                                flowLayout.itemSize = CGSize(width: w, height: h)
                                                collectionView.reloadData()
                                }
                }
        let w = (collectionView.size.width - flowLayout.minimumLineSpacing) / 2
        let h = (collectionView.size.height - flowLayout.minimumInteritemSpacing) / 2.0
                override func setRx() {
        if flowLayout.itemSize.width != w || flowLayout.itemSize.height != h{
            flowLayout.itemSize = CGSize(width: w, height: h)
            collectionView.reloadData()
        }
    }
                }
    override func setRx() {
        NotificationCenter.default.rx.notification(ResetLession_Noti).take(until: self.rx.deallocated).subscribe(onNext: {[weak self]_ in
            self?.restore()
            self?.viewDidLoad()
        }).disposed(by: disposeBag)
    }
                func restore(){
                                viewModel.answerType.accept(.none)
                                answterCount  = 0
                                playVoiceAt = nil
                                playVoiceRealAt = nil
    func restore(){
        viewModel.answerType.accept(.none)
        answterCount  = 0
        playVoiceAt = nil
        playVoiceRealAt = nil
                                for subV in view.subviews{
                                                if subV is VoiceHandleView{
                                                                subV.removeFromSuperview()
                                                }
                                }
                                setUI()
                                collectionView.reloadData()
                }
        for subV in view.subviews{
            if subV is VoiceHandleView{
                subV.removeFromSuperview()
            }
        }
        setUI()
        collectionView.reloadData()
    }
                private func setAnswerStackView(){
    private func setAnswerStackView(){
                                for subView in stackView.arrangedSubviews{
                                                subView.removeFromSuperview()
                                }
        for subView in stackView.arrangedSubviews{
            subView.removeFromSuperview()
        }
                                if !view.subviews.contains(stackView){
                                                view.addSubview(stackView)
                                                stackView.snp.makeConstraints { make in
                                                                make.right.equalToSuperview().offset(-14)
                                                                make.centerY.equalToSuperview()
                                                }
                                }
        if !view.subviews.contains(stackView){
            view.addSubview(stackView)
            stackView.snp.makeConstraints { make in
                make.right.equalToSuperview().offset(-14)
                make.centerY.equalToSuperview()
            }
        }
                                var tempArray = [VoiceHandleView]()
                                for (i,value) in listenNewModel.subjectList[page].enumerated(){
                                                let answerView = VoiceHandleView()
                                                answerView.listenType = .lesson5
                                                answerView.tag = 1000+i
                                                answerView.playAt {[weak self] index in
                                                                print("--->\(index)--\(answerView.frame.origin.x / 248)")
                                                                self?.playVoiceRealAt = Int(answerView.frame.origin.x) / 248
                                                                self?.playVoiceAt = index - 1000
                                                                self?.showHintText(true)
                                                }
                                                answerView.playUrl = value.correct
                                                answerView.snp.makeConstraints { make in
                                                                make.width.equalTo(159)
                                                                make.height.equalTo(52)
                                                }
        var tempArray = [VoiceHandleView]()
        for (i,value) in listenNewModel.subjectList[page].enumerated(){
            let answerView = VoiceHandleView()
            answerView.listenType = .lesson5
            answerView.tag = 1000+i
            answerView.playAt {[weak self] index in
                self?.playVoiceRealAt = Int(answerView.frame.origin.x) / 248
                self?.playVoiceAt = index - 1000
            }
            answerView.playUrl = value.correct
            answerView.snp.makeConstraints { make in
                make.width.equalTo(221)
                make.height.equalTo(40)
            }
                                                UIView.animate(withDuration: 0.05 + Double(i)) {
                                                                answerView.alpha = 1
                                                }
                                                tempArray.append(answerView)
                                }
                                tempArray.shuffle()
                                stackView.addArrangedSubviews(tempArray)
                }
            UIView.animate(withDuration: 0.05 + Double(i)) {
                answerView.alpha = 1
            }
            tempArray.append(answerView)
        }
        tempArray.shuffle()
                private func showHintText(_ state:Bool){
                                if let at = playVoiceAt{
                                                label_hint.text = listenNewModel.subjectList[page][at].name
                                                UIView.animate(withDuration: 0.5) {
                                                                if state{
                                                                                self.label_hint.snp.remakeConstraints { make in
                                                                                                make.left.right.equalToSuperview()
                                                                                                make.top.equalTo(self.stackView.snp.bottom).offset(6)
                                                                                                make.height.equalTo(25)
                                                                                }
                                                                }else{
                                                                                self.label_hint.snp.remakeConstraints { make in
                                                                                                make.left.right.equalToSuperview()
                                                                                                make.top.equalTo(self.stackView.snp.bottom).offset(6)
                                                                                                make.height.equalTo(0)
                                                                                }
                                                                }
                                                                self.view.layoutIfNeeded()
                                                }
                                }
                }
        DispatchQueue.main.asyncAfter(deadline: .now()+1.0) {
            //自动播放第一条
            tempArray.first?.playingAction()
            self.playingTags.insert(tempArray.first?.tag ?? 0)
        }
        stackView.addArrangedSubviews(tempArray)
    }
    func handleClouseAction(clouse:@escaping ()->Void){
        self.handleClouse = clouse
    }
}
extension HomeListenFight_lesson_5_VC:UICollectionViewDelegate{
                func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
                                if isListen == false{
                                                alertError(msg: "请先听题");return
                                }
                                isListen = false
        if isListen == false{
            alertError(msg: "请先听题");return
        }
        isListen = false
                                viewModel.selectIndex.accept(indexPath)
                                guard playVoiceAt != nil else {return}
        viewModel.selectIndex.accept(indexPath)
        guard playVoiceAt != nil else {return}
                                let answer = listenNewModel.subjectList[page][playVoiceAt!]
                                let selectAnswer = listenNewModel.subjectList[page][indexPath.row]
        let answer = listenNewModel.subjectList[page][playVoiceAt!]
        let selectAnswer = listenNewModel.subjectList[page][indexPath.row]
                                var answerType:Fight_lessonType = .none
                                if answer.id == selectAnswer.id{
                                                answerType = .success
                                                voicePlayer.playSuccessVoice()
                                                let teamId = listenNewModel.data?.id.components(separatedBy: ",")[page]
                                                rootViewModel.insertCorrectAnswer(teamId: teamId, answerId: selectAnswer.id)
                                }else{
                                                answerType = .fail
                                                voicePlayer.playFailVoice()
                                }
        var answerType:Fight_lessonType = .none
        if answer.id == selectAnswer.id{
            answerType = .success
            voicePlayer.playSuccessVoice()
            let teamId = listenNewModel.data?.id.components(separatedBy: ",")[page]
            rootViewModel.insertCorrectAnswer(teamId: teamId, answerId: selectAnswer.id)
                                let tempSubV = stackView.arrangedSubviews[self.playVoiceRealAt!] as! VoiceHandleView
                                label_hint.text = ""
                                switch answerType {
                                                case .success:
                                                                answterCount += 1
                                                                rootViewModel.correctNum += 1
                                                                viewModel.answerType.accept(.success)
            let data = listenNewModel.list[page]
            data.status = 2
            Services.answerQuestion(id: data.id, status: 2).subscribe(onNext: {[weak self]_ in
                self?.handleClouse?()
            }).disposed(by: disposeBag)
        }else{
            answerType = .fail
            voicePlayer.playFailVoice()
                                                                let copyView = tempSubV.copyView()
                                                                copyView.listenType = .lesson5
                                                                copyView.jq_cornerRadius = 0
                                                                copyView.playUrl = selectAnswer.correct
                                                                let newRect = tempSubV.convert(tempSubV.bounds, to: self.view)
                                                                copyView.frame = CGRect(origin: newRect.origin, size: CGSize(width: 159, height: 52))
                                                                self.view.addSubview(copyView)
                                                                tempSubV.alpha = 0
            let data = listenNewModel.list[page]
            data.status = 3
            Services.answerQuestion(id: data.id, status: 3).subscribe(onNext: {[weak self]_ in
                self?.handleClouse?()
            }).disposed(by: disposeBag)
        }
                                                                DispatchQueue.main.asyncAfter(deadline: .now()+0.4) {
        let tempSubV = stackView.arrangedSubviews[self.playVoiceRealAt!] as! VoiceHandleView
                                                                                //获取Cell的顶部试图
                                                                                let flowLayout = self.collectionView.collectionViewLayout as! UICollectionViewFlowLayout
        switch answerType {
        case .success:
            answterCount += 1
            rootViewModel.correctNum += 1
            viewModel.answerType.accept(.success)
                                                                                if let cell = self.collectionView.dequeueReusableCell(withReuseIdentifier: "_ListenFight_lesson_1_CCell", for: indexPath) as? ListenFight_lesson_1_CCell{
                                                                                                var newRect1 = cell.convert(cell.bounds, to: self.collectionView)
                                                                                                newRect1.origin.x += (collectionView.frame.origin.x + 5)
                                                                                                newRect1.origin.y += 94 + 24
            let copyView = tempSubV.copyView()
            copyView.isPlayed = true
            copyView.listenType = .lesson5
            copyView.jq_cornerRadius = 8
            copyView.playUrl = selectAnswer.correct
            let newRect = tempSubV.convert(tempSubV.bounds, to: self.view)
            copyView.frame = CGRect(origin: newRect.origin, size: CGSize(width: 221, height: 52))
            self.view.addSubview(copyView)
            tempSubV.alpha = 0
                                                                                                UIView.animateKeyframes(withDuration: 0.4, delay: 0,options: .calculationModeLinear) {
                                                                                                                copyView.frame = CGRect(origin: newRect1.origin, size: CGSize(width: flowLayout.itemSize.width - 10 , height: 40))
            DispatchQueue.main.asyncAfter(deadline: .now()+0.4) {
                                                                                                }completion: { _ in
                                                                                                                copyView.playingAction()
                                                                                                                self.playVoiceRealAt = nil
                                                                                                                self.playVoiceAt = nil
                                                                                                                self.collectionView.reloadData()
                                                                                                }
                                                                                }
                                                                }
                //获取Cell的顶部试图
                let flowLayout = self.collectionView.collectionViewLayout as! UICollectionViewFlowLayout
                if let cell = self.collectionView.cellForItem(at: indexPath) as? ListenFight_lesson_1_CCell{
                    var newRect1 = cell.convert(cell.bounds, to: self.collectionView)
                    newRect1.origin.x += (collectionView.frame.origin.x)
                    newRect1.origin.y += 94
                                                case .fail:
                                                                viewModel.answerType.accept(.fail)
                                                                rootViewModel.errorNum += 1
                                                                collectionView.reloadData()
                                                case .none:
                                                                break
                                }
                }
                    UIView.animateKeyframes(withDuration: 0.4, delay: 0,options: .calculationModeLinear) {
                        copyView.frame = CGRect(origin: newRect1.origin, size: CGSize(width: flowLayout.itemSize.width , height: 40))
                    }completion: { _ in
                        //                        copyView.playingAction()
                        self.playVoiceRealAt = nil
                        self.playVoiceAt = nil
                        self.collectionView.reloadData()
                        //播放下一个
                        DispatchQueue.main.asyncAfter(deadline: .now()+1) {
                            for v in self.stackView.subviews as! [VoiceHandleView]{
                                if !self.playingTags.contains(v.tag){
                                    v.playingAction()
                                    self.playingTags.insert(v.tag)
                                    break
                                }
                            }
                        }
                    }
                }
            }
        case .fail:
            viewModel.answerType.accept(.fail)
            rootViewModel.errorNum += 1
            collectionView.reloadData()
        case .none:
            break
        }
    }
}
extension HomeListenFight_lesson_5_VC:UICollectionViewDelegateFlowLayout{
                func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
                                return CGSize.zero
                }
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
        return CGSize.zero
    }
}
extension HomeListenFight_lesson_5_VC:UICollectionViewDataSource{
                func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
                                let model = listenNewModel.subjectList[page][indexPath.row]
                                let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "_ListenFight_lesson_1_CCell", for: indexPath) as! ListenFight_lesson_1_CCell
                                cell.jq_addShadows(shadowColor: .black.withAlphaComponent(0.31), corner: 8, radius: 3, offset: CGSize(width: 0, height: 1), opacity: 1)
                                if viewModel.selectIndex.value == indexPath{
                                                cell.setState(state: viewModel.answerType.value)
                                }else{
                                                cell.setState(state: .none)
                                }
                                cell.setListen1SubModel(model)
                                return cell
                }
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let model = listenNewModel.subjectList[page][indexPath.row]
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "_ListenFight_lesson_1_CCell", for: indexPath) as! ListenFight_lesson_1_CCell
        cell.jq_addShadows(shadowColor: .black.withAlphaComponent(0.31), corner: 8, radius: 3, offset: CGSize(width: 0, height: 1), opacity: 1)
        if viewModel.selectIndex.value == indexPath{
            cell.setState(state: viewModel.answerType.value)
        }else{
            cell.setState(state: .none)
        }
        cell.setListen1SubModel(model)
        return cell
    }
                func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
                                let reusableView = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "header", for: indexPath)
                                return reusableView
    func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
        let reusableView = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "header", for: indexPath)
        return reusableView
                }
    }
                func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
                                return listenNewModel.subjectList[page].count
                }
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return listenNewModel.subjectList[page].count
    }
                func numberOfSections(in collectionView: UICollectionView) -> Int {
                                return 1
                }
    func numberOfSections(in collectionView: UICollectionView) -> Int {
        return 1
    }
}
extension HomeListenFight_lesson_5_VC:VoicePlayerDelegate{
                func playComplete() {
                                view.isUserInteractionEnabled = true
                                isListen = true
                                for subV in stackView.arrangedSubviews as! [VoiceHandleView]{
                                                subV.resetView()
                                }
    func playComplete() {
        view.isUserInteractionEnabled = true
        isListen = true
        for subV in stackView.arrangedSubviews as! [VoiceHandleView]{
            subV.resetView()
        }
                                if viewModel.answerType.value == .success{
                                                let v = rootViewModel.answerCount.value + 1
                                                rootViewModel.answerCount.accept(v)
                                                viewModel.answerType.accept(.none)
                                }
        if viewModel.answerType.value == .success{
            let v = rootViewModel.answerCount.value + 1
            rootViewModel.answerCount.accept(v)
            viewModel.answerType.accept(.none)
        }
                                if self.answterCount >= 4{
//                                                DispatchQueue.main.asyncAfter(delay: 3.0) {
                                                                self.voicePlayer.playerEnd()
                                                                NotificationCenter.default.post(name: NextLession_Noti, object: nil)
//                                                }
                                }
                }
                func playing() {
                                view.isUserInteractionEnabled = false
                }
        if self.answterCount >= 4{
            //                                                DispatchQueue.main.asyncAfter(delay: 3.0) {
            self.voicePlayer.playerEnd()
            NotificationCenter.default.post(name: NextLession_Noti, object: nil)
            //                                                }
        }
    }
    func playing() {
        view.isUserInteractionEnabled = false
    }
}
DolphinEnglishLearnStudent/Moudle/Home/Listen/VC/HomeListenGame_1_VC.swift
@@ -11,384 +11,384 @@
class HomeListenGame_1_VC: BaseVC {
                private var viewModel = FightAnswerViewModel()
                private var listen1Model:Listen1Model!
    private var viewModel = FightAnswerViewModel()
    private var listen1Model:Listen1Model!
                var rootViewModel:HomeListenFightViewModel!
    var rootViewModel:HomeListenFightViewModel!
                private var totalCount:Int = 1 //游戏的总数量
    private var totalCount:Int = 1 //游戏的总数量
                private lazy var label_class:UILabel = {
                                let label = UILabel()
                                label.textColor = .white
                                label.text = "1"
                                label.textAlignment = .center
                                label.font = UIFont.init(name: "Impact", size: 21)
                                return label
    private lazy var label_class:UILabel = {
        let label = UILabel()
        label.textColor = .white
        label.text = "1"
        label.textAlignment = .center
        label.font = UIFont.init(name: "Impact", size: 21)
        return label
                }()
    }()
                private let view_class_title = UIView()
    private let view_class_title = UIView()
                private lazy var label_hint:UILabel = {
                                let label = UILabel()
                                label.textColor = UIColor(hexStr: "#EE1111")
                                label.text = "请在10s内选择答案!"
                                label.textAlignment = .center
                                label.isHidden = true
                                label.font = .systemFont(ofSize: 14, weight: .medium)
                                return label
                }()
    private lazy var label_hint:UILabel = {
        let label = UILabel()
        label.textColor = UIColor(hexStr: "#EE1111")
        label.text = "请在10s内选择答案!"
        label.textAlignment = .center
        label.isHidden = true
        label.font = .systemFont(ofSize: 14, weight: .medium)
        return label
    }()
                private lazy var view_studyHandleView:VoiceHandleView = {
                                let studyHandleView = VoiceHandleView()
                                return studyHandleView
                }()
    private lazy var view_studyHandleView:VoiceHandleView = {
        let studyHandleView = VoiceHandleView()
        return studyHandleView
    }()
                private lazy var collectionView:UICollectionView = {
                                let flowLayout = UICollectionViewFlowLayout()
                                flowLayout.minimumInteritemSpacing = 3
                                flowLayout.minimumLineSpacing = 3
                                flowLayout.scrollDirection = .vertical
                                let collection = UICollectionView(frame: .zero, collectionViewLayout: flowLayout)
                                collection.contentInset = UIEdgeInsets(top: 0, left: 35, bottom: 0, right: 35)
                                collection.register(UINib(nibName: "ListenFight_Game_CCell", bundle: nil), forCellWithReuseIdentifier: "_ListenFight_Game_CCell")
                                collection.isScrollEnabled = false
                                return collection
                }()
    private lazy var collectionView:UICollectionView = {
        let flowLayout = UICollectionViewFlowLayout()
        flowLayout.minimumInteritemSpacing = 3
        flowLayout.minimumLineSpacing = 3
        flowLayout.scrollDirection = .vertical
        let collection = UICollectionView(frame: .zero, collectionViewLayout: flowLayout)
        collection.contentInset = UIEdgeInsets(top: 0, left: 35, bottom: 0, right: 35)
        collection.register(UINib(nibName: "ListenFight_Game_CCell", bundle: nil), forCellWithReuseIdentifier: "_ListenFight_Game_CCell")
        collection.isScrollEnabled = false
        return collection
    }()
                private var timer:Timer?
                private var times:Int = 10
                private var voicePlayer = VoicePlayer.share()
    private var timer:Timer?
    private var times:Int = 10
    private var voicePlayer = VoicePlayer.share()
                private var answerSet = Set<Listen1SubModel>()
                private var currentAnswer:Listen1SubModel?{
                                didSet{
                                                if let v = currentAnswer{
                                                                view_studyHandleView.playUrl = v.correct
                                                                voicePlayer.playerAt(url: v.correct)
                                                                viewModel.answerType.accept(.none)
                                                }
                                }
                }
    private var answerSet = Set<Listen1SubModel>()
    private var currentAnswer:Listen1SubModel?{
        didSet{
            if let v = currentAnswer{
                view_studyHandleView.playUrl = v.correct
                voicePlayer.playerAt(url: v.correct)
                viewModel.answerType.accept(.none)
            }
        }
    }
                required init(listen1Model:Listen1Model){
                                super.init(nibName: nil, bundle: nil)
                                self.listen1Model = listen1Model
                }
    required init(listen1Model:Listen1Model){
        super.init(nibName: nil, bundle: nil)
        self.listen1Model = listen1Model
    }
                required init?(coder: NSCoder) {
                                fatalError("init(coder:) has not been implemented")
                }
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
                override func viewDidAppear(_ animated: Bool) {
                                super.viewDidAppear(animated)
                                voicePlayer.delegate = self
                }
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        voicePlayer.delegate = self
    }
                override func viewDidDisappear(_ animated: Bool) {
                                super.viewDidDisappear(animated)
                                timer?.invalidate()
                                timer = nil
                                voicePlayer.delegate = nil
                                voicePlayer.playerInterrupt()
                }
    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        timer?.invalidate()
        timer = nil
        voicePlayer.delegate = nil
        voicePlayer.playerInterrupt()
    }
    override func viewDidLoad() {
        super.viewDidLoad()
                                if  listen1Model != nil{
                                                times = (listen1Model?.data?.time ?? 10) + 1
                                                collectionView.reloadData()
                                                label_hint.isHidden = false
                                                label_hint.text = "准备听题"
        if  listen1Model != nil{
            times = (listen1Model?.data?.time ?? 10) + 1
            collectionView.reloadData()
            label_hint.isHidden = false
            label_hint.text = "准备听题"
                                                for v in listen1Model?.subjectList ?? []{
                                                                answerSet.insert(v)
                                                }
            for v in listen1Model?.subjectList ?? []{
                answerSet.insert(v)
            }
                                                print("--->开始答题:剩余:\(answerSet.count)")
            print("--->开始答题:剩余:\(answerSet.count)")
                                                if listen1Model.data?.playNow  == true{
                                                                self.currentAnswer = self.answerSet.randomElement() //随机
                                                                if self.timer == nil{self.startTimer()}
                                                }else{
                                                                DispatchQueue.main.asyncAfter(deadline: .now()+2) {
                                                                                self.currentAnswer = self.answerSet.randomElement() //随机
                                                                                if self.timer == nil{self.startTimer()}
                                                                }
                                                }
                                }
            if listen1Model.data?.playNow  == true{
                self.currentAnswer = self.answerSet.randomElement() //随机
                if self.timer == nil{self.startTimer()}
            }else{
                DispatchQueue.main.asyncAfter(deadline: .now()+2) {
                    self.currentAnswer = self.answerSet.randomElement() //随机
                    if self.timer == nil{self.startTimer()}
                }
            }
        }
    }
                override func setUI() {
                                super.setUI()
    override func setUI() {
        super.setUI()
                                view_class_title.jq_cornerRadius = 16
                                view_class_title.backgroundColor = UIColor(hexStr: "#FBCF0F")
                                view.addSubview(view_class_title)
                                view_class_title.snp.makeConstraints { make in
                                                make.top.equalTo(self.view.safeAreaLayoutGuide.snp.top).offset(40)
                                                make.left.equalToSuperview().offset(40)
                                                make.height.equalTo(32)
                                                make.width.greaterThanOrEqualTo(32)
                                }
        view_class_title.jq_cornerRadius = 16
        view_class_title.backgroundColor = UIColor(hexStr: "#FBCF0F")
        view.addSubview(view_class_title)
        view_class_title.snp.makeConstraints { make in
            make.top.equalTo(self.view.safeAreaLayoutGuide.snp.top).offset(40)
            make.left.equalToSuperview().offset(40)
            make.height.equalTo(32)
            make.width.greaterThanOrEqualTo(32)
        }
                                view_class_title.addSubview(label_class)
                                label_class.text = "\(totalCount)"
                                label_class.snp.makeConstraints { make in
                                                make.left.equalTo(11)
                                                make.right.equalTo(-12)
                                                make.centerY.equalToSuperview()
                                }
        view_class_title.addSubview(label_class)
        label_class.text = "\(totalCount)"
        label_class.snp.makeConstraints { make in
            make.left.equalTo(11)
            make.right.equalTo(-12)
            make.centerY.equalToSuperview()
        }
                                view.addSubview(view_studyHandleView)
                                view_studyHandleView.snp.makeConstraints { make in
                                                make.left.equalTo(view_class_title.snp.right).offset(12)
                                                make.centerY.equalTo(view_class_title)
                                                make.width.equalTo(159)
                                                make.height.equalTo(52)
                                }
        view.addSubview(view_studyHandleView)
        view_studyHandleView.snp.makeConstraints { make in
            make.left.equalTo(view_class_title.snp.right).offset(12)
            make.centerY.equalTo(view_class_title)
            make.width.equalTo(159)
            make.height.equalTo(52)
        }
                                view.addSubview(label_hint)
                                label_hint.snp.makeConstraints { make in
                                                make.left.equalTo(view_studyHandleView.snp.right).offset(23)
                                                make.centerY.equalTo(view_class_title)
                                                make.height.equalTo(20)
                                }
        view.addSubview(label_hint)
        label_hint.snp.makeConstraints { make in
            make.left.equalTo(view_studyHandleView.snp.right).offset(23)
            make.centerY.equalTo(view_class_title)
            make.height.equalTo(20)
        }
                                collectionView.delegate = self
                                collectionView.dataSource = self
                                collectionView.showsVerticalScrollIndicator = false
                                collectionView.jq_addShadows(shadowColor: UIColor.black.withAlphaComponent(0.1), corner: 8, radius: 10, offset: CGSize(width: 0, height: 2), opacity: 1)
                                collectionView.backgroundColor = .clear
                                view.addSubview(collectionView)
                                collectionView.snp.makeConstraints { make in
                                                make.top.equalTo(self.view.safeAreaLayoutGuide.snp.top).offset(99)
                                                make.left.right.equalToSuperview()
                                                make.bottom.equalToSuperview()
                                }
        collectionView.delegate = self
        collectionView.dataSource = self
        collectionView.showsVerticalScrollIndicator = false
        collectionView.jq_addShadows(shadowColor: UIColor.black.withAlphaComponent(0.1), corner: 8, radius: 10, offset: CGSize(width: 0, height: 2), opacity: 1)
        collectionView.backgroundColor = .clear
        view.addSubview(collectionView)
        collectionView.snp.makeConstraints { make in
            make.top.equalTo(self.view.safeAreaLayoutGuide.snp.top).offset(99)
            make.left.right.equalToSuperview()
            make.bottom.equalToSuperview()
        }
                                view.layoutIfNeeded()
                }
        view.layoutIfNeeded()
    }
                override func viewDidLayoutSubviews() {
                                super.viewDidLayoutSubviews()
                                if let res = Array<Any>.CalmulateCell(listen1Model.subjectList.count){
                                                let layout = collectionView.collectionViewLayout as! UICollectionViewFlowLayout
                                                let w = (JQ_ScreenW - (collectionView.contentInset.left * 2) - (CGFloat(res.0) - 1.0) * layout.minimumInteritemSpacing) / Double(res.0)
                                                let h = (collectionView.frame.height - (layout.minimumLineSpacing * (Double(res.1) - 1.0))) / Double(res.1)
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        if let res = Array<Any>.CalmulateCell(listen1Model.subjectList.count){
            let layout = collectionView.collectionViewLayout as! UICollectionViewFlowLayout
            let w = (JQ_ScreenW - (collectionView.contentInset.left * 2) - (CGFloat(res.0) - 1.0) * layout.minimumInteritemSpacing) / Double(res.0)
            let h = (collectionView.frame.height - (layout.minimumLineSpacing * (Double(res.1) - 1.0))) / Double(res.1)
                                                if layout.itemSize != CGSize(width: w, height: h){
                                                                layout.itemSize = CGSize(width: w, height: h)
                                                                collectionView.reloadData()
                                                }
                                }
                }
            if layout.itemSize != CGSize(width: w, height: h){
                layout.itemSize = CGSize(width: w, height: h)
                collectionView.reloadData()
            }
        }
    }
                func startTimer(){
                                if timer == nil{
                                                timer = Timer(timeInterval: 1.0, target: self, selector: #selector(runloopTime), userInfo: nil, repeats: true)
                                }
                                timer?.fire()
                                RunLoop.current.add(timer!, forMode: .common)
                }
    func startTimer(){
        if timer == nil{
            timer = Timer(timeInterval: 1.0, target: self, selector: #selector(runloopTime), userInfo: nil, repeats: true)
        }
        timer?.fire()
        RunLoop.current.add(timer!, forMode: .common)
    }
                @objc private func runloopTime(){
                                print("进入。。。")
                                times -= 1
                                label_hint.text = "请在\(max(times,1))s内选择答案!"
    @objc private func runloopTime(){
        print("进入。。。")
        times -= 1
        label_hint.text = "请在\(max(times,1))s内选择答案!"
                                if times == 0{
                                                timer?.fireDate = .distantFuture
                                                if let c = currentAnswer{
                                                                answerSet.remove(c)
                                                }
                                                currentAnswer = answerSet.randomElement() //随机
                                                times = (listen1Model?.data?.time ?? 0) + 1
                                                timer?.fireDate = .distantPast
                                                totalCount += 1
                                                rootViewModel.errorNum += 1
                                                label_class.text = "\(totalCount)"
                                }
                                //答题完成
                                if self.answerSet.count == 0{
                                                timer?.invalidate()
                                                completeQuestion()
                                }
                }
        if times == 0{
            timer?.fireDate = .distantFuture
            if let c = currentAnswer{
                answerSet.remove(c)
            }
            currentAnswer = answerSet.randomElement() //随机
            times = (listen1Model?.data?.time ?? 0) + 1
            timer?.fireDate = .distantPast
            totalCount += 1
            rootViewModel.errorNum += 1
            label_class.text = "\(totalCount)"
        }
        //答题完成
        if self.answerSet.count == 0{
            timer?.invalidate()
            completeQuestion()
        }
    }
                private func answerQuestion(){
                                view.layoutIfNeeded()
                                view.isUserInteractionEnabled = false
                                guard let row = viewModel.selectIndex.value?.row else { alertError(msg: "请选择");return  }
    private func answerQuestion(){
        view.layoutIfNeeded()
        view.isUserInteractionEnabled = false
        guard let row = viewModel.selectIndex.value?.row else { alertError(msg: "请选择");return  }
                                var answerType:Fight_lessonType = .none
        var answerType:Fight_lessonType = .none
                                if currentAnswer?.id == listen1Model?.subjectList[row].id{
                                                answerType = .success
                                                voicePlayer.playSuccessVoice()
                                }else{
                                                answerType = .fail
                                                voicePlayer.playFailVoice()
                                }
                                switch answerType {
                                                case .success:
                                                                timer?.fireDate = .distantFuture
                                                                viewModel.answerType.accept(.success)
                                                                collectionView.reloadData()
                                                                if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "_ListenFight_Game_CCell", for: viewModel.selectIndex.value!) as? ListenFight_Game_CCell{
                                                                                let newRect = cell.contentView.convert(cell.bounds, from: self.collectionView)
                                                                                let x = abs(newRect.origin.x) + self.collectionView.contentInset.left + 5
                                                                                let y = abs(newRect.origin.y) + 99 + 5
                                                                                let layout = self.collectionView.collectionViewLayout as! UICollectionViewFlowLayout
                                                                                let copyView = view_studyHandleView.copyView()
                                                                                copyView.playBtn.isEnabled = false
                                                                                view.addSubview(copyView)
        if currentAnswer?.id == listen1Model?.subjectList[row].id{
            answerType = .success
            voicePlayer.playSuccessVoice()
        }else{
            answerType = .fail
            voicePlayer.playFailVoice()
        }
        switch answerType {
        case .success:
            timer?.fireDate = .distantFuture
            viewModel.answerType.accept(.success)
            collectionView.reloadData()
            if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "_ListenFight_Game_CCell", for: viewModel.selectIndex.value!) as? ListenFight_Game_CCell{
                let newRect = cell.contentView.convert(cell.bounds, from: self.collectionView)
                let x = abs(newRect.origin.x) + self.collectionView.contentInset.left + 5
                let y = abs(newRect.origin.y) + 99 + 5
                let layout = self.collectionView.collectionViewLayout as! UICollectionViewFlowLayout
                let copyView = view_studyHandleView.copyView()
                copyView.playBtn.isEnabled = false
                view.addSubview(copyView)
                                                                                UIView.animate(withDuration: 0.5) {
                                                                                                copyView.frame = CGRect(x: x, y: y, width: layout.itemSize.width - 10, height: 40)
                                                                                } completion: { _ in
//                                                                                                DispatchQueue.main.asyncAfter(deadline: .now()+0.5) {
//                                                                                                                self.voicePlayer.playerAt(url: self.currentAnswer?.correct)
//                                                                                                }
                UIView.animate(withDuration: 0.5) {
                    copyView.frame = CGRect(x: x, y: y, width: layout.itemSize.width - 10, height: 40)
                } completion: { _ in
                    //                                                                                                DispatchQueue.main.asyncAfter(deadline: .now()+0.5) {
                    //                                                                                                                self.voicePlayer.playerAt(url: self.currentAnswer?.correct)
                    //                                                                                                }
                                                                                                if self.viewModel.answerType.value == .success{
                                                                                                                self.timer?.fireDate = .distantFuture
                                                                                                                DispatchQueue.main.asyncAfter(deadline: .now()+1) {
                                                                                                                                self.times = (self.listen1Model?.data?.time ?? 10) + 1
                                                                                                                                self.totalCount += 1
                                                                                                                                self.rootViewModel.correctNum += 1
                                                                                                                                self.label_class.text = "\(self.totalCount)"
                    if self.viewModel.answerType.value == .success{
                        self.timer?.fireDate = .distantFuture
                        DispatchQueue.main.asyncAfter(deadline: .now()+1) {
                            self.times = (self.listen1Model?.data?.time ?? 10) + 1
                            self.totalCount += 1
                            self.rootViewModel.correctNum += 1
                            self.label_class.text = "\(self.totalCount)"
                                                                                                                                if let currentA = self.currentAnswer{
                                                                                                                                                self.answerSet.remove(currentA)
                                                                                                                                }
                            if let currentA = self.currentAnswer{
                                self.answerSet.remove(currentA)
                            }
                                                                                                                                self.currentAnswer = self.answerSet.randomElement()
                                                                                                                                self.viewModel.answerType.accept(.none)
                                                                                                                                print("--->下一题:\(self.currentAnswer?.id ?? 0) 剩余\(self.answerSet.count)  计数:\(self.totalCount)")
                                                                                                                                self.timer?.fireDate = .distantPast
                                                                                                                }
                                                                                                }
                                                                                }
                                                                }
                            self.currentAnswer = self.answerSet.randomElement()
                            self.viewModel.answerType.accept(.none)
                            print("--->下一题:\(self.currentAnswer?.id ?? 0) 剩余\(self.answerSet.count)  计数:\(self.totalCount)")
                            self.timer?.fireDate = .distantPast
                        }
                    }
                }
            }
                                                case .fail:
                                                                rootViewModel.errorNum += 1
                                                                totalCount += 1
                                                                label_class.text = "\(totalCount)"
                                                                viewModel.answerType.accept(.fail)
                                                                timer?.fireDate = .distantFuture
                                                                label_hint.text = "准备听题"
                                                                //移除当前题目
                                                                if let c = currentAnswer{
                                                                                answerSet.remove(c)
                                                                }
                                                                collectionView.reloadData()
                                                                DispatchQueue.main.asyncAfter(deadline: .now()+1) {
                                                                                self.timer?.fireDate = .distantPast
                                                                                self.times = (self.listen1Model?.data?.time ?? 10) + 1
                                                                                self.currentAnswer = self.answerSet.randomElement()
                                                                }
                                                case .none:
                                                                break
                                }
                }
                private func completeQuestion(){
                                print("答题完成")
                                self.label_hint.text = "已完成全部答题"
                                self.label_hint.snp.makeConstraints { make in
                                                make.centerX.equalToSuperview()
                                                make.centerY.equalTo(view_class_title)
                                                make.height.equalTo(20)
                                }
        case .fail:
            rootViewModel.errorNum += 1
            totalCount += 1
            label_class.text = "\(totalCount)"
            viewModel.answerType.accept(.fail)
            timer?.fireDate = .distantFuture
            label_hint.text = "准备听题"
            //移除当前题目
            if let c = currentAnswer{
                answerSet.remove(c)
            }
            collectionView.reloadData()
            DispatchQueue.main.asyncAfter(deadline: .now()+1) {
                self.timer?.fireDate = .distantPast
                self.times = (self.listen1Model?.data?.time ?? 10) + 1
                self.currentAnswer = self.answerSet.randomElement()
            }
        case .none:
            break
        }
    }
                                view_class_title.isHidden = true
                                view_studyHandleView.isHidden = true
    private func completeQuestion(){
        print("答题完成")
        self.label_hint.text = "已完成全部答题"
        self.label_hint.snp.makeConstraints { make in
            make.centerX.equalToSuperview()
            make.centerY.equalTo(view_class_title)
            make.height.equalTo(20)
        }
                                self.timer?.invalidate()
                                self.rootViewModel.answerItems[0] = self.listen1Model
                                NotificationCenter.default.post(name: NextLession_Noti, object: ["gameId":listen1Model.data!.id,"gameIntegral":listen1Model.data!.integral,"complete":true])
                }
        view_class_title.isHidden = true
        view_studyHandleView.isHidden = true
        self.timer?.invalidate()
        self.rootViewModel.answerItems[0] = self.listen1Model
        NotificationCenter.default.post(name: NextLession_Noti, object: ["gameId":listen1Model.data!.id,"gameIntegral":listen1Model.data!.integral,"complete":true])
    }
}
extension HomeListenGame_1_VC:UICollectionViewDelegate{
                func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
//                                _ = listen1Model!.subjectList[indexPath.row]
                                                viewModel.selectIndex.accept(indexPath)
                                                answerQuestion()
                }
    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        //                                _ = listen1Model!.subjectList[indexPath.row]
        viewModel.selectIndex.accept(indexPath)
        answerQuestion()
    }
}
extension HomeListenGame_1_VC:UICollectionViewDataSource{
                func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
                                let model = listen1Model!.subjectList[indexPath.row]
                                let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "_ListenFight_Game_CCell", for: indexPath) as! ListenFight_Game_CCell
                                if viewModel.selectIndex.value == indexPath{
                                                cell.setState(state: viewModel.answerType.value)
                                }else{
                                                cell.setState(state: .none)
                                }
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let model = listen1Model!.subjectList[indexPath.row]
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "_ListenFight_Game_CCell", for: indexPath) as! ListenFight_Game_CCell
        if viewModel.selectIndex.value == indexPath{
            cell.setState(state: viewModel.answerType.value)
        }else{
            cell.setState(state: .none)
        }
                                cell.setModel(model)
                                return cell
                }
        cell.setModel(model)
        return cell
    }
                func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
                                return listen1Model?.subjectList.count ?? 0
                }
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return listen1Model?.subjectList.count ?? 0
    }
}
extension HomeListenGame_1_VC:VoicePlayerDelegate{
                func playComplete() {
    func playComplete() {
                                //防止再次播放时,误操作计算了正确率
                                if viewModel.answerType.value == .none{
                                                view.isUserInteractionEnabled = true
                                }
        //防止再次播放时,误操作计算了正确率
        if viewModel.answerType.value == .none{
            view.isUserInteractionEnabled = true
        }
                                timer?.fireDate = .distantPast ////播放中,恢复计时
        timer?.fireDate = .distantPast ////播放中,恢复计时
                                view_studyHandleView.resetView()
        view_studyHandleView.resetView()
                                self.label_hint.text = "准备听题"
        self.label_hint.text = "准备听题"
//                                if viewModel.answerType.value == .success{
//                                                timer?.fireDate = .distantFuture
//                                                DispatchQueue.main.asyncAfter(deadline: .now()+1) {
//                                                                self.times = (self.listen1Model?.data?.time ?? 10) + 1
//                                                                self.totalCount += 1
//                                                                self.rootViewModel.correctNum += 1
//                                                                self.label_class.text = "\(self.totalCount)"
//
//                                                                if let currentA = self.currentAnswer{
//                                                                                self.answerSet.remove(currentA)
//                                                                }
//
//                                                                self.currentAnswer = self.answerSet.randomElement()
//                                                                self.viewModel.answerType.accept(.none)
//                                                                print("--->下一题:\(self.currentAnswer?.id ?? 0) 剩余\(self.answerSet.count)  计数:\(self.totalCount)")
//                                                                self.timer?.fireDate = .distantPast
//                                                }
//                                }
        //                                if viewModel.answerType.value == .success{
        //                                                timer?.fireDate = .distantFuture
        //                                                DispatchQueue.main.asyncAfter(deadline: .now()+1) {
        //                                                                self.times = (self.listen1Model?.data?.time ?? 10) + 1
        //                                                                self.totalCount += 1
        //                                                                self.rootViewModel.correctNum += 1
        //                                                                self.label_class.text = "\(self.totalCount)"
        //
        //                                                                if let currentA = self.currentAnswer{
        //                                                                                self.answerSet.remove(currentA)
        //                                                                }
        //
        //                                                                self.currentAnswer = self.answerSet.randomElement()
        //                                                                self.viewModel.answerType.accept(.none)
        //                                                                print("--->下一题:\(self.currentAnswer?.id ?? 0) 剩余\(self.answerSet.count)  计数:\(self.totalCount)")
        //                                                                self.timer?.fireDate = .distantPast
        //                                                }
        //                                }
                                //答题完成
                                if self.answerSet.count == 0{completeQuestion()}
                }
                func playing() {
                                view.isUserInteractionEnabled = false
                                view_studyHandleView.playing()
                                timer?.fireDate = .distantFuture //播放中,暂停计时
//                                label_hint.text = "播放中"
//                                label_hint.text = ""
                                label_hint.text = "请在\(times)s内选择答案!"
                }
        //答题完成
        if self.answerSet.count == 0{completeQuestion()}
    }
    func playing() {
        view.isUserInteractionEnabled = false
        view_studyHandleView.playing()
        timer?.fireDate = .distantFuture //播放中,暂停计时
        //                                label_hint.text = "播放中"
        //                                label_hint.text = ""
        label_hint.text = "请在\(times)s内选择答案!"
    }
}
DolphinEnglishLearnStudent/Moudle/Home/Listen/VC/HomeListenSubVC.swift
@@ -11,336 +11,336 @@
class HomeListenSubVC: BaseVC {
                private var page:Int!
                private var quarter:Int!
                private var week:Int!
                private(set) var tableView:UITableView!
                var studyScheduleModel:StudyScheduleModel?
    private var page:Int!
    private var quarter:Int!
    private var week:Int!
    private(set) var tableView:UITableView!
    var studyScheduleModel:StudyScheduleModel?
                required init(page:Int,quarter:Int,week:Int,studyScheduleModel:StudyScheduleModel) {
                                super.init(nibName: nil, bundle: nil)
                                self.page = page
                                self.quarter = quarter
                                self.week = week
                                self.studyScheduleModel = studyScheduleModel
                }
                required init?(coder: NSCoder) {
                                fatalError("init(coder:) has not been implemented")
                }
    required init(page:Int,quarter:Int,week:Int,studyScheduleModel:StudyScheduleModel) {
        super.init(nibName: nil, bundle: nil)
        self.page = page
        self.quarter = quarter
        self.week = week
        self.studyScheduleModel = studyScheduleModel
    }
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    override func viewDidLoad() {
        super.viewDidLoad()
                                navigationItem.titleView = UIView()
        navigationItem.titleView = UIView()
    }
                override func setUI() {
                                super.setUI()
                                tableView = UITableView(frame: .zero, style: .plain)
                                tableView.delegate = self
                                tableView.dataSource = self
                                tableView.separatorStyle = .none
                                tableView.showsVerticalScrollIndicator = false
                                tableView.showsHorizontalScrollIndicator = false
                                tableView.backgroundColor = Config.ThemeBGColor
                                tableView.register(UINib(nibName: "HomeListen_process_TCell", bundle: nil), forCellReuseIdentifier: "_HomeListen_process_TCell")
                                tableView.register(UINib(nibName: "HomeListen_item_TCell", bundle: nil), forCellReuseIdentifier: "_HomeListen_item_TCell")
                                view.addSubview(tableView)
                                tableView.snp.makeConstraints { make in
                                                make.edges.equalToSuperview()
                                }
                }
    override func setUI() {
        super.setUI()
        tableView = UITableView(frame: .zero, style: .plain)
        tableView.delegate = self
        tableView.dataSource = self
        tableView.separatorStyle = .none
        tableView.showsVerticalScrollIndicator = false
        tableView.showsHorizontalScrollIndicator = false
        tableView.backgroundColor = Config.ThemeBGColor
        tableView.register(UINib(nibName: "HomeListen_process_TCell", bundle: nil), forCellReuseIdentifier: "_HomeListen_process_TCell")
        tableView.register(UINib(nibName: "HomeListen_item_TCell", bundle: nil), forCellReuseIdentifier: "_HomeListen_item_TCell")
        view.addSubview(tableView)
        tableView.snp.makeConstraints { make in
            make.edges.equalToSuperview()
        }
    }
                func jumpAt(listenType:ListenType){
                                let row = listenType.rawValue - 1
                                let jumpIndex:IndexPath = IndexPath(row: row, section: 1)
                                tableView(self.tableView, didSelectRowAt: jumpIndex)
                }
    func jumpAt(listenType:ListenType){
        let row = listenType.rawValue - 1
        let jumpIndex:IndexPath = IndexPath(row: row, section: 1)
        tableView(self.tableView, didSelectRowAt: jumpIndex)
    }
}
extension HomeListenSubVC:UITableViewDelegate{
                func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
                                if page <= 4 && indexPath.section == 0{return}
        if page <= 4 && indexPath.section == 0{return}
                                let day = page + 1
                                sceneDelegate?.startTimer()
        let day = page + 1
        sceneDelegate?.startTimer()
                                if page <= 4{
                                                if indexPath.row == 0{
                                                                Services.teamSchedule(type: .lesson1, week: week, day: day).subscribe(onNext: {[weak self] teamSchedule in
                                                                                guard let weakSelf = self else { return }
                                                                                //听音选图
                                                                                Services.listenSelectPicture(day:day, quarter: weakSelf.quarter, week: weakSelf.week).subscribe(onNext: {[weak self] result in
                                                                                                guard let weakSelf = self else { return }
                                                                                                if let data = result.data{
                                                                                                                let fightVC = HomeListenFightVC(listenType: .lesson1,quarter:weakSelf.quarter,week: weakSelf.week,day:day)
                                                                                                                fightVC.title = ListenType.lesson1.rawTitle
                                                                                                                fightVC.teamScheduleModel = teamSchedule.data
                                                                                                                fightVC.data = data
                                                                                                                fightVC.studyScheduleModel = self?.studyScheduleModel
                                                                                                                JQ_currentViewController().jq_push(vc:fightVC)
                                                                                                }
                                                                                }).disposed(by: weakSelf.disposeBag)
                                                                }).disposed(by: disposeBag)
                                                }
        if page <= 4{
            if indexPath.row == 0{
                Services.teamSchedule(type: .lesson1, week: week, day: day).subscribe(onNext: {[weak self] teamSchedule in
                    guard let weakSelf = self else { return }
                    //听音选图
                    Services.listenSelectPicture(day:day, quarter: weakSelf.quarter, week: weakSelf.week).subscribe(onNext: {[weak self] result in
                        guard let weakSelf = self else { return }
                        if let data = result.data{
                            let fightVC = HomeListenFightVC(listenType: .lesson1,quarter:weakSelf.quarter,week: weakSelf.week,day:day)
                            fightVC.title = ListenType.lesson1.rawTitle
                            fightVC.teamScheduleModel = teamSchedule.data
                            fightVC.data = data
                            fightVC.studyScheduleModel = self?.studyScheduleModel
                            JQ_currentViewController().jq_push(vc:fightVC)
                        }
                    }).disposed(by: weakSelf.disposeBag)
                }).disposed(by: disposeBag)
            }
                                                if indexPath.row == 1{
                                                                //看图选音
                                                                Services.teamSchedule(type: .lesson2, week: week, day: day).subscribe(onNext: {[weak self] teamSchedule in
                                                                                guard let weakSelf = self else { return }
                                                                                Services.pictureSelectVoice(day: day, quarter: weakSelf.quarter, week: weakSelf.week).subscribe(onNext: {[weak self] result in
                                                                                                guard let weakSelf = self else { return }
                                                                                                if let data = result.data{
                                                                                                                let fightVC = HomeListenFightVC(listenType: .lesson2,quarter:weakSelf.quarter,week: weakSelf.week,day:day)
                                                                                                                fightVC.title = ListenType.lesson2.rawTitle
                                                                                                                fightVC.teamScheduleModel = teamSchedule.data
                                                                                                                fightVC.data = data
                                                                                                                fightVC.studyScheduleModel = self?.studyScheduleModel
                                                                                                                JQ_currentViewController().jq_push(vc:fightVC)
                                                                                                }
                                                                                }).disposed(by: weakSelf.disposeBag)
                                                                }).disposed(by: disposeBag)
            if indexPath.row == 1{
                //看图选音
                Services.teamSchedule(type: .lesson2, week: week, day: day).subscribe(onNext: {[weak self] teamSchedule in
                    guard let weakSelf = self else { return }
                    Services.pictureSelectVoice(day: day, quarter: weakSelf.quarter, week: weakSelf.week).subscribe(onNext: {[weak self] result in
                        guard let weakSelf = self else { return }
                        if let data = result.data{
                            let fightVC = HomeListenFightVC(listenType: .lesson2,quarter:weakSelf.quarter,week: weakSelf.week,day:day)
                            fightVC.title = ListenType.lesson2.rawTitle
                            fightVC.teamScheduleModel = teamSchedule.data
                            fightVC.data = data
                            fightVC.studyScheduleModel = self?.studyScheduleModel
                            JQ_currentViewController().jq_push(vc:fightVC)
                        }
                    }).disposed(by: weakSelf.disposeBag)
                }).disposed(by: disposeBag)
                                                }
            }
                                                if indexPath.row == 2{
                                                                //归纳排除
                                                                Services.teamSchedule(type: .lesson3, week: week, day: day).subscribe(onNext: {[weak self] teamSchedule in
                                                                                guard let weakSelf = self else { return }
                                                                                Services.induceExclude(day: day, quarter: weakSelf.quarter, week: weakSelf.week).subscribe(onNext: {[weak self] result in
                                                                                guard let weakSelf = self else { return }
                                                                                if let data = result.data{
                                                                                                let fightVC = HomeListenFightVC(listenType: .lesson3,quarter:weakSelf.quarter,week: weakSelf.week,day:day)
                                                                                                fightVC.title = ListenType.lesson3.rawTitle
                                                                                                fightVC.teamScheduleModel = teamSchedule.data
                                                                                                fightVC.data = data
                                                                                                fightVC.studyScheduleModel = self?.studyScheduleModel
                                                                                                JQ_currentViewController().jq_push(vc: fightVC)
                                                                                }
                                                                                }).disposed(by: weakSelf.disposeBag)
                                                                }).disposed(by: disposeBag)
                                                }
            if indexPath.row == 2{
                //归纳排除
                Services.teamSchedule(type: .lesson3, week: week, day: day).subscribe(onNext: {[weak self] teamSchedule in
                    guard let weakSelf = self else { return }
                    Services.induceExclude(day: day, quarter: weakSelf.quarter, week: weakSelf.week).subscribe(onNext: {[weak self] result in
                        guard let weakSelf = self else { return }
                        if let data = result.data{
                            let fightVC = HomeListenFightVC(listenType: .lesson3,quarter:weakSelf.quarter,week: weakSelf.week,day:day)
                            fightVC.title = ListenType.lesson3.rawTitle
                            fightVC.teamScheduleModel = teamSchedule.data
                            fightVC.data = data
                            fightVC.studyScheduleModel = self?.studyScheduleModel
                            JQ_currentViewController().jq_push(vc: fightVC)
                        }
                    }).disposed(by: weakSelf.disposeBag)
                }).disposed(by: disposeBag)
            }
                                                if indexPath.row == 3{
                                                                //有问有答
                                                                Services.teamSchedule(type: .lesson4, week: week, day: day).subscribe(onNext: {[weak self] teamSchedule in
                                                                                guard let weakSelf = self else { return }
                                                                                Services.questionsAndAnswers(day: day, quarter: weakSelf.quarter, week: weakSelf.week).subscribe(onNext: {[weak self] result in
                                                                                                guard let weakSelf = self else { return }
                                                                                                if let data = result.data{
                                                                                                                let fightVC = HomeListenFightVC(listenType: .lesson4,quarter:weakSelf.quarter,week: weakSelf.week,day:day)
                                                                                                                fightVC.title = ListenType.lesson4.rawTitle
                                                                                                                fightVC.teamScheduleModel = teamSchedule.data
                                                                                                                fightVC.data = data
                                                                                                                fightVC.studyScheduleModel = self?.studyScheduleModel
                                                                                                                JQ_currentViewController().jq_push(vc: fightVC)
                                                                                                }
                                                                                }).disposed(by: weakSelf.disposeBag)
                                                                }).disposed(by: disposeBag)
                                                }
            if indexPath.row == 3{
                //有问有答
                Services.teamSchedule(type: .lesson4, week: week, day: day).subscribe(onNext: {[weak self] teamSchedule in
                    guard let weakSelf = self else { return }
                    Services.questionsAndAnswers(day: day, quarter: weakSelf.quarter, week: weakSelf.week).subscribe(onNext: {[weak self] result in
                        guard let weakSelf = self else { return }
                        if let data = result.data{
                            let fightVC = HomeListenFightVC(listenType: .lesson4,quarter:weakSelf.quarter,week: weakSelf.week,day:day)
                            fightVC.title = ListenType.lesson4.rawTitle
                            fightVC.teamScheduleModel = teamSchedule.data
                            fightVC.data = data
                            fightVC.studyScheduleModel = self?.studyScheduleModel
                            JQ_currentViewController().jq_push(vc: fightVC)
                        }
                    }).disposed(by: weakSelf.disposeBag)
                }).disposed(by: disposeBag)
            }
                                                if indexPath.row == 4{
                                                                //音图相配
                                                                Services.teamSchedule(type: .lesson5, week: week, day: day).subscribe(onNext: {[weak self] teamSchedule in
                                                                                guard let weakSelf = self else { return }
                                                                                Services.pictureMateVoice(day: day, quarter: weakSelf.quarter, week: weakSelf.week).subscribe(onNext: {[weak self] result in
                                                                                                guard let weakSelf = self else { return }
                                                                                                if let data = result.data{
                                                                                                                let fightVC = HomeListenFightVC(listenType: .lesson5,quarter:weakSelf.quarter,week: weakSelf.week,day:day)
                                                                                                                fightVC.title = ListenType.lesson5.rawTitle
                                                                                                                fightVC.teamScheduleModel = teamSchedule.data
                                                                                                                fightVC.data = data
                                                                                                                fightVC.studyScheduleModel = self?.studyScheduleModel
                                                                                                                JQ_currentViewController().jq_push(vc: fightVC)
                                                                                                }
                                                                                }).disposed(by: weakSelf.disposeBag)
                                                                }).disposed(by: disposeBag)
                                                }
                                }
            if indexPath.row == 4{
                //音图相配
                Services.teamSchedule(type: .lesson5, week: week, day: day).subscribe(onNext: {[weak self] teamSchedule in
                    guard let weakSelf = self else { return }
                    Services.pictureMateVoice(day: day, quarter: weakSelf.quarter, week: weakSelf.week).subscribe(onNext: {[weak self] result in
                        guard let weakSelf = self else { return }
                        if let data = result.data{
                            let fightVC = HomeListenFightVC(listenType: .lesson5,quarter:weakSelf.quarter,week: weakSelf.week,day:day)
                            fightVC.title = ListenType.lesson5.rawTitle
                            fightVC.teamScheduleModel = teamSchedule.data
                            fightVC.data = data
                            fightVC.studyScheduleModel = self?.studyScheduleModel
                            JQ_currentViewController().jq_push(vc: fightVC)
                        }
                    }).disposed(by: weakSelf.disposeBag)
                }).disposed(by: disposeBag)
            }
        }
                                //自主游戏
                                if page == 5{
                                                if indexPath.row == 0{
                                                                                let fightVC = HomeListenFightVC(listenType: .game1,quarter: quarter,week: week,day: day)
                                                                fightVC.title = ListenType.game1.rawTitle
                                                                fightVC.studyScheduleModel = studyScheduleModel
                                                                JQ_currentViewController().jq_push(vc:fightVC)
                                                }
                                                if indexPath.row == 1{
                                                                Services.gameMemory(quarter: quarter, week: week).subscribe(onNext: {[weak self]result in
                                                                                guard let weakSelf = self else { return }
                                                                                if let data = result.data{
                                                                                                let fightVC = HomeListenFightVC(listenType: .game2,quarter: weakSelf.quarter,week: weakSelf.week,day: day)
                                                                                                fightVC.title = ListenType.game2.rawTitle
                                                                                                fightVC.data = data
                                                                                                fightVC.studyScheduleModel = self?.studyScheduleModel
                                                                                                JQ_currentViewController().jq_push(vc: fightVC)
                                                                                }
                                                                }).disposed(by: disposeBag)
                                                }
                                }
        //自主游戏
        if page == 5{
            if indexPath.row == 0{
                let fightVC = HomeListenFightVC(listenType: .game1,quarter: quarter,week: week,day: day)
                fightVC.title = ListenType.game1.rawTitle
                fightVC.studyScheduleModel = studyScheduleModel
                JQ_currentViewController().jq_push(vc:fightVC)
            }
            if indexPath.row == 1{
                Services.gameMemory(quarter: quarter, week: week).subscribe(onNext: {[weak self]result in
                    guard let weakSelf = self else { return }
                    if let data = result.data{
                        let fightVC = HomeListenFightVC(listenType: .game2,quarter: weakSelf.quarter,week: weakSelf.week,day: day)
                        fightVC.title = ListenType.game2.rawTitle
                        fightVC.data = data
                        fightVC.studyScheduleModel = self?.studyScheduleModel
                        JQ_currentViewController().jq_push(vc: fightVC)
                    }
                }).disposed(by: disposeBag)
            }
        }
                                //听故事
                                if page == 6{
                                                if indexPath.row == 0{
                                                                Services.lookpictureDbu(quarter: quarter, week: week).subscribe(onNext: {[weak self]result in
                                                                                guard let weakSelf = self else { return }
                                                                                if let data = result.data{
                                                                                                let fightVC = HomeListenFightVC(listenType: .story1,quarter: weakSelf.quarter,week: weakSelf.week,day: day)
                                                                                                fightVC.title = ListenType.story1.rawTitle
                                                                                                fightVC.data = data
                                                                                                fightVC.studyScheduleModel = self?.studyScheduleModel
                                                                                                JQ_currentViewController().jq_push(vc: fightVC)
                                                                                }
                                                                }).disposed(by: disposeBag)
                                                }
                                                if indexPath.row == 1{
                                                                Services.frameworkMemory(quarter: quarter, week: week).subscribe(onNext: {[weak self]result in
                                                                                guard let weakSelf = self else { return }
                                                                                if let data = result.data{
                                                                                                let fightVC = HomeListenFightVC(listenType: .story2,quarter: weakSelf.quarter,week: weakSelf.week,day: day)
                                                                                                fightVC.title = ListenType.story2.rawTitle
                                                                                                fightVC.data = data
                                                                                                fightVC.studyScheduleModel = self?.studyScheduleModel
                                                                                                JQ_currentViewController().jq_push(vc: fightVC)
                                                                                }
                                                                }).disposed(by: disposeBag)
                                                }
                                }
                }
        //听故事
        if page == 6{
            if indexPath.row == 0{
                Services.lookpictureDbu(quarter: quarter, week: week).subscribe(onNext: {[weak self]result in
                    guard let weakSelf = self else { return }
                    if let data = result.data{
                        let fightVC = HomeListenFightVC(listenType: .story1,quarter: weakSelf.quarter,week: weakSelf.week,day: day)
                        fightVC.title = ListenType.story1.rawTitle
                        fightVC.data = data
                        fightVC.studyScheduleModel = self?.studyScheduleModel
                        JQ_currentViewController().jq_push(vc: fightVC)
                    }
                }).disposed(by: disposeBag)
            }
            if indexPath.row == 1{
                Services.frameworkMemory(quarter: quarter, week: week).subscribe(onNext: {[weak self]result in
                    guard let weakSelf = self else { return }
                    if let data = result.data{
                        let fightVC = HomeListenFightVC(listenType: .story2,quarter: weakSelf.quarter,week: weakSelf.week,day: day)
                        fightVC.title = ListenType.story2.rawTitle
                        fightVC.data = data
                        fightVC.studyScheduleModel = self?.studyScheduleModel
                        JQ_currentViewController().jq_push(vc: fightVC)
                    }
                }).disposed(by: disposeBag)
            }
        }
    }
}
extension HomeListenSubVC:UITableViewDataSource{
                func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
                                if page <= 4{
                                                if section == 0{return 1}
                                                return 5
                                }else{
                                                return 2
                                }
                }
        if page <= 4{
            if section == 0{return 1}
            return 5
        }else{
            return 2
        }
    }
                func numberOfSections(in tableView: UITableView) -> Int {
                                if page <= 4{
                                                return 2
                                }
                                return 1
                }
    func numberOfSections(in tableView: UITableView) -> Int {
        if page <= 4{
            return 2
        }
        return 1
    }
                func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
                                if indexPath.section == 0 && page <= 4{
                                                let cell = tableView.dequeueReusableCell(withIdentifier: "_HomeListen_process_TCell", for: indexPath) as! HomeListen_process_TCell
                                                cell.studyScheduleModel = studyScheduleModel
                                                cell.label_currentWeek.text = "当前周目:\(week.jq_cn)周目"
                                                return cell
                                }else{
                                                let cell = tableView.dequeueReusableCell(withIdentifier: "_HomeListen_item_TCell", for: indexPath) as! HomeListen_item_TCell
                                                cell.label_title.text = "\(indexPath.row + 1)"
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        if indexPath.section == 0 && page <= 4{
            let cell = tableView.dequeueReusableCell(withIdentifier: "_HomeListen_process_TCell", for: indexPath) as! HomeListen_process_TCell
            cell.studyScheduleModel = studyScheduleModel
            cell.label_currentWeek.text = "当前周目:\(week.jq_cn)周目"
            return cell
        }else{
            let cell = tableView.dequeueReusableCell(withIdentifier: "_HomeListen_item_TCell", for: indexPath) as! HomeListen_item_TCell
            cell.label_title.text = "\(indexPath.row + 1)"
                                                if page <= 4{
                                                                cell.view_bg1.isHidden = true
                                                                switch indexPath.row {
                                                                                case 0:
                                                                                                cell.label_title.text = "自主学习1-听音选图"
                                                                                                cell.view_bg2.backgroundColor = UIColor(hexString: "#6EC3FF")
            if page <= 4{
                cell.view_bg1.isHidden = true
                switch indexPath.row {
                case 0:
                    cell.label_title.text = "自主学习1-听音选图"
                    cell.view_bg2.backgroundColor = UIColor(hexString: "#6EC3FF")
                                                                                                if studyScheduleModel?.day == (page + 1){
                                                                                                                cell.setProgress(progress: studyScheduleModel?.listen ?? 0)
                                                                                                }else if (studyScheduleModel?.day ?? 0) > (page+1){
                                                                                                                cell.setProgress(progress: 100)
                                                                                                }else{
                                                                                                                cell.setProgress(progress: 0)
                                                                                                }
                                                                                case 1:
                                                                                                cell.label_title.text = "自主学习2-看图选音"
                                                                                                cell.view_bg2.backgroundColor = UIColor(hexString: "#FF9A85")
                    if studyScheduleModel?.day == (page + 1){
                        cell.setProgress(progress: studyScheduleModel?.listen ?? 0)
                    }else if (studyScheduleModel?.day ?? 0) > (page+1){
                        cell.setProgress(progress: 100)
                    }else{
                        cell.setProgress(progress: 0)
                    }
                case 1:
                    cell.label_title.text = "自主学习2-看图选音"
                    cell.view_bg2.backgroundColor = UIColor(hexString: "#FF9A85")
                                                                                                if studyScheduleModel?.day == (page + 1){
                                                                                                                cell.setProgress(progress: studyScheduleModel?.look ?? 0)
                                                                                                }else if (studyScheduleModel?.day ?? 0) > (page+1){
                                                                                                                cell.setProgress(progress: 100)
                                                                                                }else{
                                                                                                                cell.setProgress(progress: 0)
                                                                                                }
                                                                                case 2:
                                                                                                cell.label_title.text = "自主学习3-归纳排除"
                                                                                                cell.view_bg2.backgroundColor = UIColor(hexString: "#28C8C5")
                    if studyScheduleModel?.day == (page + 1){
                        cell.setProgress(progress: studyScheduleModel?.look ?? 0)
                    }else if (studyScheduleModel?.day ?? 0) > (page+1){
                        cell.setProgress(progress: 100)
                    }else{
                        cell.setProgress(progress: 0)
                    }
                case 2:
                    cell.label_title.text = "自主学习3-归纳排除"
                    cell.view_bg2.backgroundColor = UIColor(hexString: "#28C8C5")
                                                                                                if studyScheduleModel?.day == (page + 1){
                                                                                                                cell.setProgress(progress: studyScheduleModel?.induction ?? 0)
                                                                                                }else if (studyScheduleModel?.day ?? 0) > (page+1){
                                                                                                                cell.setProgress(progress: 100)
                                                                                                }else{
                                                                                                                cell.setProgress(progress: 0)
                                                                                                }
                                                                                case 3:
                                                                                                cell.label_title.text = "自主学习4-有问有答"
                                                                                                cell.view_bg2.backgroundColor = UIColor(hexString: "#F8A169")
                    if studyScheduleModel?.day == (page + 1){
                        cell.setProgress(progress: studyScheduleModel?.induction ?? 0)
                    }else if (studyScheduleModel?.day ?? 0) > (page+1){
                        cell.setProgress(progress: 100)
                    }else{
                        cell.setProgress(progress: 0)
                    }
                case 3:
                    cell.label_title.text = "自主学习4-有问有答"
                    cell.view_bg2.backgroundColor = UIColor(hexString: "#F8A169")
                                                                                                if studyScheduleModel?.day == (page + 1){
                                                                                                                cell.setProgress(progress: studyScheduleModel?.answer ?? 0)
                                                                                                }else if (studyScheduleModel?.day ?? 0) > (page+1){
                                                                                                                cell.setProgress(progress: 100)
                                                                                                }else{
                                                                                                                cell.setProgress(progress: 0)
                                                                                                }
                                                                                case 4:
                                                                                                cell.label_title.text = "自主学习5-音图相配"
                                                                                                cell.view_bg2.backgroundColor = UIColor(hexString: "#92CADB")
                    if studyScheduleModel?.day == (page + 1){
                        cell.setProgress(progress: studyScheduleModel?.answer ?? 0)
                    }else if (studyScheduleModel?.day ?? 0) > (page+1){
                        cell.setProgress(progress: 100)
                    }else{
                        cell.setProgress(progress: 0)
                    }
                case 4:
                    cell.label_title.text = "自主学习5-音图相配"
                    cell.view_bg2.backgroundColor = UIColor(hexString: "#92CADB")
                                                                                                if studyScheduleModel?.day == (page + 1){
                                                                                                                cell.setProgress(progress: studyScheduleModel?.pair ?? 0)
                                                                                                }else if (studyScheduleModel?.day ?? 0) > (page+1){
                                                                                                                cell.setProgress(progress: 100)
                                                                                                }else{
                                                                                                                cell.setProgress(progress: 0)
                                                                                                }
                                                                                default:break
                                                                }
                                                }
                    if studyScheduleModel?.day == (page + 1){
                        cell.setProgress(progress: studyScheduleModel?.pair ?? 0)
                    }else if (studyScheduleModel?.day ?? 0) > (page+1){
                        cell.setProgress(progress: 100)
                    }else{
                        cell.setProgress(progress: 0)
                    }
                default:break
                }
            }
                                                if page == 5{
                                                                cell.view_bg2.isHidden = true
                                                                cell.view_state.isHidden = true
                                                                switch indexPath.row {
                                                                                case 0:
                                                                                                cell.label_title.text = "自主游戏1-超级听力"
                                                                                                cell.view_bg1.backgroundColor = UIColor(hexString: "#6EC3FF")
                                                                                case 1:
                                                                                                cell.label_title.text = "自主游戏2-超级记忆"
                                                                                                cell.view_bg1.backgroundColor = UIColor(hexString: "#FF9A85")
                                                                                default:break
                                                                }
                                                }
            if page == 5{
                cell.view_bg2.isHidden = true
                cell.view_state.isHidden = true
                switch indexPath.row {
                case 0:
                    cell.label_title.text = "自主游戏1-超级听力"
                    cell.view_bg1.backgroundColor = UIColor(hexString: "#6EC3FF")
                case 1:
                    cell.label_title.text = "自主游戏2-超级记忆"
                    cell.view_bg1.backgroundColor = UIColor(hexString: "#FF9A85")
                default:break
                }
            }
                                                if page == 6{
                                                                cell.view_bg2.isHidden = true
                                                                cell.view_state.isHidden = true
                                                                switch indexPath.row {
                                                                                case 0:
                                                                                                cell.label_title.text = "自主故事1-看图配音"
                                                                                                cell.view_bg1.backgroundColor = UIColor(hexString: "#6EC3FF")
                                                                                case 1:
                                                                                                cell.label_title.text = "自主故事2-框架记忆"
                                                                                                cell.view_bg1.backgroundColor = UIColor(hexString: "#FF9A85")
                                                                                default:break
                                                                }
                                                }
            if page == 6{
                cell.view_bg2.isHidden = true
                cell.view_state.isHidden = true
                switch indexPath.row {
                case 0:
                    cell.label_title.text = "自主故事1-看图配音"
                    cell.view_bg1.backgroundColor = UIColor(hexString: "#6EC3FF")
                case 1:
                    cell.label_title.text = "自主故事2-框架记忆"
                    cell.view_bg1.backgroundColor = UIColor(hexString: "#FF9A85")
                default:break
                }
            }
                                                return cell
                                }
                }
            return cell
        }
    }
                func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
                                if page <= 5{
                                                if indexPath.section == 0{return 145.5}
                                                return 127.5
                                }else{
                                                return 127.5
                                }
                }
    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        if page <= 5{
            if indexPath.section == 0{return 145.5}
            return 127.5
        }else{
            return 127.5
        }
    }
}
DolphinEnglishLearnStudent/Moudle/Home/Listen/VC/HomeListenVC.swift
@@ -83,11 +83,13 @@
                                }
                                self.pageVC.reloadData()
        #if !DEBUG
                                if limitDay == 6{
                                                self.pageVC.scroll(toPage: 4, animation: true)
                                }else{
                                                self.pageVC.scroll(toPage: self.limitDay - 1, animation: true)
                                }
        #endif
                }
                override func setUI() {
DolphinEnglishLearnStudent/Moudle/Home/Listen/View/Lesson_4_AnswerView.swift
@@ -15,17 +15,29 @@
                @IBOutlet weak var view_state: UIView!
                @IBOutlet weak var view_handle: UIView!
                @IBOutlet weak var btn_isAnswer: UIButton!
                @IBOutlet weak var img_play: UIImageView!
    @IBOutlet weak var img_play: UIButton!
                @IBOutlet weak var btn_playing: UIButton!
                
                var voiceUrl:String?
                var isCopy:Bool = false
                var playAtClouse:((Int)->Void)?
    var isplayend:Bool = false
                override func awakeFromNib() {
                                super.awakeFromNib()
                                img_state.alpha = 0
                                img_play.alpha = 0
        view_handle.backgroundColor = .white
        btn_isAnswer.setImage(UIImage(named: "icon_answer"), for: .normal)
        img_play.setImage(UIImage(named: "icon_play"), for: .normal)
        btn_playing.setImage(UIImage(named: "icon_play_1"), for: .normal)
        view_handle.isUserInteractionEnabled = true
        let tap = UITapGestureRecognizer(target: self, action: #selector(playAction))
        view_handle.addGestureRecognizer(tap)
//                                VoicePlayer.share().playEnd {
//                                                if self.isCopy{
//                                                                self.img_play.alpha = 1
@@ -44,22 +56,23 @@
                }
                func isPlaying(){
                                btn_playing.setImage(UIImage(named: "icon_playing"), for: .normal)
        isplayend = true
        btn_playing.setImage(UIImage(named: "icon_playing")?.themeGreen, for: .normal)
                                btn_isAnswer.isHidden = true
                                img_play.isHidden = true
                }
                func playEnd(){
                                btn_isAnswer.isHidden = false
                                btn_playing.setImage(UIImage(named: "icon_play_1"), for: .normal)
        btn_playing.setImage(UIImage(named: "icon_play_1"), for: .normal)
                }
                @IBAction func playAction(_ sender: UIButton) {
                @objc private func playAction() {
                                if let url = voiceUrl{
                                                VoicePlayer.share().playerAt(url: url)
                                                img_play.alpha = 0
                                                playAtClouse?(self.tag)
                                                btn_playing.setImage(UIImage(named: "icon_playing"), for: .normal)
                                                btn_playing.setImage(UIImage(named: "icon_playing")?.themeGreen, for: .normal)
                                                btn_isAnswer.isHidden = true
                                                img_play.isHidden = true
DolphinEnglishLearnStudent/Moudle/Home/Listen/View/Lesson_4_AnswerView.xib
@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="32700.99.1234" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="23504" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
    <device id="ipad10_9rounded" orientation="portrait" layout="fullscreen" appearance="light"/>
    <dependencies>
        <deployment identifier="iOS"/>
        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22684"/>
        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="23506"/>
        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
    </dependencies>
    <objects>
@@ -19,50 +19,43 @@
                        <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="qsr-6I-w3R">
                            <rect key="frame" x="0.0" y="0.0" width="152" height="52"/>
                            <subviews>
                                <button opaque="NO" userInteractionEnabled="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="otl-1Z-qED">
                                    <rect key="frame" x="15.5" y="15" width="28" height="22"/>
                                    <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
                                    <state key="normal" image="icon_answer"/>
                                </button>
                                <button opaque="NO" userInteractionEnabled="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="mba-RI-50M">
                                    <rect key="frame" x="62.5" y="12.5" width="27" height="27"/>
                                    <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
                                    <state key="normal" image="icon_play_1"/>
                                </button>
                                <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="icon_fail" translatesAutoresizingMaskIntoConstraints="NO" id="mkh-g4-79e">
                                    <rect key="frame" x="100.5" y="10" width="32" height="32"/>
                                    <rect key="frame" x="60" y="10" width="32" height="32"/>
                                    <constraints>
                                        <constraint firstAttribute="height" constant="32" id="unt-83-tZW"/>
                                        <constraint firstAttribute="width" constant="32" id="yDR-8r-kHx"/>
                                    </constraints>
                                </imageView>
                                <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="tfO-7L-o0T">
                                <stackView opaque="NO" contentMode="scaleToFill" distribution="fillEqually" translatesAutoresizingMaskIntoConstraints="NO" id="x83-pW-htY">
                                    <rect key="frame" x="0.0" y="0.0" width="152" height="52"/>
                                    <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
                                    <connections>
                                        <action selector="playAction:" destination="iN0-l3-epB" eventType="touchUpInside" id="mAa-LF-0SJ"/>
                                    </connections>
                                </button>
                                <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="icon_play" translatesAutoresizingMaskIntoConstraints="NO" id="YeU-8E-35u">
                                    <rect key="frame" x="102" y="10" width="32" height="32"/>
                                </imageView>
                                    <subviews>
                                        <button opaque="NO" userInteractionEnabled="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="otl-1Z-qED">
                                            <rect key="frame" x="0.0" y="0.0" width="50.5" height="52"/>
                                            <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
                                            <state key="normal" image="icon_answer"/>
                                        </button>
                                        <button opaque="NO" userInteractionEnabled="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="mba-RI-50M">
                                            <rect key="frame" x="50.5" y="0.0" width="51" height="52"/>
                                            <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
                                            <state key="normal" image="icon_play_1"/>
                                        </button>
                                        <button opaque="NO" userInteractionEnabled="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="KTh-43-lUh">
                                            <rect key="frame" x="101.5" y="0.0" width="50.5" height="52"/>
                                            <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
                                            <state key="normal" image="icon_play"/>
                                        </button>
                                    </subviews>
                                </stackView>
                            </subviews>
                            <color key="backgroundColor" red="0.25490196079999999" green="0.63529411759999999" blue="0.92156862750000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                            <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                            <constraints>
                                <constraint firstItem="mba-RI-50M" firstAttribute="leading" secondItem="otl-1Z-qED" secondAttribute="trailing" constant="19" id="0nx-t9-1zV"/>
                                <constraint firstItem="YeU-8E-35u" firstAttribute="centerY" secondItem="tfO-7L-o0T" secondAttribute="centerY" id="2DM-IT-SGr"/>
                                <constraint firstAttribute="width" constant="152" id="3Zx-vg-H2U"/>
                                <constraint firstAttribute="bottom" secondItem="tfO-7L-o0T" secondAttribute="bottom" id="8ro-2n-YNH"/>
                                <constraint firstAttribute="trailing" secondItem="tfO-7L-o0T" secondAttribute="trailing" id="A2h-po-R4k"/>
                                <constraint firstAttribute="trailing" secondItem="YeU-8E-35u" secondAttribute="trailing" constant="18" id="DAX-dg-Ase"/>
                                <constraint firstItem="mba-RI-50M" firstAttribute="centerY" secondItem="otl-1Z-qED" secondAttribute="centerY" id="FTc-eb-PWk"/>
                                <constraint firstItem="tfO-7L-o0T" firstAttribute="top" secondItem="qsr-6I-w3R" secondAttribute="top" id="HqC-Ia-1OU"/>
                                <constraint firstItem="mba-RI-50M" firstAttribute="centerX" secondItem="tfO-7L-o0T" secondAttribute="centerX" id="M4G-fi-i36"/>
                                <constraint firstItem="mkh-g4-79e" firstAttribute="centerY" secondItem="otl-1Z-qED" secondAttribute="centerY" id="N6f-d4-80e"/>
                                <constraint firstItem="tfO-7L-o0T" firstAttribute="leading" secondItem="qsr-6I-w3R" secondAttribute="leading" id="QfH-1Q-LyV"/>
                                <constraint firstItem="mkh-g4-79e" firstAttribute="leading" secondItem="mba-RI-50M" secondAttribute="trailing" constant="11" id="a9o-Lq-9SP"/>
                                <constraint firstItem="mba-RI-50M" firstAttribute="centerY" secondItem="tfO-7L-o0T" secondAttribute="centerY" id="gPd-3s-zX2"/>
                                <constraint firstItem="otl-1Z-qED" firstAttribute="centerY" secondItem="qsr-6I-w3R" secondAttribute="centerY" id="v0P-gn-zbN"/>
                                <constraint firstItem="mkh-g4-79e" firstAttribute="centerY" secondItem="x83-pW-htY" secondAttribute="centerY" id="FBf-rK-ic5"/>
                                <constraint firstAttribute="trailing" secondItem="x83-pW-htY" secondAttribute="trailing" id="Q2V-v1-cdD"/>
                                <constraint firstItem="x83-pW-htY" firstAttribute="top" secondItem="qsr-6I-w3R" secondAttribute="top" id="SgE-1G-uBq"/>
                                <constraint firstItem="x83-pW-htY" firstAttribute="leading" secondItem="qsr-6I-w3R" secondAttribute="leading" id="Wre-wg-0ii"/>
                                <constraint firstAttribute="bottom" secondItem="x83-pW-htY" secondAttribute="bottom" id="kkD-H8-FcP"/>
                                <constraint firstItem="mkh-g4-79e" firstAttribute="centerX" secondItem="x83-pW-htY" secondAttribute="centerX" id="ree-1r-FCJ"/>
                            </constraints>
                            <userDefinedRuntimeAttributes>
                                <userDefinedRuntimeAttribute type="boolean" keyPath="ld_maskToBoundsXIB" value="YES"/>
@@ -103,7 +96,7 @@
                <outlet property="btn_choose" destination="v7f-gv-EWR" id="wFE-Gp-xAd"/>
                <outlet property="btn_isAnswer" destination="otl-1Z-qED" id="0Fg-A8-9dl"/>
                <outlet property="btn_playing" destination="mba-RI-50M" id="Gmt-aT-rn4"/>
                <outlet property="img_play" destination="YeU-8E-35u" id="Xvc-ls-0sz"/>
                <outlet property="img_play" destination="KTh-43-lUh" id="7GM-KZ-Ikc"/>
                <outlet property="img_state" destination="mkh-g4-79e" id="IzD-w8-jjK"/>
                <outlet property="view_handle" destination="qsr-6I-w3R" id="ptU-09-NIc"/>
                <outlet property="view_state" destination="Gbs-f8-132" id="Swt-nY-Bpv"/>
@@ -114,9 +107,9 @@
    <resources>
        <image name="btn_radio" width="52" height="52"/>
        <image name="btn_radio_u" width="52" height="52"/>
        <image name="icon_answer" width="28" height="14"/>
        <image name="icon_answer" width="42" height="21"/>
        <image name="icon_fail" width="80" height="80"/>
        <image name="icon_play" width="32" height="32"/>
        <image name="icon_play_1" width="27" height="27"/>
        <image name="icon_play" width="45" height="45"/>
        <image name="icon_play_1" width="28.5" height="30"/>
    </resources>
</document>
DolphinEnglishLearnStudent/Moudle/Home/VC/HomeStudyCompleteVC.swift
@@ -11,180 +11,180 @@
let StudyCompleteCoinUpdate_Noti = Notification.Name.init("StudyCompleteCoinUpdate_Noti")
class HomeStudyCompleteVC: BaseVC {
                @IBOutlet weak var label_coin: UILabel!
                @IBOutlet weak var label_correctNum: UILabel!
                @IBOutlet weak var label_title_correctNum: UILabel!
                @IBOutlet weak var label_totalNum: UILabel!
                @IBOutlet weak var label_title_totalNum: UILabel!
                @IBOutlet weak var label_errorNum: UILabel!
                @IBOutlet weak var label_title_errorNum: UILabel!
                @IBOutlet weak var label_ratioNum: UILabel!
                @IBOutlet weak var btn_next: UIButton!
                @IBOutlet weak var stackView: UIStackView!
                @IBOutlet weak var btn_back: UIButton!
    @IBOutlet weak var label_coin: UILabel!
    @IBOutlet weak var label_correctNum: UILabel!
    @IBOutlet weak var label_title_correctNum: UILabel!
    @IBOutlet weak var label_totalNum: UILabel!
    @IBOutlet weak var label_title_totalNum: UILabel!
    @IBOutlet weak var label_errorNum: UILabel!
    @IBOutlet weak var label_title_errorNum: UILabel!
    @IBOutlet weak var label_ratioNum: UILabel!
    @IBOutlet weak var btn_next: UIButton!
    @IBOutlet weak var stackView: UIStackView!
    @IBOutlet weak var btn_back: UIButton!
//                private var totalCoin:Int = 0
                private var totalNum:Int? //总题目数量
    //                private var totalCoin:Int = 0
    private var totalNum:Int? //总题目数量
                var viewModel:HomeListenFightViewModel!
                var studyScheduleModel:StudyScheduleModel!
    var viewModel:HomeListenFightViewModel!
    var studyScheduleModel:StudyScheduleModel!
                required init(totalNum:Int? = nil,viewModel:HomeListenFightViewModel,studyScheduleModel:StudyScheduleModel){
                                super.init(nibName: nil, bundle: nil)
//                                self.totalCoin = totalCoin
                                self.totalNum = totalNum
                                self.viewModel = viewModel
                                self.studyScheduleModel = studyScheduleModel
                }
    required init(totalNum:Int? = nil,viewModel:HomeListenFightViewModel,studyScheduleModel:StudyScheduleModel){
        super.init(nibName: nil, bundle: nil)
        //                                self.totalCoin = totalCoin
        self.totalNum = totalNum
        self.viewModel = viewModel
        self.studyScheduleModel = studyScheduleModel
    }
                required init?(coder: NSCoder) {
                                fatalError("init(coder:) has not been implemented")
                }
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
                override func viewDidLoad() {
                                super.viewDidLoad()
    override func viewDidLoad() {
        super.viewDidLoad()
                                navigationController?.viewControllers.removeAll(where: { vc in
                                                return vc is HomeListenFightVC
                                })
        navigationController?.viewControllers.removeAll(where: { vc in
            return vc is HomeListenFightVC
        })
                                yy_popBlock = {[weak self] () in
                                                self?.backAction()
                                }
        yy_popBlock = {[weak self] () in
            self?.backAction()
        }
                                label_coin.text = "恭喜您,已完成全部答题!获得0积分!"
                                label_correctNum.text = "\(viewModel.correctNum)次"
                                label_errorNum.text = "\(viewModel.errorNum)次"
                                label_totalNum.text = "\(viewModel.correctNum + viewModel.errorNum)次"
        label_coin.text = "恭喜您,已完成全部答题!获得0积分!"
        label_correctNum.text = "\(viewModel.correctNum)次"
        label_errorNum.text = "\(viewModel.errorNum)次"
        label_totalNum.text = "\(viewModel.correctNum + viewModel.errorNum)次"
                                if viewModel.correctNum + viewModel.errorNum == 0{
                                                label_ratioNum.text = String(format: "正确率:0%%")
                                }else{
                                                label_ratioNum.text = String(format: "正确率:%ld%%", floor(Double(viewModel.correctNum) / Double(viewModel.correctNum + viewModel.errorNum) * 100).int)
                                }
        if viewModel.correctNum + viewModel.errorNum == 0{
            label_ratioNum.text = String(format: "正确率:0%%")
        }else{
            label_ratioNum.text = String(format: "正确率:%ld%%", floor(Double(viewModel.correctNum) / Double(viewModel.correctNum + viewModel.errorNum) * 100).int)
        }
                                if totalNum != nil && viewModel.listenType.value == .game2{
                                                label_title_totalNum.text = "总题目:"
                                                label_title_correctNum.text = "正确题目:"
                                                label_title_errorNum.text = "错误题目:"
                                                label_coin.text = "恭喜您,已完成游戏!获得0积分!"
        if totalNum != nil && viewModel.listenType.value == .game2{
            label_title_totalNum.text = "总题目:"
            label_title_correctNum.text = "正确题目:"
            label_title_errorNum.text = "错误题目:"
            label_coin.text = "恭喜您,已完成游戏!获得0积分!"
                                                label_totalNum.text = "\(totalNum!)"
                                                label_correctNum.text = "\(viewModel.correctNum)"
                                                label_errorNum.text = "\(viewModel.errorNum)"
                                }
            label_totalNum.text = "\(totalNum!)"
            label_correctNum.text = "\(viewModel.correctNum)"
            label_errorNum.text = "\(viewModel.errorNum)"
        }
                                switch viewModel.listenType.value{
                                                case .lesson5,.game1,.game2,.story1,.story2:btn_next.isHidden = true
                                                default:btn_next.isHidden = false
                                }
        switch viewModel.listenType.value{
        case .lesson5,.game1,.game2,.story1,.story2:btn_next.isHidden = true
        default:btn_next.isHidden = false
        }
                                stackView.isHidden = viewModel.listenType.value == .story2
                                label_ratioNum.isHidden = viewModel.listenType.value == .story2
        stackView.isHidden = viewModel.listenType.value == .story2
        label_ratioNum.isHidden = viewModel.listenType.value == .story2
                                NotificationCenter.default.post(name: MeUserInfoUpdate_Noti, object: nil)
                }
        NotificationCenter.default.post(name: MeUserInfoUpdate_Noti, object: nil)
    }
                override func setUI() {
                                super.setUI()
                }
    override func setUI() {
        super.setUI()
    }
                override func setRx() {
                                NotificationCenter.default.rx.notification(StudyCompleteCoinUpdate_Noti).take(until: self.rx.deallocated).subscribe(onNext: {[weak self] data in
                                                guard let weakSelf = self else { return }
    override func setRx() {
        NotificationCenter.default.rx.notification(StudyCompleteCoinUpdate_Noti).take(until: self.rx.deallocated).subscribe(onNext: {[weak self] data in
            guard let weakSelf = self else { return }
                                                if let coin = data.object as? Int{
                                                                switch weakSelf.viewModel.listenType.value {
                                                                                case .game2:weakSelf.label_coin.text = "恭喜您,已完成游戏!获得\(coin)积分!"
                                                                                default:weakSelf.label_coin.text = "恭喜您,已完成全部答题!获得\(coin)积分!"
                                                                }
                                                }
                                }).disposed(by: disposeBag)
                }
            if let coin = data.object as? Int{
                switch weakSelf.viewModel.listenType.value {
                case .game2:weakSelf.label_coin.text = "恭喜您,已完成游戏!获得\(coin)积分!"
                default:weakSelf.label_coin.text = "恭喜您,已完成全部答题!获得\(coin)积分!"
                }
            }
        }).disposed(by: disposeBag)
    }
                @IBAction func backHomeAction(_ sender: UIButton) {
                                backAction()
                }
    @IBAction func backHomeAction(_ sender: UIButton) {
        backAction()
    }
                @IBAction func nextAction(_ sender: UIButton) {
    @IBAction func nextAction(_ sender: UIButton) {
                                var toVC:UIViewController?
                                for subv in self.navigationController?.viewControllers ?? []{
                                                if subv is HomeListenVC{
                                                                toVC = subv;break
                                                }
                                }
        var toVC:UIViewController?
        for subv in self.navigationController?.viewControllers ?? []{
            if subv is HomeListenVC{
                toVC = subv;break
            }
        }
                                if toVC == nil{
                                                self.navigationController?.popToRootViewController(animated: true)
                                }else{
                                                let nextType = ListenType(rawValue: viewModel.listenType.value.rawValue + 1)!
        if toVC == nil{
            self.navigationController?.popToRootViewController(animated: true)
        }else{
            let nextType = ListenType(rawValue: viewModel.listenType.value.rawValue + 1)!
                                                sceneDelegate?.startTimer()
            sceneDelegate?.startTimer()
                                                switch nextType {
                                                                case .lesson2:
                                                                                Services.pictureSelectVoice(day:viewModel.day.value!, quarter: viewModel.quarter.value!, week: viewModel.week.value!).subscribe(onNext: {[weak self] result in
                                                                                                guard let weakSelf = self else { return }
                                                                                                if let data = result.data{
                                                                                                                let fightVC = HomeListenFightVC(listenType: .lesson2,quarter:weakSelf.viewModel.quarter.value!,week: weakSelf.viewModel.week.value!,day:weakSelf.viewModel.day.value!)
                                                                                                                fightVC.title = ListenType.lesson2.rawTitle
                                                                                                                fightVC.data = data
                                                                                                                fightVC.studyScheduleModel = weakSelf.studyScheduleModel
                                                                                                                weakSelf.push(vc: fightVC)
                                                                                                }
                                                                                }).disposed(by: disposeBag)
            switch nextType {
            case .lesson2:
                Services.pictureSelectVoice(day:viewModel.day.value!, quarter: viewModel.quarter.value!, week: viewModel.week.value!).subscribe(onNext: {[weak self] result in
                    guard let weakSelf = self else { return }
                    if let data = result.data{
                        let fightVC = HomeListenFightVC(listenType: .lesson2,quarter:weakSelf.viewModel.quarter.value!,week: weakSelf.viewModel.week.value!,day:weakSelf.viewModel.day.value!)
                        fightVC.title = ListenType.lesson2.rawTitle
                        fightVC.data = data
                        fightVC.studyScheduleModel = weakSelf.studyScheduleModel
                        weakSelf.push(vc: fightVC)
                    }
                }).disposed(by: disposeBag)
                                                                case .lesson3:
                                                                                Services.induceExclude(day: viewModel.day.value!, quarter: viewModel.quarter.value!, week: viewModel.week.value!).subscribe(onNext: {[weak self] result in
                                                                                                guard let weakSelf = self else { return }
                                                                                                if let data = result.data{
                                                                                                                let fightVC = HomeListenFightVC(listenType: .lesson3,quarter:weakSelf.viewModel.quarter.value!,week: weakSelf.viewModel.week.value!,day:weakSelf.viewModel.day.value!)
                                                                                                                fightVC.title = ListenType.lesson3.rawTitle
                                                                                                                fightVC.data = data
                                                                                                                fightVC.studyScheduleModel = weakSelf.studyScheduleModel
                                                                                                                weakSelf.push(vc: fightVC)
                                                                                                }
                                                                                }).disposed(by: disposeBag)
            case .lesson3:
                Services.induceExclude(day: viewModel.day.value!, quarter: viewModel.quarter.value!, week: viewModel.week.value!).subscribe(onNext: {[weak self] result in
                    guard let weakSelf = self else { return }
                    if let data = result.data{
                        let fightVC = HomeListenFightVC(listenType: .lesson3,quarter:weakSelf.viewModel.quarter.value!,week: weakSelf.viewModel.week.value!,day:weakSelf.viewModel.day.value!)
                        fightVC.title = ListenType.lesson3.rawTitle
                        fightVC.data = data
                        fightVC.studyScheduleModel = weakSelf.studyScheduleModel
                        weakSelf.push(vc: fightVC)
                    }
                }).disposed(by: disposeBag)
                                                                case .lesson4:
                                                                                Services.questionsAndAnswers(day: viewModel.day.value!, quarter: viewModel.quarter.value!, week: viewModel.week.value!).subscribe(onNext: {[weak self] result in
                                                                                                guard let weakSelf = self else { return }
                                                                                                if let data = result.data{
                                                                                                                let fightVC = HomeListenFightVC(listenType: .lesson4,quarter:weakSelf.viewModel.quarter.value!,week: weakSelf.viewModel.week.value!,day:weakSelf.viewModel.day.value!)
                                                                                                                fightVC.title = ListenType.lesson4.rawTitle
                                                                                                                fightVC.data = data
                                                                                                                fightVC.studyScheduleModel = weakSelf.studyScheduleModel
                                                                                                                weakSelf.push(vc: fightVC)
                                                                                                }
                                                                                }).disposed(by: disposeBag)
            case .lesson4:
                Services.questionsAndAnswers(day: viewModel.day.value!, quarter: viewModel.quarter.value!, week: viewModel.week.value!).subscribe(onNext: {[weak self] result in
                    guard let weakSelf = self else { return }
                    if let data = result.data{
                        let fightVC = HomeListenFightVC(listenType: .lesson4,quarter:weakSelf.viewModel.quarter.value!,week: weakSelf.viewModel.week.value!,day:weakSelf.viewModel.day.value!)
                        fightVC.title = ListenType.lesson4.rawTitle
                        fightVC.data = data
                        fightVC.studyScheduleModel = weakSelf.studyScheduleModel
                        weakSelf.push(vc: fightVC)
                    }
                }).disposed(by: disposeBag)
                                                                case .lesson5:
                                                                                Services.pictureMateVoice(day: viewModel.day.value!, quarter: viewModel.quarter.value!, week: viewModel.week.value!).subscribe(onNext: {[weak self] result in
                                                                                                guard let weakSelf = self else { return }
                                                                                                if let data = result.data{
                                                                                                                let fightVC = HomeListenFightVC(listenType: .lesson5,quarter:weakSelf.viewModel.quarter.value!,week: weakSelf.viewModel.week.value!,day:weakSelf.viewModel.day.value!)
                                                                                                                fightVC.title = ListenType.lesson5.rawTitle
                                                                                                                fightVC.data = data
                                                                                                                fightVC.studyScheduleModel = weakSelf.studyScheduleModel
                                                                                                                weakSelf.push(vc: fightVC)
                                                                                                }
                                                                                }).disposed(by: disposeBag)
            case .lesson5:
                Services.pictureMateVoice(day: viewModel.day.value!, quarter: viewModel.quarter.value!, week: viewModel.week.value!).subscribe(onNext: {[weak self] result in
                    guard let weakSelf = self else { return }
                    if let data = result.data{
                        let fightVC = HomeListenFightVC(listenType: .lesson5,quarter:weakSelf.viewModel.quarter.value!,week: weakSelf.viewModel.week.value!,day:weakSelf.viewModel.day.value!)
                        fightVC.title = ListenType.lesson5.rawTitle
                        fightVC.data = data
                        fightVC.studyScheduleModel = weakSelf.studyScheduleModel
                        weakSelf.push(vc: fightVC)
                    }
                }).disposed(by: disposeBag)
                                                                default:break
                                                }
                                }
                }
            default:break
            }
        }
    }
                private func backAction(){
                                for vc in navigationController?.viewControllers ?? []{
                                                if vc.isKind(of: HomeListenVC.self){
                                                                navigationController?.popToViewController(vc, animated: true);break
                                                }
                                }
                }
    private func backAction(){
        for vc in navigationController?.viewControllers ?? []{
            if vc.isKind(of: HomeListenVC.self){
                navigationController?.popToViewController(vc, animated: true);break
            }
        }
    }
}
DolphinEnglishLearnStudent/Moudle/Me/TCell/GoodsItemTCell.swift
@@ -9,99 +9,99 @@
import RxSwift
class GoodsItemTCell: UITableViewCell {
                @IBOutlet weak var label_state: UILabel!
                @IBOutlet weak var label_goodsName: UILabel!
                @IBOutlet weak var label_types: UILabel!
                @IBOutlet weak var label_goodsNum: UILabel!
                @IBOutlet weak var label_receiptInfo: UILabel!
                @IBOutlet weak var label_sendInfo: UILabel!
                @IBOutlet weak var btn_state: UIButton!
                @IBOutlet weak var label_coin: UILabel!
                @IBOutlet weak var img_cover: UIImageView!
                @IBOutlet weak var view_container: UIView!
                private var exchangeRecordModel:ExchangeRecordModel?
                private var disposeBag = DisposeBag()
    @IBOutlet weak var label_state: UILabel!
    @IBOutlet weak var label_goodsName: UILabel!
    @IBOutlet weak var label_types: UILabel!
    @IBOutlet weak var label_goodsNum: UILabel!
    @IBOutlet weak var label_receiptInfo: UILabel!
    @IBOutlet weak var label_sendInfo: UILabel!
    @IBOutlet weak var btn_state: UIButton!
    @IBOutlet weak var label_coin: UILabel!
    @IBOutlet weak var img_cover: UIImageView!
    @IBOutlet weak var view_container: UIView!
                override func awakeFromNib() {
    private var exchangeRecordModel:ExchangeRecordModel?
    private var disposeBag = DisposeBag()
    override func awakeFromNib() {
        super.awakeFromNib()
                                selectionStyle = .none
                                backgroundColor = .clear
                                view_container.jq_addShadows(shadowColor: UIColor(hexStr: "#D9D9D9").withAlphaComponent(0.28), corner: 8, radius: 3, offset: CGSize(width: 0, height: 2), opacity: 1)
        selectionStyle = .none
        backgroundColor = .clear
        view_container.jq_addShadows(shadowColor: UIColor(hexStr: "#D9D9D9").withAlphaComponent(0.28), corner: 8, radius: 3, offset: CGSize(width: 0, height: 2), opacity: 1)
    }
                func setModel(_ model:ExchangeRecordModel){
                                exchangeRecordModel = model
                                label_goodsNum.text = "商品数量:\(model.count)"
                                label_coin.text = "\(model.integral)积分"
                                label_goodsName.text = model.goodsName
                                label_types.text = model.goodsType.joined(separator: "|")
                                img_cover.sd_setImage(with: URL(string: model.coverImg))
                                var items_consignee = Array<String>()
                                items_consignee.append(model.consigneeName)
                                items_consignee.append(model.consigneePhone)
                                items_consignee.append(model.consigneeAddress)
                                label_receiptInfo.text = "收货信息:" + items_consignee.joined(separator: "|")
    func setModel(_ model:ExchangeRecordModel){
        exchangeRecordModel = model
        label_goodsNum.text = "商品数量:\(model.count)"
        label_coin.text = "\(model.integral)积分"
        label_goodsName.text = model.goodsName
        label_types.text = model.goodsType.joined(separator: "|")
        img_cover.sd_setImage(with: URL(string: model.coverImg))
        var items_consignee = Array<String>()
        items_consignee.append(model.consigneeName)
        items_consignee.append(model.consigneePhone)
        items_consignee.append(model.consigneeAddress)
        label_receiptInfo.text = "收货信息:" + items_consignee.joined(separator: "|")
                                var items_express = Array<String>()
                                items_express.append(model.express)
                                items_express.append(model.expressNumber)
        var items_express = Array<String>()
        items_express.append(model.express)
        items_express.append(model.expressNumber)
                                label_sendInfo.isHidden = items_express.filter({!$0.isEmpty}).count == 0
                                label_sendInfo.text = "发货信息:" + items_express.joined(separator: "|")
        label_sendInfo.isHidden = items_express.filter({!$0.isEmpty}).count == 0
        label_sendInfo.text = "发货信息:" + items_express.joined(separator: "|")
                                //订单状态1待发货2已发货3已完成
                                switch model.state{
                                                case 1:
                                                                label_state.text = "待发货"
                                                                btn_state.setTitle("修改地址", for: .normal)
                                                                btn_state.isHidden = false
                                                case 2:
                                                                label_state.text = "平台已发货,请耐心等待"
                                                                btn_state.setTitle("已收货", for: .normal)
                                                                btn_state.isHidden = false
                                                case 3:
                                                                label_state.text = "已完成"
                                                                btn_state.isHidden = true
                                                default:
                                                                btn_state.isHidden = true
                                }
                }
        //订单状态1待发货2已发货3已完成
        switch model.state{
        case 1:
            label_state.text = "待发货"
            btn_state.setTitle("修改地址", for: .normal)
            btn_state.isHidden = false
        case 2:
            label_state.text = "平台已发货,请耐心等待"
            btn_state.setTitle("已收货", for: .normal)
            btn_state.isHidden = false
        case 3:
            label_state.text = "已完成"
            btn_state.isHidden = true
        default:
            btn_state.isHidden = true
        }
    }
                @IBAction func handleAction(_ sender: UIButton) {
    @IBAction func handleAction(_ sender: UIButton) {
                                switch exchangeRecordModel!.state{
                                                case 1:
                                                                let vc = AddressManageVC(type: .choose)
                                                                vc.title = "修改地址"
                                                                vc.chooseAddress { m in
                                                                                CommonAlertView.show(content: "确认修改当前收货地址吗?") {[weak self] () in
                                                                                                guard let weakSelf = self else { return }
                                                                                                if weakSelf.exchangeRecordModel?.consigneeAddress == m.address && weakSelf.exchangeRecordModel?.consigneeName == m.recipient && weakSelf.exchangeRecordModel?.consigneePhone == m.recipientPhone{
                                                                                                                alertError(msg: "修改地址信息与原地址信息相同");return
                                                                                                }
                                                                                                Services.updateOrderAddress(orderId: weakSelf.exchangeRecordModel!.orderId, recipientId: m.id).subscribe(onNext: {data in
                                                                                                                alertSuccess(msg: "修改成功")
                                                                                                                DispatchQueue.main.asyncAfter(delay: 1.8) {
                                                                                                                                NotificationCenter.default.post(name: Refresh_MarketExchange_Noti, object: nil)
                                                                                                                }
                                                                                                }).disposed(by: weakSelf.disposeBag)
                                                                                }
                                                                }
                                                                JQ_currentViewController().jq_push(vc: vc)
                                                case 2:
                                                                CommonAlertView.show(isSinple: false, content: "确认已收到货吗?") {[weak self] () in
                                                                                guard let weakSelf = self else { return }
                                                                                Services.confirmStudy(id: weakSelf.exchangeRecordModel!.orderId).subscribe(onNext: {data in
                                                                                                DispatchQueue.main.asyncAfter(delay: 1.8) {
                                                                                                                NotificationCenter.default.post(name: Refresh_MarketExchange_Noti, object: nil)
                                                                                                }
                                                                                }).disposed(by: weakSelf.disposeBag)
                                                                }
                                                default:break
                                }
                }
        switch exchangeRecordModel!.state{
        case 1:
            let vc = AddressManageVC(type: .choose)
            vc.title = "修改地址"
            vc.chooseAddress { m in
                CommonAlertView.show(content: "确认修改当前收货地址吗?") {[weak self] () in
                    guard let weakSelf = self else { return }
                    if weakSelf.exchangeRecordModel?.consigneeAddress == m.address && weakSelf.exchangeRecordModel?.consigneeName == m.recipient && weakSelf.exchangeRecordModel?.consigneePhone == m.recipientPhone{
                        alertError(msg: "修改地址信息与原地址信息相同");return
                    }
                    Services.updateOrderAddress(orderId: weakSelf.exchangeRecordModel!.orderId, recipientId: m.id).subscribe(onNext: {data in
                        alertSuccess(msg: "修改成功")
                        DispatchQueue.main.asyncAfter(delay: 1.8) {
                            NotificationCenter.default.post(name: Refresh_MarketExchange_Noti, object: nil)
                        }
                    }).disposed(by: weakSelf.disposeBag)
                }
            }
            JQ_currentViewController().jq_push(vc: vc)
        case 2:
            CommonAlertView.show(isSinple: false, content: "确认已收到货吗?") {[weak self] () in
                guard let weakSelf = self else { return }
                Services.confirmStudy(id: weakSelf.exchangeRecordModel!.orderId).subscribe(onNext: {data in
                    DispatchQueue.main.asyncAfter(delay: 1.8) {
                        NotificationCenter.default.post(name: Refresh_MarketExchange_Noti, object: nil)
                    }
                }).disposed(by: weakSelf.disposeBag)
            }
        default:break
        }
    }
}
DolphinEnglishLearnStudent/Other/UIView/StudyHandleView.swift
@@ -10,7 +10,8 @@
import AVFoundation
class StudyHandleView: UIView,JQNibView{
                @IBOutlet weak var btn_choose: UIButton!
    @IBOutlet weak var view_container: UIView!
    @IBOutlet weak var btn_choose: UIButton!
                @IBOutlet weak var view_choose: UIView!
                @IBOutlet weak var btn_state: UIButton!
                @IBOutlet weak var btn_voice: UIButton!
@@ -23,11 +24,20 @@
                var voicePlayer = VoicePlayer.share()
                var vioceSoundUrl:String?
    var isplayend:Bool = false
                override func awakeFromNib() {
                                super.awakeFromNib()
                                btn_state.alpha = 0
                                view_choose.alpha = 0
        alpha = 0
        btn_voice.setImage(UIImage(named: "icon_play_1"), for: .normal)
        btn_pay.setImage(UIImage(named: "icon_play"), for: .normal)
        view_container.backgroundColor = .white
        btn_choose.setImage(UIImage(named: "btn_radio_u"), for: .normal)
        btn_choose.setImage(UIImage(named: "btn_radio"), for: .selected)
                }
                func chooseClouse(callback:@escaping (UIButton)->Void){
@@ -50,7 +60,7 @@
                                switch listenType {
                                                case .lesson1,.lesson5,.game1:
                                                                UIView.animate(withDuration: 0.25) {
                                                                                self.btn_state.setImage(UIImage(named: "icon_play"), for: .normal)
                    self.btn_state.setImage(UIImage(named: "icon_play"), for: .normal)
                                                                                self.btn_voice.alpha = 1
                                                                                self.btn_pay.alpha = 1
                                                                                self.btn_state.alpha = 0
@@ -68,10 +78,12 @@
                }
                func isplaying(){
        isplayend = true
        alpha =  1
                                switch listenType {
                                                case .lesson1,.lesson5,.game1:
                                                                UIView.animate(withDuration: 0.25) {
                                                                                self.btn_state.setImage(UIImage(named: "icon_playing"), for: .normal)
                                                                                self.btn_state.setImage(UIImage(named: "icon_playing")?.themeGreen, for: .normal)
                                                                                self.btn_voice.alpha = 0
                                                                                self.btn_pay.alpha = 0
                                                                                self.btn_state.alpha = 1
@@ -79,7 +91,7 @@
                                                                break
                                                case .lesson2:
                                                                UIView.animate(withDuration: 0.25) {
                                                                                self.btn_state.setImage(UIImage(named: "icon_playing"), for: .normal)
                                                                                self.btn_state.setImage(UIImage(named: "icon_playing")?.themeGreen, for: .normal)
                                                                                self.btn_voice.alpha = 0
                                                                                self.btn_pay.alpha = 0
                                                                                self.btn_state.alpha = 1
DolphinEnglishLearnStudent/Other/UIView/StudyHandleView.xib
@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="32700.99.1234" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="23504" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
    <device id="ipad10_9rounded" orientation="portrait" layout="fullscreen" appearance="light"/>
    <dependencies>
        <deployment identifier="iOS"/>
        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22684"/>
        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="23506"/>
        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
    </dependencies>
    <objects>
@@ -19,7 +19,7 @@
                            <rect key="frame" x="0.0" y="0.0" width="298" height="103"/>
                            <subviews>
                                <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="eGw-vD-STe">
                                    <rect key="frame" x="91" y="0.0" width="27" height="103"/>
                                    <rect key="frame" x="89.5" y="0.0" width="28.5" height="103"/>
                                    <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
                                    <state key="normal" image="icon_play_1"/>
                                </button>
@@ -33,19 +33,19 @@
                                    <state key="normal" image="icon_success_small"/>
                                </button>
                                <button opaque="NO" userInteractionEnabled="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="mz1-vG-vfG">
                                    <rect key="frame" x="174" y="0.0" width="32" height="103"/>
                                    <rect key="frame" x="174" y="0.0" width="45" height="103"/>
                                    <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
                                    <state key="normal" image="icon_play"/>
                                </button>
                                <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="xZL-Va-yis">
                                    <rect key="frame" x="91" y="0.0" width="115" height="103"/>
                                    <rect key="frame" x="89.5" y="0.0" width="129.5" height="103"/>
                                    <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
                                    <connections>
                                        <action selector="payAction:" destination="iN0-l3-epB" eventType="touchUpInside" id="Hsm-4b-6ud"/>
                                    </connections>
                                </button>
                            </subviews>
                            <color key="backgroundColor" red="0.25490196078431371" green="0.63529411764705879" blue="0.92156862745098034" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                            <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                            <constraints>
                                <constraint firstAttribute="bottom" secondItem="xZL-Va-yis" secondAttribute="bottom" id="6CU-mm-T5g"/>
                                <constraint firstAttribute="bottom" secondItem="eGw-vD-STe" secondAttribute="bottom" id="IFe-rp-rkn"/>
@@ -105,6 +105,7 @@
                <outlet property="btn_state" destination="zhb-53-qPO" id="VwF-zX-XaX"/>
                <outlet property="btn_voice" destination="eGw-vD-STe" id="vtB-m3-iOY"/>
                <outlet property="view_choose" destination="xya-RD-K98" id="gz4-Jd-l5U"/>
                <outlet property="view_container" destination="0XQ-2a-X00" id="fpu-P3-hBc"/>
            </connections>
            <point key="canvasLocation" x="89.268292682926827" y="-171.10169491525426"/>
        </view>
@@ -112,8 +113,8 @@
    <resources>
        <image name="btn_radio" width="52" height="52"/>
        <image name="btn_radio_u" width="52" height="52"/>
        <image name="icon_play" width="32" height="32"/>
        <image name="icon_play_1" width="27" height="27"/>
        <image name="icon_play" width="45" height="45"/>
        <image name="icon_play_1" width="28.5" height="30"/>
        <image name="icon_success_small" width="42" height="42"/>
    </resources>
</document>
DolphinEnglishLearnStudent/Other/UIView/VoiceHandleView.swift
@@ -9,109 +9,112 @@
class VoiceHandleView: UIView {
                private lazy var img_hint:UIImageView = {
                                let img = UIImageView(image: UIImage(named: "icon_play_1")?.themeGreen)
                                return img
                }()
    private lazy var img_hint:UIImageView = {
        let img = UIImageView(image: UIImage(named: "icon_play_1"))
        return img
    }()
                private lazy var img_hint_playing:UIImageView = {
    private lazy var img_hint_playing:UIImageView = {
        let img = UIImageView(image: UIImage(named: "icon_playing")?.themeGreen)
                                img.isHidden = true
                                return img
                }()
        img.isHidden = true
        return img
    }()
                private lazy var btn_play:UIButton = {
                                let btn = UIButton(type: .custom)
        btn.setImage(UIImage(named: "icon_play")?.themeGreen, for: .normal)
                                return btn
                }()
    private lazy var btn_play:UIButton = {
        let btn = UIButton(type: .custom)
        btn.setImage(UIImage(named: "icon_play"), for: .normal)
        return btn
    }()
                let playBtn = UIButton(type: .custom)
    let playBtn = UIButton(type: .custom)
                var playUrl:String?
                var listenType:ListenType?
                private var playAtClouse:((Int)->Void)?
    var isPlayed:Bool = false //是否已播放
    var playUrl:String?
    var listenType:ListenType?
    private var playAtClouse:((Int)->Void)?
                override init(frame: CGRect) {
                                super.init(frame: frame)
                                setUI()
    override init(frame: CGRect) {
        super.init(frame: frame)
        setUI()
                                VoicePlayer.share().playEnd {
                                                self.resetView()
                                }
                }
        VoicePlayer.share().playEnd {
            self.resetView()
        }
    }
                private func setUI(){
//                                backgroundColor = UIColor(hexString: "#41A2EB")
    private func setUI(){
        //                                backgroundColor = UIColor(hexString: "#41A2EB")
        backgroundColor = UIColor.white
                                jq_cornerRadius = 8
                                addSubview(img_hint_playing)
                                addSubview(img_hint)
                                addSubview(btn_play)
        jq_cornerRadius = 8
        addSubview(img_hint_playing)
        addSubview(img_hint)
        addSubview(btn_play)
                                img_hint_playing.snp.makeConstraints { make in
                                                make.center.equalToSuperview()
                                                make.width.equalTo(45)
                                                make.height.equalTo(31)
                                }
        img_hint_playing.snp.makeConstraints { make in
            make.center.equalToSuperview()
            make.width.equalTo(45)
            make.height.equalTo(31)
        }
                                btn_play.isUserInteractionEnabled = false
                                btn_play.snp.makeConstraints { make in
                                                make.left.equalTo(img_hint_playing.snp.right).offset(0)
                                                make.centerY.equalToSuperview()
                                                make.width.equalTo(32)
                                                make.height.equalTo(32)
                                }
        btn_play.isUserInteractionEnabled = false
        btn_play.snp.makeConstraints { make in
            make.left.equalTo(img_hint_playing.snp.right).offset(0)
            make.centerY.equalToSuperview()
            make.width.equalTo(32)
            make.height.equalTo(32)
        }
                                img_hint.snp.makeConstraints { make in
                                                make.right.equalTo(img_hint_playing.snp.left).offset(0)
                                                make.centerY.equalToSuperview()
                                                make.width.equalTo(27)
                                                make.height.equalTo(27)
                                }
        img_hint.snp.makeConstraints { make in
            make.right.equalTo(img_hint_playing.snp.left).offset(0)
            make.centerY.equalToSuperview()
            make.width.equalTo(27)
            make.height.equalTo(27)
        }
                                playBtn.addTarget(self, action: #selector(playingAction), for: .touchUpInside)
                                addSubview(playBtn)
                                playBtn.snp.makeConstraints { make in
                                                make.edges.equalToSuperview()
                                }
                }
        playBtn.addTarget(self, action: #selector(playingAction), for: .touchUpInside)
        addSubview(playBtn)
        playBtn.snp.makeConstraints { make in
            make.edges.equalToSuperview()
        }
    }
                required init?(coder: NSCoder) {
                                fatalError("init(coder:) has not been implemented")
                }
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
                func copyView()->VoiceHandleView{
                                let copyView = VoiceHandleView()
                                copyView.listenType = self.listenType
                                copyView.playUrl = self.playUrl
                                copyView.frame = self.frame
                                return copyView
                }
    func copyView()->VoiceHandleView{
        let copyView = VoiceHandleView()
        copyView.listenType = self.listenType
        copyView.playUrl = self.playUrl
        copyView.frame = self.frame
        return copyView
    }
                func resetView(){
                                img_hint.isHidden = false
                                btn_play.isHidden = false
                                img_hint_playing.isHidden = true
                                jq_cornerRadius = 8
                }
    func resetView(){
        img_hint.isHidden = false
        btn_play.isHidden = false
        img_hint_playing.isHidden = true
        isPlayed = false
        jq_cornerRadius = 8
    }
                func playing(){
                                img_hint.isHidden = true
                                btn_play.isHidden = true
                                img_hint_playing.isHidden = false
                }
    func playing(){
        img_hint.isHidden = true
        btn_play.isHidden = true
        isPlayed = true
        img_hint_playing.isHidden = false
    }
                func playAt(_ clouse:@escaping(Int)->Void){
                                self.playAtClouse = clouse
                }
    func playAt(_ clouse:@escaping(Int)->Void){
        self.playAtClouse = clouse
    }
                @objc func playingAction(){
                                if let url = playUrl{
                                                playAtClouse?(self.tag)
                                                VoicePlayer.share().playerAt(url: url)
                                                playing()
                                }
                }
    @objc func playingAction(){
        if let url = playUrl{
            playAtClouse?(self.tag)
            VoicePlayer.share().playerAt(url: url)
            playing()
        }
    }
}
DolphinEnglishLearnStudent/Services/NetworkRequest.swift
@@ -16,24 +16,24 @@
// 假设这是服务端返回的统一定义的response格式
struct BaseResponse<T :HandyJSON>: HandyJSON {
                var sysTime: Int = 0
                var code: Int = -1 // 服务端返回码
                var data: T? = nil // 具体的data的格式和业务相关,故用泛型定义
                var msg: String = ""
    var sysTime: Int = 0
    var code: Int = -1 // 服务端返回码
    var data: T? = nil // 具体的data的格式和业务相关,故用泛型定义
    var msg: String = ""
}
struct BaseData<T: HandyJSON>: HandyJSON {
                var records = [T]()
    var records = [T]()
}
struct SimpleModel: HandyJSON {
}
struct HtmlModel: HandyJSON {
                var  content = ""
                var  content1 = ""
                var id = 0
                var type =  0
    var  content = ""
    var  content1 = ""
    var id = 0
    var type =  0
}
extension String: HandyJSON{
@@ -53,227 +53,227 @@
let SHAKEY = ""
class ParamsAppender: NSObject {
                var url: URL
                var params:Dictionary = [String: Any]()
    var url: URL
    var params:Dictionary = [String: Any]()
                private init(url: String){
                                self.url = URL(string: url)!
                }
    private init(url: String){
        self.url = URL(string: url)!
    }
                @discardableResult
                func interface(url: String) -> ParamsAppender {
                                self.url.appendPathComponent(url)
                                return self
                }
    @discardableResult
    func interface(url: String) -> ParamsAppender {
        self.url.appendPathComponent(url)
        return self
    }
                @discardableResult
                func append(key: String,value: Bool) -> ParamsAppender {
                                params += ["\(key)":"\(value)"]
                                return self
                }
    @discardableResult
    func append(key: String,value: Bool) -> ParamsAppender {
        params += ["\(key)":"\(value)"]
        return self
    }
                @discardableResult
                func append(key: String,value: String?) -> ParamsAppender {
                                if value != nil && value?.isEmpty == false {
                                                params += ["\(key)":"\(value!)"]
                                }
                                return self
                }
    @discardableResult
    func append(key: String,value: String?) -> ParamsAppender {
        if value != nil && value?.isEmpty == false {
            params += ["\(key)":"\(value!)"]
        }
        return self
    }
                @discardableResult
                func append(key: String,value: Array<String>) -> ParamsAppender {
                                if value.isEmpty == false {
                                                params += ["\(key)":value]
                                }
                                return self
                }
    @discardableResult
    func append(key: String,value: Array<String>) -> ParamsAppender {
        if value.isEmpty == false {
            params += ["\(key)":value]
        }
        return self
    }
                @discardableResult
                func append(key: String, value: Int?) -> ParamsAppender {
                                if value != nil{
                                                params += ["\(key)":value!]
                                }
                                return self
                }
    @discardableResult
    func append(key: String, value: Int?) -> ParamsAppender {
        if value != nil{
            params += ["\(key)":value!]
        }
        return self
    }
                @discardableResult
                func append(key: String, value: Int64) -> ParamsAppender {
                                params += ["\(key)":value]
                                return self
                }
    @discardableResult
    func append(key: String, value: Int64) -> ParamsAppender {
        params += ["\(key)":value]
        return self
    }
                @discardableResult
                func append(key: String, value: Double?) -> ParamsAppender {
                                if value != nil{
                                                params += ["\(key)":value!]
                                }
                                return self
                }
    @discardableResult
    func append(key: String, value: Double?) -> ParamsAppender {
        if value != nil{
            params += ["\(key)":value!]
        }
        return self
    }
                @discardableResult
                func append(key: String,data: Data?) -> ParamsAppender {
                                if data != nil{
                                                params += ["\(key)": data!]
                                }
                                return self
                }
    @discardableResult
    func append(key: String,data: Data?) -> ParamsAppender {
        if data != nil{
            params += ["\(key)": data!]
        }
        return self
    }
                @discardableResult
                func append(key: String,url: URL) -> ParamsAppender {
                                params += ["\(key)":"\(url)"]
                                return self
                }
    @discardableResult
    func append(key: String,url: URL) -> ParamsAppender {
        params += ["\(key)":"\(url)"]
        return self
    }
                @discardableResult
                func append(dic: [String : Any]) -> ParamsAppender {
                                params += dic
                                return self
                }
    @discardableResult
    func append(dic: [String : Any]) -> ParamsAppender {
        params += dic
        return self
    }
                /// 参数加密
                @discardableResult
                func done() -> Parameters {
                                var paramsArray: [String] = []
                                // 排序
                                let sortedArray: [String] = Array(params.keys).sorted()
    /// 参数加密
    @discardableResult
    func done() -> Parameters {
        var paramsArray: [String] = []
        // 排序
        let sortedArray: [String] = Array(params.keys).sorted()
                                //防止自签名而错误
                                if !sortedArray.contains("sign"){
                                                for item in sortedArray{
                                                                // 拼接字符串
                                                                if params.has(key: item){
                                                                                paramsArray.append("\(item)=\(params[item]!)")
                                                                }
                                                }
                                                let content = paramsArray.joined(separator: "&")
                                                params += ["sign": "\(content.jq_hmacBase64(algorithm: .SHA1, key: SHAKEY))"]
        //防止自签名而错误
        if !sortedArray.contains("sign"){
            for item in sortedArray{
                // 拼接字符串
                if params.has(key: item){
                    paramsArray.append("\(item)=\(params[item]!)")
                }
            }
            let content = paramsArray.joined(separator: "&")
            params += ["sign": "\(content.jq_hmacBase64(algorithm: .SHA1, key: SHAKEY))"]
#if DEBUG
                                                LogInfo("签名:\(content) ----- \(content.jq_hmacBase64(algorithm: .SHA1, key: SHAKEY))")
            LogInfo("签名:\(content) ----- \(content.jq_hmacBase64(algorithm: .SHA1, key: SHAKEY))")
#endif
                                }
                                return self.params
                }
        }
        return self.params
    }
                class func build(url: String) -> ParamsAppender {
                                return ParamsAppender(url: url)
                }
    class func build(url: String) -> ParamsAppender {
        return ParamsAppender(url: url)
    }
}
class NetworkRequest {
                static let sharedSessionManager: Alamofire.Session = {
                                let configuration = URLSessionConfiguration.default
                                configuration.timeoutIntervalForRequest = 10
                                return Alamofire.Session(configuration: configuration)
                }()
                enum NetRequestError: Error {
                                case Other(Int,String)
                                case URLNotFound
                                case DownloadFailed
                                case InvaildSession
                                case ModelError(String)
                                case DataAnalysis(String)
                }
    static let sharedSessionManager: Alamofire.Session = {
        let configuration = URLSessionConfiguration.default
        configuration.timeoutIntervalForRequest = 10
        return Alamofire.Session(configuration: configuration)
    }()
    enum NetRequestError: Error {
        case Other(Int,String)
        case URLNotFound
        case DownloadFailed
        case InvaildSession
        case ModelError(String)
        case DataAnalysis(String)
    }
                class func request<T: HandyJSON>(params: ParamsAppender, method: HTTPMethod,  encoding: ParameterEncoding? = nil, progress: Bool = true,ignoreAlert:Bool = false) -> Observable<BaseResponse<T>>{
    class func request<T: HandyJSON>(params: ParamsAppender, method: HTTPMethod,  encoding: ParameterEncoding? = nil, progress: Bool = true,ignoreAlert:Bool = false) -> Observable<BaseResponse<T>>{
                                return Observable<BaseResponse<T>>.create{ ob in
                                                guard NetworkReachabilityManager.init(host: All_Url)!.isReachable else {
                                                                alertError(msg: "当前网络不可用")
                                                                ob.onError(AFError.invalidURL(url: params.url))
                                                                return Disposables.create{}
                                                }
        return Observable<BaseResponse<T>>.create{ ob in
            guard NetworkReachabilityManager.init(host: All_Url)!.isReachable else {
                alertError(msg: "当前网络不可用")
                ob.onError(AFError.invalidURL(url: params.url))
                return Disposables.create{}
            }
                                                if progress {showHUD()}
            if progress {showHUD()}
                                                var headers = HTTPHeaders()
            var headers = HTTPHeaders()
                                                if let token = LoginTokenModel.getToken()?.access_token{
                                                                headers.add(name: "Authorization", value: "Bearer" + " " + token)
                                                                LogInfo("USER_token:Bearer \(token)")
                                                }
            if let token = LoginTokenModel.getToken()?.access_token{
                headers.add(name: "Authorization", value: "Bearer" + " " + token)
                LogInfo("USER_token:Bearer \(token)")
            }
                                                if encoding is JSONEncoding {
                                                                headers.add(name: "Content-Type", value: "application/json;charset=UTF-8")
                                                }
            if encoding is JSONEncoding {
                headers.add(name: "Content-Type", value: "application/json;charset=UTF-8")
            }
                                                var newEncoding: ParameterEncoding
                                                if encoding != nil {
                                                                newEncoding = encoding!
                                                } else {
                                                                newEncoding = method == .post ? URLEncoding.httpBody : URLEncoding.queryString
                                                }
            var newEncoding: ParameterEncoding
            if encoding != nil {
                newEncoding = encoding!
            } else {
                newEncoding = method == .post ? URLEncoding.httpBody : URLEncoding.queryString
            }
                                                sharedSessionManager.request(params.url.absoluteString, method: method, parameters:params.done(), encoding: newEncoding, headers:headers).validate().responseData{response in
                                                                LogInfo("请求地址:\(params.url)")
                                                                LogInfo("请求参数:\(params.params)")
                                                                if progress{hiddenHUD()}
            sharedSessionManager.request(params.url.absoluteString, method: method, parameters:params.done(), encoding: newEncoding, headers:headers).validate().responseData{response in
                LogInfo("请求地址:\(params.url)")
                LogInfo("请求参数:\(params.params)")
                if progress{hiddenHUD()}
                                                                guard response.error == nil else {
                                                                                LogError("\(response.error!)")
                guard response.error == nil else {
                    LogError("\(response.error!)")
                                                                                var errorString = ""
                                                                                errorString.append("服务器故障:\(response.error!.localizedDescription)")
                                                                                if let code = response.error?.responseCode{
                                                                                                errorString.append("\n【错误码:\(code)】")
                                                                                }
                                                                                if !ignoreAlert{
                                                                                                alert(msg: errorString)
                                                                                }
                                                                                ob.onError(response.error!)
                                                                                return
                                                                }
                                                                if let data = response.data,let jsonString = String(data: data, encoding: String.Encoding.utf8){
                                                                                LogResponse(try! JSONSerialization.jsonObject(with: data))
                                                                                if let next = BaseResponse<T>.deserialize(from: jsonString){
                                                                                                switch next.code{
                                                                                                                case 200:ob.onNext(next)
                                                                                                                case 506:
                                                                                                                                ob.onError(NetRequestError.Other(next.code,next.msg))
                                                                                                                case 502: //登录被冻结
                                                                                                                                CommonAlertView.show(isSinple: true, content: next.msg)
                                                                                                                case 501:
                                                                                                                                CommonAlertView.show(content: "以下内容仅限会员查看,请先成为会员!", completeTitle: "成为会员") {
                                                                                                                                                 let vc = VIPCenterVC()
                                                                                                                                                vc.title = "会员中心"
                                                                                                                                                JQ_currentNavigationController().pushViewController(vc)
                    var errorString = ""
                    errorString.append("服务器故障:\(response.error!.localizedDescription)")
                    if let code = response.error?.responseCode{
                        errorString.append("\n【错误码:\(code)】")
                    }
                    if !ignoreAlert{
                        alert(msg: errorString)
                    }
                    ob.onError(response.error!)
                    return
                }
                if let data = response.data,let jsonString = String(data: data, encoding: String.Encoding.utf8){
                    LogResponse(try! JSONSerialization.jsonObject(with: data))
                    if let next = BaseResponse<T>.deserialize(from: jsonString){
                        switch next.code{
                        case 200:ob.onNext(next)
                        case 506:
                            ob.onError(NetRequestError.Other(next.code,next.msg))
                        case 502: //登录被冻结
                            CommonAlertView.show(isSinple: true, content: next.msg)
                        case 501:
                            CommonAlertView.show(content: "以下内容仅限会员查看,请先成为会员!", completeTitle: "成为会员") {
                                let vc = VIPCenterVC()
                                vc.title = "会员中心"
                                JQ_currentNavigationController().pushViewController(vc)
                                                                                                                                } cancelClouse: {
                            } cancelClouse: {
                                                                                                                                }
                                                                                                                case 401,505,600:
                                                                                                                                if !ignoreAlert{
                                                                                                                                                alertError(msg: "登录失效,请重新登录");ob.onError(NetRequestError.InvaildSession)
                                                                                                                                }
                                                                                                                                sceneDelegate?.needLogin()
                                                                                                                default:
                                                                                                                                //503是手机验证码错误
                                                                                                                                if !ignoreAlert{
                                                                                                                                                DispatchQueue.main.async {
                                                                                                                                                                alertError(msg: "\(next.msg)")
                                                                                                                                                }
                                                                                                                                }
                                                                                                                                ob.onError(NetRequestError.Other(next.code,next.msg))
                                                                                                }
                                                                                }
                                                                }
                                                                ob.onCompleted()
                                                }
                                                return Disposables.create{}
                                }
                }
                            }
                        case 401,505,600:
                            if !ignoreAlert{
                                alertError(msg: "登录失效,请重新登录");ob.onError(NetRequestError.InvaildSession)
                            }
                            sceneDelegate?.needLogin()
                        default:
                            //503是手机验证码错误
                            if !ignoreAlert{
                                DispatchQueue.main.async {
                                    alertError(msg: "\(next.msg)")
                                }
                            }
                            ob.onError(NetRequestError.Other(next.code,next.msg))
                        }
                    }
                }
                ob.onCompleted()
            }
            return Disposables.create{}
        }
    }
}
extension Dictionary {
                mutating func append(dict: Dictionary) {
                                dict.forEach { (key, value) in
                                                self.updateValue(value, forKey: key)
                                }
                }
    mutating func append(dict: Dictionary) {
        dict.forEach { (key, value) in
            self.updateValue(value, forKey: key)
        }
    }
}
func createError(text:String,code:Int)->AFError{
                return AFError.createURLRequestFailed(error: NSError(domain: text, code: code))
    return AFError.createURLRequestFailed(error: NSError(domain: text, code: code))
}
DolphinEnglishLearnStudent/Services/Services.swift
@@ -12,7 +12,7 @@
import JQTools
#if DEBUG
//let All_Url = "http://192.168.110.237:9000"
//let All_Url = "http://vwpmxwbhv59i.guyubao.com"
let All_Url = "https://dollearn.com/api"
#else
let All_Url = "https://dollearn.com/api"
@@ -23,179 +23,179 @@
}
extension Services{
                class func weekList(quarter:Int)->Observable<BaseResponse<[ListenWeekModel]>>{
                                let params = ParamsAppender.build(url: All_Url)
                                params.interface(url: "/study/base/study/weekList")
                                params.append(key: "quarter", value: quarter)
                                return NetworkRequest.request(params: params, method: .get, progress: true)
                }
    class func weekList(quarter:Int)->Observable<BaseResponse<[ListenWeekModel]>>{
        let params = ParamsAppender.build(url: All_Url)
        params.interface(url: "/study/base/study/weekList")
        params.append(key: "quarter", value: quarter)
        return NetworkRequest.request(params: params, method: .get, progress: true)
    }
                /// 自主学习1-听音选图
                class func listenSelectPicture(day:Int,quarter:Int,week:Int)->Observable<BaseResponse<ListenNewModel>>{
                                let params = ParamsAppender.build(url: All_Url)
                                params.interface(url: "/study/base/study/listenSelectPicture")
                                params.append(key: "quarter", value: quarter)
                                params.append(key: "day", value: day)
                                params.append(key: "week", value: week)
                                return NetworkRequest.request(params: params, method: .get, progress: true)
                }
    /// 自主学习1-听音选图
    class func listenSelectPicture(day:Int,quarter:Int,week:Int)->Observable<BaseResponse<ListenNewModel>>{
        let params = ParamsAppender.build(url: All_Url)
        params.interface(url: "/study/base/study/listenSelectPicture")
        params.append(key: "quarter", value: quarter)
        params.append(key: "day", value: day)
        params.append(key: "week", value: week)
        return NetworkRequest.request(params: params, method: .get, progress: true)
    }
                /// 自主学习2-看图选音
                class func pictureSelectVoice(day:Int,quarter:Int,week:Int)->Observable<BaseResponse<ListenNewModel>>{
                                let params = ParamsAppender.build(url: All_Url)
                                params.interface(url: "/study/base/study/pictureSelectVoice")
                                params.append(key: "quarter", value: quarter)
                                params.append(key: "day", value: day)
                                params.append(key: "week", value: week)
                                return NetworkRequest.request(params: params, method: .get, progress: true)
                }
    /// 自主学习2-看图选音
    class func pictureSelectVoice(day:Int,quarter:Int,week:Int)->Observable<BaseResponse<ListenNewModel>>{
        let params = ParamsAppender.build(url: All_Url)
        params.interface(url: "/study/base/study/pictureSelectVoice")
        params.append(key: "quarter", value: quarter)
        params.append(key: "day", value: day)
        params.append(key: "week", value: week)
        return NetworkRequest.request(params: params, method: .get, progress: true)
    }
                /// 自主学习3-归纳排除
                class func induceExclude(day:Int,quarter:Int,week:Int)->Observable<BaseResponse<ListenNewModel>>{
                                let params = ParamsAppender.build(url: All_Url)
                                params.interface(url: "/study/base/study/induceExclude")
                                params.append(key: "quarter", value: quarter)
                                params.append(key: "day", value: day)
                                params.append(key: "week", value: week)
                                return NetworkRequest.request(params: params, method: .get, progress: true)
                }
    /// 自主学习3-归纳排除
    class func induceExclude(day:Int,quarter:Int,week:Int)->Observable<BaseResponse<ListenNewModel>>{
        let params = ParamsAppender.build(url: All_Url)
        params.interface(url: "/study/base/study/induceExclude")
        params.append(key: "quarter", value: quarter)
        params.append(key: "day", value: day)
        params.append(key: "week", value: week)
        return NetworkRequest.request(params: params, method: .get, progress: true)
    }
                /// 自主学习4-有问有答
                class func questionsAndAnswers(day:Int,quarter:Int,week:Int)->Observable<BaseResponse<ListenNewModel>>{
                                let params = ParamsAppender.build(url: All_Url)
                                params.interface(url: "/study/base/study/questionsAndAnswers")
                                params.append(key: "quarter", value: quarter)
                                params.append(key: "day", value: day)
                                params.append(key: "week", value: week)
                                return NetworkRequest.request(params: params, method: .get, progress: true)
                }
    /// 自主学习4-有问有答
    class func questionsAndAnswers(day:Int,quarter:Int,week:Int)->Observable<BaseResponse<ListenNewModel>>{
        let params = ParamsAppender.build(url: All_Url)
        params.interface(url: "/study/base/study/questionsAndAnswers")
        params.append(key: "quarter", value: quarter)
        params.append(key: "day", value: day)
        params.append(key: "week", value: week)
        return NetworkRequest.request(params: params, method: .get, progress: true)
    }
                /// 自主学习5-音图相配
                class func pictureMateVoice(day:Int,quarter:Int,week:Int)->Observable<BaseResponse<ListenNewModel>>{
                                let params = ParamsAppender.build(url: All_Url)
                                params.interface(url: "/study/base/study/pictureMateVoice")
                                params.append(key: "quarter", value: quarter)
                                params.append(key: "day", value: day)
                                params.append(key: "week", value: week)
                                return NetworkRequest.request(params: params, method: .get, progress: true)
                }
    /// 自主学习5-音图相配
    class func pictureMateVoice(day:Int,quarter:Int,week:Int)->Observable<BaseResponse<ListenNewModel>>{
        let params = ParamsAppender.build(url: All_Url)
        params.interface(url: "/study/base/study/pictureMateVoice")
        params.append(key: "quarter", value: quarter)
        params.append(key: "day", value: day)
        params.append(key: "week", value: week)
        return NetworkRequest.request(params: params, method: .get, progress: true)
    }
                /// 完成学习
                class func completeLearing(type:Int,studyTime:Int,studyIds:String,quarter:Int,week:Int,day:Int,accracy:Int)->Observable<BaseResponse<Int>>{
                                let params = ParamsAppender.build(url: All_Url)
                                params.interface(url: "/study/base/study/completeLearning")
                                                .append(key: "type", value: type)
                                                .append(key: "studyTime", value: studyTime)
                                                .append(key: "studyIds", value: studyIds)
                                                .append(key: "week", value: week)
                                                .append(key: "day", value: day)
                                                .append(key: "quarter", value: quarter)
                                                .append(key: "accuracy", value: accracy)
                                return NetworkRequest.request(params: params, method: .post,encoding: JSONEncoding.default, progress: true)
                }
    /// 完成学习
    class func completeLearing(type:Int,studyTime:Int,studyIds:String,quarter:Int,week:Int,day:Int,accracy:Int)->Observable<BaseResponse<Int>>{
        let params = ParamsAppender.build(url: All_Url)
        params.interface(url: "/study/base/study/completeLearning")
            .append(key: "type", value: type)
            .append(key: "studyTime", value: studyTime)
            .append(key: "studyIds", value: studyIds)
            .append(key: "week", value: week)
            .append(key: "day", value: day)
            .append(key: "quarter", value: quarter)
            .append(key: "accuracy", value: accracy)
        return NetworkRequest.request(params: params, method: .post,encoding: JSONEncoding.default, progress: true)
    }
                /// 完成游戏
                class func completeGames(gameId:Int,gameName:String,difficulty:Int,accuracy:Int,useTime:Int)->Observable<BaseResponse<Int>>{
                                let params = ParamsAppender.build(url: All_Url)
                                params.interface(url: "/study/base/study/gameAchievement")
                                                .append(key: "gameId", value: gameId)
                                                .append(key: "accuracy", value: accuracy)
                                                .append(key: "difficulty", value: difficulty)
                                                .append(key: "useTime", value: useTime)
                                                .append(key: "gameName", value: gameName)
                                return NetworkRequest.request(params: params, method: .post,encoding: JSONEncoding.default, progress: true)
                }
    /// 完成游戏
    class func completeGames(gameId:Int,gameName:String,difficulty:Int,accuracy:Int,useTime:Int)->Observable<BaseResponse<Int>>{
        let params = ParamsAppender.build(url: All_Url)
        params.interface(url: "/study/base/study/gameAchievement")
            .append(key: "gameId", value: gameId)
            .append(key: "accuracy", value: accuracy)
            .append(key: "difficulty", value: difficulty)
            .append(key: "useTime", value: useTime)
            .append(key: "gameName", value: gameName)
        return NetworkRequest.request(params: params, method: .post,encoding: JSONEncoding.default, progress: true)
    }
                class func gameHearing(difficulty:Int,quarter:Int,week:Int)->Observable<BaseResponse<Listen1Model>>{
                                let params = ParamsAppender.build(url: All_Url)
                                params.interface(url: "/study/base/study/gameHearing")
                                params.append(key: "quarter", value: quarter)
                                params.append(key: "difficulty", value: difficulty)
                                params.append(key: "week", value: week)
                                return NetworkRequest.request(params: params, method: .get, progress: true)
                }
    class func gameHearing(difficulty:Int,quarter:Int,week:Int)->Observable<BaseResponse<Listen1Model>>{
        let params = ParamsAppender.build(url: All_Url)
        params.interface(url: "/study/base/study/gameHearing")
        params.append(key: "quarter", value: quarter)
        params.append(key: "difficulty", value: difficulty)
        params.append(key: "week", value: week)
        return NetworkRequest.request(params: params, method: .get, progress: true)
    }
                class func gameMemory(quarter:Int,week:Int)->Observable<BaseResponse<Listen1Model>>{
                                let params = ParamsAppender.build(url: All_Url)
                                params.interface(url: "/study/base/study/gameMemory")
                                params.append(key: "quarter", value: quarter)
                                params.append(key: "week", value: week)
                                return NetworkRequest.request(params: params, method: .get, progress: true)
                }
    class func gameMemory(quarter:Int,week:Int)->Observable<BaseResponse<Listen1Model>>{
        let params = ParamsAppender.build(url: All_Url)
        params.interface(url: "/study/base/study/gameMemory")
        params.append(key: "quarter", value: quarter)
        params.append(key: "week", value: week)
        return NetworkRequest.request(params: params, method: .get, progress: true)
    }
                class func frameworkMemory(quarter:Int,week:Int)->Observable<BaseResponse<Listen1Model>>{
                                let params = ParamsAppender.build(url: All_Url)
                                params.interface(url: "/study/base/study/frameworkMemory")
                                params.append(key: "quarter", value: quarter)
                                params.append(key: "week", value: week)
                                return NetworkRequest.request(params: params, method: .get, progress: true)
                }
    class func frameworkMemory(quarter:Int,week:Int)->Observable<BaseResponse<Listen1Model>>{
        let params = ParamsAppender.build(url: All_Url)
        params.interface(url: "/study/base/study/frameworkMemory")
        params.append(key: "quarter", value: quarter)
        params.append(key: "week", value: week)
        return NetworkRequest.request(params: params, method: .get, progress: true)
    }
                class func lookpictureDbu(quarter:Int,week:Int)->Observable<BaseResponse<Listen1Model>>{
                                let params = ParamsAppender.build(url: All_Url)
                                params.interface(url: "/study/base/study/lookPictureDbu")
                                params.append(key: "quarter", value: quarter)
                                params.append(key: "week", value: week)
                                return NetworkRequest.request(params: params, method: .get, progress: true)
                }
    class func lookpictureDbu(quarter:Int,week:Int)->Observable<BaseResponse<Listen1Model>>{
        let params = ParamsAppender.build(url: All_Url)
        params.interface(url: "/study/base/study/lookPictureDbu")
        params.append(key: "quarter", value: quarter)
        params.append(key: "week", value: week)
        return NetworkRequest.request(params: params, method: .get, progress: true)
    }
                class  func completeStory(storyId:Int,accuracy:Int,studyTime:Int,type:Int)->Observable<BaseResponse<Int>>{
                                let params = ParamsAppender.build(url: All_Url)
                                params.interface(url: "/study/base/study/completeStory")
                                params.append(key: "storyId", value: storyId)
                                params.append(key: "accuracy", value: accuracy)
                                params.append(key: "studyTime", value: studyTime)
                                params.append(key: "type", value: type)
                                return NetworkRequest.request(params: params, method: .get, progress: true)
                }
    class  func completeStory(storyId:Int,accuracy:Int,studyTime:Int,type:Int)->Observable<BaseResponse<Int>>{
        let params = ParamsAppender.build(url: All_Url)
        params.interface(url: "/study/base/study/completeStory")
        params.append(key: "storyId", value: storyId)
        params.append(key: "accuracy", value: accuracy)
        params.append(key: "studyTime", value: studyTime)
        params.append(key: "type", value: type)
        return NetworkRequest.request(params: params, method: .get, progress: true)
    }
                class func studySchedule(week:Int)->Observable<BaseResponse<StudyScheduleModel>>{
                                let params = ParamsAppender.build(url: All_Url)
                                params.interface(url: "/study/base/study/studySchedule")
                                params.append(key: "week", value: week)
                                return NetworkRequest.request(params: params, method: .get, progress: true)
                }
    class func studySchedule(week:Int)->Observable<BaseResponse<StudyScheduleModel>>{
        let params = ParamsAppender.build(url: All_Url)
        params.interface(url: "/study/base/study/studySchedule")
        params.append(key: "week", value: week)
        return NetworkRequest.request(params: params, method: .get, progress: true)
    }
                class func teamSchedule(type:ListenType,week:Int,day:Int)->Observable<BaseResponse<TeamScheduleModel>>{
                                let params = ParamsAppender.build(url: All_Url)
                                params.interface(url: "/study/base/study/teamSchedule")
                                params.append(key: "type", value: type.rawValue)
                                params.append(key: "week", value: week)
                                params.append(key: "day", value: day)
                                return NetworkRequest.request(params: params, method: .get, progress: false)
                }
    class func teamSchedule(type:ListenType,week:Int,day:Int)->Observable<BaseResponse<TeamScheduleModel>>{
        let params = ParamsAppender.build(url: All_Url)
        params.interface(url: "/study/base/study/teamSchedule")
        params.append(key: "type", value: type.rawValue)
        params.append(key: "week", value: week)
        params.append(key: "day", value: day)
        return NetworkRequest.request(params: params, method: .get, progress: false)
    }
                class func parentPage()->Observable<BaseResponse<String>>{
                                let params = ParamsAppender.build(url: All_Url)
                                params.interface(url: "/study/base/user/studyPage")
                                return NetworkRequest.request(params: params, method: .post, progress: false)
                }
    class func parentPage()->Observable<BaseResponse<String>>{
        let params = ParamsAppender.build(url: All_Url)
        params.interface(url: "/study/base/user/studyPage")
        return NetworkRequest.request(params: params, method: .post, progress: false)
    }
                class func promptVoice()->Observable<BaseResponse<PromptVoiceModel>>{
                                let params = ParamsAppender.build(url: All_Url)
                                params.interface(url: "/study/base/study/promptVoice")
                                return NetworkRequest.request(params: params, method: .get, progress: false)
                }
    class func promptVoice()->Observable<BaseResponse<PromptVoiceModel>>{
        let params = ParamsAppender.build(url: All_Url)
        params.interface(url: "/study/base/study/promptVoice")
        return NetworkRequest.request(params: params, method: .get, progress: false)
    }
}
// MARK: -- 登录部分
extension Services{
                class func sendPhoneCode(phone:String)->Observable<BaseResponse<SimpleModel>>{
                                let params = ParamsAppender.build(url: All_Url)
                                params.interface(url: "/study/base/user/sendPhoneCode")
                                params.append(key: "phone", value: phone)
                                return NetworkRequest.request(params: params, method: .get, progress: true)
                }
    class func sendPhoneCode(phone:String)->Observable<BaseResponse<SimpleModel>>{
        let params = ParamsAppender.build(url: All_Url)
        params.interface(url: "/study/base/user/sendPhoneCode")
        params.append(key: "phone", value: phone)
        return NetworkRequest.request(params: params, method: .get, progress: true)
    }
                class func login(phone:String,code:String)->Observable<BaseResponse<LoginModel>>{
                                                let params = ParamsAppender.build(url: All_Url)
                                                params.interface(url: "/study/base/user/studyLogin")
                                params.append(key: "phone", value: phone)
                                params.append(key: "phoneCode", value: code)
                                return NetworkRequest.request(params: params, method: .post,encoding: JSONEncoding.default, progress: true)
                }
    class func login(phone:String,code:String)->Observable<BaseResponse<LoginModel>>{
        let params = ParamsAppender.build(url: All_Url)
        params.interface(url: "/study/base/user/studyLogin")
        params.append(key: "phone", value: phone)
        params.append(key: "phoneCode", value: code)
        return NetworkRequest.request(params: params, method: .post,encoding: JSONEncoding.default, progress: true)
    }
}
// MARK: -- 首页
@@ -206,232 +206,256 @@
// MARK: -- 商品
extension Services{
                class func goodRecommend()->Observable<BaseResponse<[RecommendModel]>>{
                                let params = ParamsAppender.build(url: All_Url)
                                params.interface(url: "/study/base/study/goodRecommend")
                                return NetworkRequest.request(params: params, method: .get, progress: false,ignoreAlert: true)
                }
    class func goodRecommend()->Observable<BaseResponse<[RecommendModel]>>{
        let params = ParamsAppender.build(url: All_Url)
        params.interface(url: "/study/base/study/goodRecommend")
        return NetworkRequest.request(params: params, method: .get, progress: false,ignoreAlert: true)
    }
                class func goodsList(keywords:String,page:Int,pageSize:Int = 20,type:[String])->Observable<BaseResponse<BaseResponseList<MarketModel>>>{
                                let params = ParamsAppender.build(url: All_Url)
                                params.interface(url: "/goods/base/goods/goodListStudy")
                                                .append(key: "keywords", value: keywords)
                                                .append(key: "pageNumber", value: page)
                                                .append(key: "pageSize", value: pageSize)
                                                .append(key: "type", value: type)
                                return NetworkRequest.request(params: params, method: .post,encoding: JSONEncoding.default, progress: false)
                }
    class func goodsList(keywords:String,page:Int,pageSize:Int = 20,type:[String])->Observable<BaseResponse<BaseResponseList<MarketModel>>>{
        let params = ParamsAppender.build(url: All_Url)
        params.interface(url: "/goods/base/goods/goodListStudy")
            .append(key: "keywords", value: keywords)
            .append(key: "pageNumber", value: page)
            .append(key: "pageSize", value: pageSize)
            .append(key: "type", value: type)
        return NetworkRequest.request(params: params, method: .post,encoding: JSONEncoding.default, progress: false)
    }
                class func getIntegral()->Observable<BaseResponse<Int>>{
                                let params = ParamsAppender.build(url: All_Url)
                                params.interface(url: "/study/base/study/getIntegralStudy")
                                return NetworkRequest.request(params: params, method: .get, progress: false)
                }
    class func getIntegral()->Observable<BaseResponse<Int>>{
        let params = ParamsAppender.build(url: All_Url)
        params.interface(url: "/study/base/study/getIntegralStudy")
        return NetworkRequest.request(params: params, method: .get, progress: false)
    }
                class func goodTypeStudy()->Observable<BaseResponse<[MarketTypeModel]>>{
                                let params = ParamsAppender.build(url: All_Url)
                                params.interface(url: "/goods/base/goods/goodTypeStudy")
                                return NetworkRequest.request(params: params, method: .get, progress: false)
                }
    class func goodTypeStudy()->Observable<BaseResponse<[MarketTypeModel]>>{
        let params = ParamsAppender.build(url: All_Url)
        params.interface(url: "/goods/base/goods/goodTypeStudy")
        return NetworkRequest.request(params: params, method: .get, progress: false)
    }
                class func goodsDetail(goodsId:Int)->Observable<BaseResponse<MarketDetailModel>>{
                                let params = ParamsAppender.build(url: All_Url)
                                params.interface(url: "/goods/base/goods/goodDetail")
                                                .append(key: "goodId", value: goodsId)
                                return NetworkRequest.request(params: params, method: .get, progress: true)
                }
    class func goodsDetail(goodsId:Int)->Observable<BaseResponse<MarketDetailModel>>{
        let params = ParamsAppender.build(url: All_Url)
        params.interface(url: "/goods/base/goods/goodDetail")
            .append(key: "goodId", value: goodsId)
        return NetworkRequest.request(params: params, method: .get, progress: true)
    }
                class func addressList()->Observable<BaseResponse<[AddressModel]>>{
                                let params = ParamsAppender.build(url: All_Url)
                                params.interface(url: "/goods/base/goods/shopAddress")
                                return NetworkRequest.request(params: params, method: .get, progress: false)
                }
    class func addressList()->Observable<BaseResponse<[AddressModel]>>{
        let params = ParamsAppender.build(url: All_Url)
        params.interface(url: "/goods/base/goods/shopAddress")
        return NetworkRequest.request(params: params, method: .get, progress: false)
    }
                class func deleteAddress(id:Int)->Observable<BaseResponse<SimpleModel>>{
                                let params = ParamsAppender.build(url: All_Url)
                                params.interface(url: "/goods/base/goods/addressDelete")
                                                .append(key: "id", value: id)
                                return NetworkRequest.request(params: params, method: .get, progress: true)
                }
    class func deleteAddress(id:Int)->Observable<BaseResponse<SimpleModel>>{
        let params = ParamsAppender.build(url: All_Url)
        params.interface(url: "/goods/base/goods/addressDelete")
            .append(key: "id", value: id)
        return NetworkRequest.request(params: params, method: .get, progress: true)
    }
                class func redeemNow(goodId:Int)->Observable<BaseResponse<MarketDetailModel>>{
                                let params = ParamsAppender.build(url: All_Url)
                                params.interface(url: "/goods/base/goods/redeemNow")
                                                .append(key: "goodId", value: goodId)
                                return NetworkRequest.request(params: params, method: .get, progress: true)
                }
    class func redeemNow(goodId:Int)->Observable<BaseResponse<MarketDetailModel>>{
        let params = ParamsAppender.build(url: All_Url)
        params.interface(url: "/goods/base/goods/redeemNow")
            .append(key: "goodId", value: goodId)
        return NetworkRequest.request(params: params, method: .get, progress: true)
    }
                class func addressTree()->Observable<BaseResponse<[AddressTreeModel]>>{
                                let params = ParamsAppender.build(url: All_Url)
                                params.interface(url: "/goods/base/goods/addressTree")
                                return NetworkRequest.request(params: params, method: .get, progress: true)
                }
    class func addressTree()->Observable<BaseResponse<[AddressTreeModel]>>{
        let params = ParamsAppender.build(url: All_Url)
        params.interface(url: "/goods/base/goods/addressTree")
        return NetworkRequest.request(params: params, method: .get, progress: true)
    }
                class func addressSaveOrUpdate(id:Int?,province:AddressTreeModel?,city:AddressTreeModel?,country:AddressTreeModel?,userName:String?,userPhone:String?,isDefault:Bool,detailAddress:String)->Observable<BaseResponse<SimpleModel>>{
                                let params = ParamsAppender.build(url: All_Url)
                                params.interface(url: "/goods/base/goods/addressSaveOrUpdate")
                                                .append(key: "id", value: id)
                                                .append(key: "address", value: detailAddress)
                                                .append(key: "city", value: city?.name)
                                                .append(key: "cityCode", value: city?.code)
                                                .append(key: "province", value: province?.name)
                                                .append(key: "provinceCode", value: province?.code)
                                                .append(key: "recipient", value: userName)
                                                .append(key: "recipientPhone", value: userPhone)
                                                .append(key: "isDefault", value: (isDefault == true) ? 1:0)
                                return NetworkRequest.request(params: params, method: .post,encoding: JSONEncoding.default, progress: true)
                }
    class func addressSaveOrUpdate(id:Int?,province:AddressTreeModel?,city:AddressTreeModel?,country:AddressTreeModel?,userName:String?,userPhone:String?,isDefault:Bool,detailAddress:String)->Observable<BaseResponse<SimpleModel>>{
        let params = ParamsAppender.build(url: All_Url)
        params.interface(url: "/goods/base/goods/addressSaveOrUpdate")
            .append(key: "id", value: id)
            .append(key: "address", value: detailAddress)
            .append(key: "city", value: city?.name)
            .append(key: "cityCode", value: city?.code)
            .append(key: "province", value: province?.name)
            .append(key: "provinceCode", value: province?.code)
            .append(key: "recipient", value: userName)
            .append(key: "recipientPhone", value: userPhone)
            .append(key: "isDefault", value: (isDefault == true) ? 1:0)
        return NetworkRequest.request(params: params, method: .post,encoding: JSONEncoding.default, progress: true)
    }
                class func setDefaultStudy(id:Int)->Observable<BaseResponse<SimpleModel>>{
                                let params = ParamsAppender.build(url: All_Url)
                                params.interface(url: "/goods/base/goods/setDefaultStudy")
                                                .append(key: "id", value: id)
                                return NetworkRequest.request(params: params, method: .get, progress: true)
                }
    class func setDefaultStudy(id:Int)->Observable<BaseResponse<SimpleModel>>{
        let params = ParamsAppender.build(url: All_Url)
        params.interface(url: "/goods/base/goods/setDefaultStudy")
            .append(key: "id", value: id)
        return NetworkRequest.request(params: params, method: .get, progress: true)
    }
                class func goodsExchangeStudy(goodsId:Int,number:Int,orderNumber:String,recipientId:Int,remark:String)->Observable<BaseResponse<SimpleModel>>{
                                let params = ParamsAppender.build(url: All_Url)
                                params.interface(url: "/goods/base/goods/goodExchangeStudy")
                                                .append(key: "goodId", value: goodsId)
                                                .append(key: "number", value: number)
                                                .append(key: "orderNumber", value: orderNumber)
                                                .append(key: "recipientId", value: recipientId)
                                                .append(key: "remark", value: remark)
                                return NetworkRequest.request(params: params, method: .post,encoding: JSONEncoding.default, progress: true,ignoreAlert: true)
                }
    class func goodsExchangeStudy(goodsId:Int,number:Int,orderNumber:String,recipientId:Int,remark:String)->Observable<BaseResponse<SimpleModel>>{
        let params = ParamsAppender.build(url: All_Url)
        params.interface(url: "/goods/base/goods/goodExchangeStudy")
            .append(key: "goodId", value: goodsId)
            .append(key: "number", value: number)
            .append(key: "orderNumber", value: orderNumber)
            .append(key: "recipientId", value: recipientId)
            .append(key: "remark", value: remark)
        return NetworkRequest.request(params: params, method: .post,encoding: JSONEncoding.default, progress: true,ignoreAlert: true)
    }
                class func userInfo()->Observable<BaseResponse<UserInfoModel>>{
                                let params = ParamsAppender.build(url: All_Url)
                                params.interface(url: "/study/base/user/userInfo")
                                return NetworkRequest.request(params: params, method: .get, progress: false)
                }
    class func userInfo()->Observable<BaseResponse<UserInfoModel>>{
        let params = ParamsAppender.build(url: All_Url)
        params.interface(url: "/study/base/user/userInfo")
        return NetworkRequest.request(params: params, method: .get, progress: false)
    }
                class func integralDetail(pageNum:Int,pageSize:Int = 20,time:String?)->Observable<BaseResponse<BaseResponseList<IntegralModel>>>{
                                let params = ParamsAppender.build(url: All_Url)
                                params.interface(url: "/study/base/study/integralDetail")
                                                .append(key: "pageNum", value: pageNum)
                                                .append(key: "pageSize", value: pageSize)
                                                .append(key: "time", value: time)
                                return NetworkRequest.request(params: params, method: .get, progress: false)
                }
    class func integralDetail(pageNum:Int,pageSize:Int = 20,time:String?)->Observable<BaseResponse<BaseResponseList<IntegralModel>>>{
        let params = ParamsAppender.build(url: All_Url)
        params.interface(url: "/study/base/study/integralDetail")
            .append(key: "pageNum", value: pageNum)
            .append(key: "pageSize", value: pageSize)
            .append(key: "time", value: time)
        return NetworkRequest.request(params: params, method: .get, progress: false)
    }
                class func exchangeRecord(page:Int,pageSize:Int = 20)->Observable<BaseResponse<BaseResponseList<ExchangeRecordModel>>>{
                                let params = ParamsAppender.build(url: All_Url)
                                params.interface(url: "/goods/base/goods/exchangeRecord")
                                                .append(key: "pageNumber", value: page)
                                                .append(key: "pageSize", value: pageSize)
                                return NetworkRequest.request(params: params, method: .get, progress: false)
                }
    class func exchangeRecord(page:Int,pageSize:Int = 20)->Observable<BaseResponse<BaseResponseList<ExchangeRecordModel>>>{
        let params = ParamsAppender.build(url: All_Url)
        params.interface(url: "/goods/base/goods/exchangeRecord")
            .append(key: "pageNumber", value: page)
            .append(key: "pageSize", value: pageSize)
        return NetworkRequest.request(params: params, method: .get, progress: false)
    }
                class func studyGamesRecord()->Observable<BaseResponse<StudyGamesModel>>{
                                let params = ParamsAppender.build(url: All_Url)
                                params.interface(url: "/study/base/study/studyRecord")
                                return NetworkRequest.request(params: params, method: .get, progress: true)
                }
    class func studyGamesRecord()->Observable<BaseResponse<StudyGamesModel>>{
        let params = ParamsAppender.build(url: All_Url)
        params.interface(url: "/study/base/study/studyRecord")
        return NetworkRequest.request(params: params, method: .get, progress: true)
    }
                class func logoutStudy()->Observable<BaseResponse<SimpleModel>>{
                                let params = ParamsAppender.build(url: All_Url)
                                params.interface(url: "/study/base/user/logoutStudy")
                                return NetworkRequest.request(params: params, method: .post,encoding: JSONEncoding.default, progress: true)
                }
    class func logoutStudy()->Observable<BaseResponse<SimpleModel>>{
        let params = ParamsAppender.build(url: All_Url)
        params.interface(url: "/study/base/user/logoutStudy")
        return NetworkRequest.request(params: params, method: .post,encoding: JSONEncoding.default, progress: true)
    }
                class func exitLearning(type:Int,quarter:Int,week:Int,day:Int,teamIds:[String],topicIds:[String],answerNumber:Int,correctNumber:Int,studyTime:Int,schedule:Int)->Observable<BaseResponse<SimpleModel>>{
                                let params = ParamsAppender.build(url: All_Url)
                                params.interface(url: "/study/base/study/exitLearning")
                                                .append(key: "week", value: week)
                                                .append(key: "day", value: day)
                                                .append(key: "teamIds", value: teamIds.joined(separator: ","))
                                                .append(key: "topicIds", value: topicIds.joined(separator: ","))
                                                .append(key: "type", value: type)
                                                .append(key: "answerNumber", value: answerNumber)
                                                .append(key: "correctNumber", value: correctNumber)
                                                .append(key: "studyTime", value: studyTime)
                                                .append(key: "schedule", value: schedule)
                                                .append(key: "quarter", value: quarter)
                                return NetworkRequest.request(params: params, method: .post,encoding: JSONEncoding.default, progress: true)
                }
    class func exitLearning(type:Int,quarter:Int,week:Int,day:Int,teamIds:[String],topicIds:[String],answerNumber:Int,correctNumber:Int,studyTime:Int,schedule:Int)->Observable<BaseResponse<SimpleModel>>{
        let params = ParamsAppender.build(url: All_Url)
        params.interface(url: "/study/base/study/exitLearning")
            .append(key: "week", value: week)
            .append(key: "day", value: day)
            .append(key: "teamIds", value: teamIds.joined(separator: ","))
            .append(key: "topicIds", value: topicIds.joined(separator: ","))
            .append(key: "type", value: type)
            .append(key: "answerNumber", value: answerNumber)
            .append(key: "correctNumber", value: correctNumber)
            .append(key: "studyTime", value: studyTime)
            .append(key: "schedule", value: schedule)
            .append(key: "quarter", value: quarter)
        return NetworkRequest.request(params: params, method: .post,encoding: JSONEncoding.default, progress: true)
    }
                class func exitGameOrStory(studyTime:Int)->Observable<BaseResponse<SimpleModel>>{
                                let params = ParamsAppender.build(url: All_Url)
                                params.interface(url: "/study/base/study/exitGameOrStory")
                                                .append(key: "studyTime", value: studyTime)
                                return NetworkRequest.request(params: params, method: .get, progress: false)
                }
    class func exitGameOrStory(studyTime:Int)->Observable<BaseResponse<SimpleModel>>{
        let params = ParamsAppender.build(url: All_Url)
        params.interface(url: "/study/base/study/exitGameOrStory")
            .append(key: "studyTime", value: studyTime)
        return NetworkRequest.request(params: params, method: .get, progress: false)
    }
                class func updateOrderAddress(orderId:Int,recipientId:Int)->Observable<BaseResponse<SimpleModel>>{
                                let params = ParamsAppender.build(url: All_Url)
                                params.interface(url: "/goods/base/goods/updateOrderAddress")
                                                .append(key: "orderId", value: orderId)
                                                .append(key: "recipientId", value: recipientId)
                                return NetworkRequest.request(params: params, method: .get, progress: true)
                }
    class func updateOrderAddress(orderId:Int,recipientId:Int)->Observable<BaseResponse<SimpleModel>>{
        let params = ParamsAppender.build(url: All_Url)
        params.interface(url: "/goods/base/goods/updateOrderAddress")
            .append(key: "orderId", value: orderId)
            .append(key: "recipientId", value: recipientId)
        return NetworkRequest.request(params: params, method: .get, progress: true)
    }
                class func onlineDuration()->Observable<BaseResponse<Int>>{
                                let params = ParamsAppender.build(url: All_Url)
                                params.interface(url: "/study/base/study/onlineDuration")
                                return NetworkRequest.request(params: params, method: .get, progress: false)
                }
    class func onlineDuration()->Observable<BaseResponse<Int>>{
        let params = ParamsAppender.build(url: All_Url)
        params.interface(url: "/study/base/study/onlineDuration")
        return NetworkRequest.request(params: params, method: .get, progress: false)
    }
                class func giveIntegral()->Observable<BaseResponse<SimpleModel>>{
                                let params = ParamsAppender.build(url: All_Url)
                                params.interface(url: "/study/base/study/giveIntegral")
                                return NetworkRequest.request(params: params, method: .get, progress: false)
                }
                class func confirmStudy(id:Int)->Observable<BaseResponse<SimpleModel>>{
                                let params = ParamsAppender.build(url: All_Url)
                                params.interface(url: "/goods/base/goods/confirmStudy")
                                                .append(key: "id", value: id)
                                return NetworkRequest.request(params: params, method: .get, progress: false)
                }
                class func shareInfo()->Observable<BaseResponse<ShareInfoModel>>{
                                let params = ParamsAppender.build(url: All_Url)
                                params.interface(url: "/study/base/user/shareInfo")
                                return NetworkRequest.request(params: params, method: .get, progress: false)
                }
                class func vipInfo()->Observable<BaseResponse<[VIPInfoModel]>>{
                                let params = ParamsAppender.build(url: All_Url)
                                params.interface(url: "/study/base/user/vipInfoStudy")
                                return NetworkRequest.request(params: params, method: .post, progress: false)
                }
                class func loginOff()->Observable<BaseResponse<SimpleModel>>{
                                let params = ParamsAppender.build(url: All_Url)
                                params.interface(url: "/study/base/user/deleteUserStudy")
                                return NetworkRequest.request(params: params, method: .post, progress: true)
                }
    class func giveIntegral()->Observable<BaseResponse<SimpleModel>>{
        let params = ParamsAppender.build(url: All_Url)
        params.interface(url: "/study/base/study/giveIntegral")
        return NetworkRequest.request(params: params, method: .get, progress: false)
    }
                /// 支付
                /// - Parameters:
                ///   - count: 月份,12
                ///   - price: 价格
                class func orderStudent(count:Int,price:Double,payType:Int = 3)->Observable<BaseResponse<PaymentInfoModel>>{
                                let params = ParamsAppender.build(url: All_Url)
                                params.interface(url: "/study/base/user/orderStudent")
                                                .append(key: "count", value: count)
                                                .append(key: "price", value: price)
                                                .append(key: "payType", value: payType)
                                return NetworkRequest.request(params: params, method: .post, progress: true)
                }
    ///   - status: 状态1灰色未答题 2绿色正确 3红色错误
    class func answerQuestion(id:Int,status:Int)->Observable<BaseResponse<SimpleModel>>{
        let params = ParamsAppender.build(url: All_Url)
        params.interface(url: "/study/base/study/answerQuestion")
            .append(key: "id", value: id)
            .append(key: "status", value: status)
        return NetworkRequest.request(params: params, method: .post,encoding: JSONEncoding.default, progress: false)
    }
                class func pay(orderId:Int,transactionIdentifier:String,payType:Int = 3)->Observable<BaseResponse<SimpleModel>>{
                                let params = ParamsAppender.build(url: All_Url)
                                params.interface(url: "/study/base/user/pay")
                                                .append(key: "orderId", value: orderId)
                                                .append(key: "transactionIdentifier", value: transactionIdentifier)
                                                .append(key: "payType", value: payType)
                                return NetworkRequest.request(params: params, method: .post,encoding: JSONEncoding.default, progress: true)
                }
    /// 2.0新增-重新开始
    /// - Parameters:
    ///   - day: 天
    ///   - type: 题目类型(1:听音选图;2:看图选音;3:归纳排除;4:有问有答;5:音图相配)
    ///   - week: 周目
    class func restart(day:Int,type:Int,week:Int)->Observable<BaseResponse<SimpleModel>>{
        let params = ParamsAppender.build(url: All_Url)
        params.interface(url: "/study/base/study/restart")
            .append(key: "day", value: day)
            .append(key: "type", value: type)
            .append(key: "week", value: week)
        return NetworkRequest.request(params: params, method: .post,encoding: JSONEncoding.default, progress: false)
    }
                class func queryOrderState(orderId:Int)->Observable<BaseResponse<Bool>>{
                                let params = ParamsAppender.build(url: All_Url)
                                params.interface(url: "/study/base/user/queryOrderState")
                                                .append(key: "orderId", value: orderId)
                                return NetworkRequest.request(params: params, method: .post, progress: false)
                }
    class func confirmStudy(id:Int)->Observable<BaseResponse<SimpleModel>>{
        let params = ParamsAppender.build(url: All_Url)
        params.interface(url: "/goods/base/goods/confirmStudy")
            .append(key: "id", value: id)
        return NetworkRequest.request(params: params, method: .get, progress: false)
    }
    class func shareInfo()->Observable<BaseResponse<ShareInfoModel>>{
        let params = ParamsAppender.build(url: All_Url)
        params.interface(url: "/study/base/user/shareInfo")
        return NetworkRequest.request(params: params, method: .get, progress: false)
    }
    class func vipInfo()->Observable<BaseResponse<[VIPInfoModel]>>{
        let params = ParamsAppender.build(url: All_Url)
        params.interface(url: "/study/base/user/vipInfoStudy")
        return NetworkRequest.request(params: params, method: .post, progress: false)
    }
    class func loginOff()->Observable<BaseResponse<SimpleModel>>{
        let params = ParamsAppender.build(url: All_Url)
        params.interface(url: "/study/base/user/deleteUserStudy")
        return NetworkRequest.request(params: params, method: .post, progress: true)
    }
    /// 支付
    /// - Parameters:
    ///   - count: 月份,12
    ///   - price: 价格
    class func orderStudent(count:Int,price:Double,payType:Int = 3)->Observable<BaseResponse<PaymentInfoModel>>{
        let params = ParamsAppender.build(url: All_Url)
        params.interface(url: "/study/base/user/orderStudent")
            .append(key: "count", value: count)
            .append(key: "price", value: price)
            .append(key: "payType", value: payType)
        return NetworkRequest.request(params: params, method: .post, progress: true)
    }
    class func pay(orderId:Int,transactionIdentifier:String,payType:Int = 3)->Observable<BaseResponse<SimpleModel>>{
        let params = ParamsAppender.build(url: All_Url)
        params.interface(url: "/study/base/user/pay")
            .append(key: "orderId", value: orderId)
            .append(key: "transactionIdentifier", value: transactionIdentifier)
            .append(key: "payType", value: payType)
        return NetworkRequest.request(params: params, method: .post,encoding: JSONEncoding.default, progress: true)
    }
    class func queryOrderState(orderId:Int)->Observable<BaseResponse<Bool>>{
        let params = ParamsAppender.build(url: All_Url)
        params.interface(url: "/study/base/user/queryOrderState")
            .append(key: "orderId", value: orderId)
        return NetworkRequest.request(params: params, method: .post, progress: false)
    }
    class func getIsOpen()->Observable<BaseResponse<Bool>>{
        let params = ParamsAppender.build(url: All_Url)
@@ -442,28 +466,28 @@
}
extension Services{
                class func getAgreement(type:AgreementType)->Observable<BaseResponse<String>>{
                                let params = ParamsAppender.build(url: All_Url)
                                params.interface(url: "/study/base/user/getProtocol")
                                params.append(key: "type", value: type.rawValue)
                                return NetworkRequest.request(params: params, method: .post, progress: true)
                }
    class func getAgreement(type:AgreementType)->Observable<BaseResponse<String>>{
        let params = ParamsAppender.build(url: All_Url)
        params.interface(url: "/study/base/user/getProtocol")
        params.append(key: "type", value: type.rawValue)
        return NetworkRequest.request(params: params, method: .post, progress: true)
    }
}
extension Services{
                static func startNetworkMonitor(){
                                let manager = NetworkReachabilityManager(host: All_Url)
                                manager?.startListening(onUpdatePerforming: { status in
                                                switch status {
                                                                case .notReachable:alertError(msg: "当前网络不可用")
                                                                case .reachable(let type):
                                                                                switch type{
                                                                                                case .ethernetOrWiFi:alert(msg: "当前为Wi-Fi网络")
                                                                                                case .cellular:alert(msg: "当前为移动网络")
                                                                                }
                                                                default:break
                                                }
                                })
                }
    static func startNetworkMonitor(){
        let manager = NetworkReachabilityManager(host: All_Url)
        manager?.startListening(onUpdatePerforming: { status in
            switch status {
            case .notReachable:alertError(msg: "当前网络不可用")
            case .reachable(let type):
                switch type{
                case .ethernetOrWiFi:alert(msg: "当前为Wi-Fi网络")
                case .cellular:alert(msg: "当前为移动网络")
                }
            default:break
            }
        })
    }
}
Podfile.lock
New file
@@ -0,0 +1,477 @@
PODS:
  - Alamofire (5.8.1)
  - CryptoSwift (1.8.0)
  - Differentiator (5.0.0)
  - EmptyDataSet-Swift (5.0.0)
  - FFPage (3.0.0)
  - HandyJSON (5.0.2)
  - IQKeyboardManager (6.5.16)
  - IQKeyboardManagerSwift (6.5.16)
  - JQTools (0.1.5):
    - EmptyDataSet-Swift
    - HandyJSON
    - IQKeyboardManager
    - IQKeyboardManagerSwift
    - MJRefresh
    - ObjectMapper
    - QMUIKit
    - RxCocoa
    - RxDataSources
    - RxSwift
    - SDWebImage
    - SnapKit
    - SVProgressHUD
    - TZImagePickerController
    - UserDefaultsStore (~> 1.5.0)
    - VTMagic
    - XCGLogger
  - Lantern (1.1.5)
  - MJRefresh (3.7.6)
  - ObjcExceptionBridging (1.0.1):
    - ObjcExceptionBridging/ObjcExceptionBridging (= 1.0.1)
  - ObjcExceptionBridging/ObjcExceptionBridging (1.0.1)
  - ObjectMapper (4.2.0)
  - QMUIKit (4.7.0):
    - QMUIKit/QMUIComponents (= 4.7.0)
    - QMUIKit/QMUICore (= 4.7.0)
    - QMUIKit/QMUILog (= 4.7.0)
    - QMUIKit/QMUIMainFrame (= 4.7.0)
    - QMUIKit/QMUIResources (= 4.7.0)
    - QMUIKit/QMUIWeakObjectContainer (= 4.7.0)
  - QMUIKit/QMUIComponents (4.7.0):
    - QMUIKit/QMUIComponents/NavigationBarTransition (= 4.7.0)
    - QMUIKit/QMUIComponents/QMUIAlertController (= 4.7.0)
    - QMUIKit/QMUIComponents/QMUIAnimation (= 4.7.0)
    - QMUIKit/QMUIComponents/QMUIAppearance (= 4.7.0)
    - QMUIKit/QMUIComponents/QMUIAssetLibrary (= 4.7.0)
    - QMUIKit/QMUIComponents/QMUIBadge (= 4.7.0)
    - QMUIKit/QMUIComponents/QMUIButton (= 4.7.0)
    - QMUIKit/QMUIComponents/QMUICAAnimationExtension (= 4.7.0)
    - QMUIKit/QMUIComponents/QMUICALayerExtension (= 4.7.0)
    - QMUIKit/QMUIComponents/QMUICellHeightCache (= 4.7.0)
    - QMUIKit/QMUIComponents/QMUICellHeightKeyCache (= 4.7.0)
    - QMUIKit/QMUIComponents/QMUICellSizeKeyCache (= 4.7.0)
    - QMUIKit/QMUIComponents/QMUICollectionViewPagingLayout (= 4.7.0)
    - QMUIKit/QMUIComponents/QMUIConsole (= 4.7.0)
    - QMUIKit/QMUIComponents/QMUIDialogViewController (= 4.7.0)
    - QMUIKit/QMUIComponents/QMUIEmotionInputManager (= 4.7.0)
    - QMUIKit/QMUIComponents/QMUIEmotionView (= 4.7.0)
    - QMUIKit/QMUIComponents/QMUIEmptyView (= 4.7.0)
    - QMUIKit/QMUIComponents/QMUIFloatLayoutView (= 4.7.0)
    - QMUIKit/QMUIComponents/QMUIGridView (= 4.7.0)
    - QMUIKit/QMUIComponents/QMUIImagePickerLibrary (= 4.7.0)
    - QMUIKit/QMUIComponents/QMUIImagePreviewView (= 4.7.0)
    - QMUIKit/QMUIComponents/QMUIKeyboardManager (= 4.7.0)
    - QMUIKit/QMUIComponents/QMUILabel (= 4.7.0)
    - QMUIKit/QMUIComponents/QMUILogManagerViewController (= 4.7.0)
    - QMUIKit/QMUIComponents/QMUILogWithConfigurationSupported (= 4.7.0)
    - QMUIKit/QMUIComponents/QMUIMarqueeLabel (= 4.7.0)
    - QMUIKit/QMUIComponents/QMUIModalPresentationViewController (= 4.7.0)
    - QMUIKit/QMUIComponents/QMUIMoreOperationController (= 4.7.0)
    - QMUIKit/QMUIComponents/QMUIMultipleDelegates (= 4.7.0)
    - QMUIKit/QMUIComponents/QMUINavigationButton (= 4.7.0)
    - QMUIKit/QMUIComponents/QMUINavigationTitleView (= 4.7.0)
    - QMUIKit/QMUIComponents/QMUIOrderedDictionary (= 4.7.0)
    - QMUIKit/QMUIComponents/QMUIPieProgressView (= 4.7.0)
    - QMUIKit/QMUIComponents/QMUIPopupContainerView (= 4.7.0)
    - QMUIKit/QMUIComponents/QMUIPopupMenuView (= 4.7.0)
    - QMUIKit/QMUIComponents/QMUIScrollAnimator (= 4.7.0)
    - QMUIKit/QMUIComponents/QMUISearchBar (= 4.7.0)
    - QMUIKit/QMUIComponents/QMUISearchController (= 4.7.0)
    - QMUIKit/QMUIComponents/QMUISegmentedControl (= 4.7.0)
    - QMUIKit/QMUIComponents/QMUIStaticTableView (= 4.7.0)
    - QMUIKit/QMUIComponents/QMUITableView (= 4.7.0)
    - QMUIKit/QMUIComponents/QMUITableViewCell (= 4.7.0)
    - QMUIKit/QMUIComponents/QMUITableViewHeaderFooterView (= 4.7.0)
    - QMUIKit/QMUIComponents/QMUITableViewProtocols (= 4.7.0)
    - QMUIKit/QMUIComponents/QMUITestView (= 4.7.0)
    - QMUIKit/QMUIComponents/QMUITextField (= 4.7.0)
    - QMUIKit/QMUIComponents/QMUITextView (= 4.7.0)
    - QMUIKit/QMUIComponents/QMUITheme (= 4.7.0)
    - QMUIKit/QMUIComponents/QMUITips (= 4.7.0)
    - QMUIKit/QMUIComponents/QMUIToastView (= 4.7.0)
    - QMUIKit/QMUIComponents/QMUIToolbarButton (= 4.7.0)
    - QMUIKit/QMUIComponents/QMUIWindowSizeMonitor (= 4.7.0)
    - QMUIKit/QMUIComponents/QMUIZoomImageView (= 4.7.0)
    - QMUIKit/QMUICore
  - QMUIKit/QMUIComponents/NavigationBarTransition (4.7.0):
    - QMUIKit/QMUIComponents/QMUINavigationTitleView
    - QMUIKit/QMUICore
    - QMUIKit/QMUIMainFrame
  - QMUIKit/QMUIComponents/QMUIAlertController (4.7.0):
    - QMUIKit/QMUIComponents/QMUIAppearance
    - QMUIKit/QMUIComponents/QMUIButton
    - QMUIKit/QMUIComponents/QMUIKeyboardManager
    - QMUIKit/QMUIComponents/QMUILabel
    - QMUIKit/QMUIComponents/QMUIModalPresentationViewController
    - QMUIKit/QMUIComponents/QMUITextField
    - QMUIKit/QMUICore
  - QMUIKit/QMUIComponents/QMUIAnimation (4.7.0):
    - QMUIKit/QMUICore
  - QMUIKit/QMUIComponents/QMUIAppearance (4.7.0):
    - QMUIKit/QMUICore
  - QMUIKit/QMUIComponents/QMUIAssetLibrary (4.7.0):
    - QMUIKit/QMUICore
  - QMUIKit/QMUIComponents/QMUIBadge (4.7.0):
    - QMUIKit/QMUIComponents/QMUILabel
    - QMUIKit/QMUICore
  - QMUIKit/QMUIComponents/QMUIButton (4.7.0):
    - QMUIKit/QMUICore
  - QMUIKit/QMUIComponents/QMUICAAnimationExtension (4.7.0):
    - QMUIKit/QMUIComponents/QMUIMultipleDelegates
    - QMUIKit/QMUICore
  - QMUIKit/QMUIComponents/QMUICALayerExtension (4.7.0):
    - QMUIKit/QMUIComponents/QMUIMultipleDelegates
    - QMUIKit/QMUICore
  - QMUIKit/QMUIComponents/QMUICellHeightCache (4.7.0):
    - QMUIKit/QMUIComponents/QMUITableViewProtocols
    - QMUIKit/QMUICore
  - QMUIKit/QMUIComponents/QMUICellHeightKeyCache (4.7.0):
    - QMUIKit/QMUIComponents/QMUIMultipleDelegates
    - QMUIKit/QMUIComponents/QMUITableViewProtocols
    - QMUIKit/QMUICore
  - QMUIKit/QMUIComponents/QMUICellSizeKeyCache (4.7.0):
    - QMUIKit/QMUIComponents/QMUIMultipleDelegates
    - QMUIKit/QMUICore
  - QMUIKit/QMUIComponents/QMUICollectionViewPagingLayout (4.7.0):
    - QMUIKit/QMUICore
  - QMUIKit/QMUIComponents/QMUIConsole (4.7.0):
    - QMUIKit/QMUIComponents/QMUIButton
    - QMUIKit/QMUIComponents/QMUICAAnimationExtension
    - QMUIKit/QMUIComponents/QMUICellHeightKeyCache
    - QMUIKit/QMUIComponents/QMUIPopupMenuView
    - QMUIKit/QMUIComponents/QMUITableView
    - QMUIKit/QMUIComponents/QMUITableViewCell
    - QMUIKit/QMUIComponents/QMUITextField
    - QMUIKit/QMUIComponents/QMUITextView
    - QMUIKit/QMUICore
    - QMUIKit/QMUIResources
  - QMUIKit/QMUIComponents/QMUIDialogViewController (4.7.0):
    - QMUIKit/QMUIComponents/QMUIAppearance
    - QMUIKit/QMUIComponents/QMUIButton
    - QMUIKit/QMUIComponents/QMUILabel
    - QMUIKit/QMUIComponents/QMUIModalPresentationViewController
    - QMUIKit/QMUIComponents/QMUINavigationTitleView
    - QMUIKit/QMUIComponents/QMUITableView
    - QMUIKit/QMUIComponents/QMUITableViewCell
    - QMUIKit/QMUIComponents/QMUITextField
    - QMUIKit/QMUICore
    - QMUIKit/QMUIMainFrame
  - QMUIKit/QMUIComponents/QMUIEmotionInputManager (4.7.0):
    - QMUIKit/QMUIComponents/QMUIEmotionView
    - QMUIKit/QMUICore
  - QMUIKit/QMUIComponents/QMUIEmotionView (4.7.0):
    - QMUIKit/QMUIComponents/QMUIButton
    - QMUIKit/QMUICore
    - QMUIKit/QMUIResources
  - QMUIKit/QMUIComponents/QMUIEmptyView (4.7.0):
    - QMUIKit/QMUIComponents/QMUIAppearance
    - QMUIKit/QMUIComponents/QMUIButton
    - QMUIKit/QMUICore
  - QMUIKit/QMUIComponents/QMUIFloatLayoutView (4.7.0):
    - QMUIKit/QMUICore
  - QMUIKit/QMUIComponents/QMUIGridView (4.7.0):
    - QMUIKit/QMUICore
  - QMUIKit/QMUIComponents/QMUIImagePickerLibrary (4.7.0):
    - QMUIKit/QMUIComponents/QMUIAlertController
    - QMUIKit/QMUIComponents/QMUIAppearance
    - QMUIKit/QMUIComponents/QMUIAssetLibrary
    - QMUIKit/QMUIComponents/QMUIButton
    - QMUIKit/QMUIComponents/QMUIEmptyView
    - QMUIKit/QMUIComponents/QMUIImagePreviewView
    - QMUIKit/QMUIComponents/QMUINavigationButton
    - QMUIKit/QMUIComponents/QMUITableViewCell
    - QMUIKit/QMUIComponents/QMUIZoomImageView
    - QMUIKit/QMUICore
    - QMUIKit/QMUIMainFrame
    - QMUIKit/QMUIResources
  - QMUIKit/QMUIComponents/QMUIImagePreviewView (4.7.0):
    - QMUIKit/QMUIComponents/QMUIAppearance
    - QMUIKit/QMUIComponents/QMUIButton
    - QMUIKit/QMUIComponents/QMUICollectionViewPagingLayout
    - QMUIKit/QMUIComponents/QMUIEmptyView
    - QMUIKit/QMUIComponents/QMUIPieProgressView
    - QMUIKit/QMUIComponents/QMUIZoomImageView
    - QMUIKit/QMUICore
    - QMUIKit/QMUIMainFrame
  - QMUIKit/QMUIComponents/QMUIKeyboardManager (4.7.0):
    - QMUIKit/QMUIComponents/QMUIAppearance
    - QMUIKit/QMUIComponents/QMUIMultipleDelegates
    - QMUIKit/QMUICore
  - QMUIKit/QMUIComponents/QMUILabel (4.7.0):
    - QMUIKit/QMUICore
  - QMUIKit/QMUIComponents/QMUILogManagerViewController (4.7.0):
    - QMUIKit/QMUIComponents/QMUIPopupMenuView
    - QMUIKit/QMUIComponents/QMUISearchController
    - QMUIKit/QMUIComponents/QMUIStaticTableView
    - QMUIKit/QMUIComponents/QMUITableView
    - QMUIKit/QMUICore
    - QMUIKit/QMUIMainFrame
  - QMUIKit/QMUIComponents/QMUILogWithConfigurationSupported (4.7.0):
    - QMUIKit/QMUICore
  - QMUIKit/QMUIComponents/QMUIMarqueeLabel (4.7.0):
    - QMUIKit/QMUICore
  - QMUIKit/QMUIComponents/QMUIModalPresentationViewController (4.7.0):
    - QMUIKit/QMUIComponents/QMUIAppearance
    - QMUIKit/QMUIComponents/QMUIKeyboardManager
    - QMUIKit/QMUICore
  - QMUIKit/QMUIComponents/QMUIMoreOperationController (4.7.0):
    - QMUIKit/QMUIComponents/QMUIAppearance
    - QMUIKit/QMUIComponents/QMUIButton
    - QMUIKit/QMUIComponents/QMUIModalPresentationViewController
    - QMUIKit/QMUICore
  - QMUIKit/QMUIComponents/QMUIMultipleDelegates (4.7.0):
    - QMUIKit/QMUICore
  - QMUIKit/QMUIComponents/QMUINavigationButton (4.7.0):
    - QMUIKit/QMUICore
    - QMUIKit/QMUIMainFrame
  - QMUIKit/QMUIComponents/QMUINavigationTitleView (4.7.0):
    - QMUIKit/QMUIComponents/QMUIAppearance
    - QMUIKit/QMUIComponents/QMUIButton
    - QMUIKit/QMUICore
  - QMUIKit/QMUIComponents/QMUIOrderedDictionary (4.7.0):
    - QMUIKit/QMUICore
  - QMUIKit/QMUIComponents/QMUIPieProgressView (4.7.0):
    - QMUIKit/QMUICore
  - QMUIKit/QMUIComponents/QMUIPopupContainerView (4.7.0):
    - QMUIKit/QMUIComponents/QMUIAppearance
    - QMUIKit/QMUICore
    - QMUIKit/QMUIMainFrame
  - QMUIKit/QMUIComponents/QMUIPopupMenuView (4.7.0):
    - QMUIKit/QMUIComponents/QMUIButton
    - QMUIKit/QMUIComponents/QMUIPopupContainerView
    - QMUIKit/QMUICore
  - QMUIKit/QMUIComponents/QMUIScrollAnimator (4.7.0):
    - QMUIKit/QMUIComponents/QMUIMultipleDelegates
    - QMUIKit/QMUICore
  - QMUIKit/QMUIComponents/QMUISearchBar (4.7.0):
    - QMUIKit/QMUICore
  - QMUIKit/QMUIComponents/QMUISearchController (4.7.0):
    - QMUIKit/QMUIComponents/QMUIEmptyView
    - QMUIKit/QMUIComponents/QMUISearchBar
    - QMUIKit/QMUICore
    - QMUIKit/QMUIMainFrame
  - QMUIKit/QMUIComponents/QMUISegmentedControl (4.7.0):
    - QMUIKit/QMUICore
  - QMUIKit/QMUIComponents/QMUIStaticTableView (4.7.0):
    - QMUIKit/QMUIComponents/QMUIMultipleDelegates
    - QMUIKit/QMUIComponents/QMUITableViewCell
    - QMUIKit/QMUICore
  - QMUIKit/QMUIComponents/QMUITableView (4.7.0):
    - QMUIKit/QMUIComponents/QMUITableViewProtocols
    - QMUIKit/QMUICore
  - QMUIKit/QMUIComponents/QMUITableViewCell (4.7.0):
    - QMUIKit/QMUIComponents/QMUIButton
    - QMUIKit/QMUICore
  - QMUIKit/QMUIComponents/QMUITableViewHeaderFooterView (4.7.0):
    - QMUIKit/QMUICore
  - QMUIKit/QMUIComponents/QMUITableViewProtocols (4.7.0):
    - QMUIKit/QMUICore
  - QMUIKit/QMUIComponents/QMUITestView (4.7.0):
    - QMUIKit/QMUICore
  - QMUIKit/QMUIComponents/QMUITextField (4.7.0):
    - QMUIKit/QMUIComponents/QMUIMultipleDelegates
    - QMUIKit/QMUICore
  - QMUIKit/QMUIComponents/QMUITextView (4.7.0):
    - QMUIKit/QMUIComponents/QMUILabel
    - QMUIKit/QMUIComponents/QMUIMultipleDelegates
    - QMUIKit/QMUICore
  - QMUIKit/QMUIComponents/QMUITheme (4.7.0):
    - QMUIKit/QMUIComponents/QMUIAlertController
    - QMUIKit/QMUIComponents/QMUIBadge
    - QMUIKit/QMUIComponents/QMUIButton
    - QMUIKit/QMUIComponents/QMUIConsole
    - QMUIKit/QMUIComponents/QMUIEmotionView
    - QMUIKit/QMUIComponents/QMUIEmptyView
    - QMUIKit/QMUIComponents/QMUIGridView
    - QMUIKit/QMUIComponents/QMUIImagePickerLibrary
    - QMUIKit/QMUIComponents/QMUIImagePreviewView
    - QMUIKit/QMUIComponents/QMUILabel
    - QMUIKit/QMUIComponents/QMUIModalPresentationViewController
    - QMUIKit/QMUIComponents/QMUIPopupContainerView
    - QMUIKit/QMUIComponents/QMUIPopupMenuView
    - QMUIKit/QMUIComponents/QMUITextField
    - QMUIKit/QMUIComponents/QMUITextView
    - QMUIKit/QMUIComponents/QMUIToastView
    - QMUIKit/QMUICore
  - QMUIKit/QMUIComponents/QMUITips (4.7.0):
    - QMUIKit/QMUIComponents/QMUIToastView
    - QMUIKit/QMUICore
    - QMUIKit/QMUIResources
  - QMUIKit/QMUIComponents/QMUIToastView (4.7.0):
    - QMUIKit/QMUIComponents/QMUIKeyboardManager
    - QMUIKit/QMUICore
  - QMUIKit/QMUIComponents/QMUIToolbarButton (4.7.0):
    - QMUIKit/QMUICore
  - QMUIKit/QMUIComponents/QMUIWindowSizeMonitor (4.7.0):
    - QMUIKit/QMUICore
  - QMUIKit/QMUIComponents/QMUIZoomImageView (4.7.0):
    - QMUIKit/QMUIComponents/QMUIAssetLibrary
    - QMUIKit/QMUIComponents/QMUIButton
    - QMUIKit/QMUIComponents/QMUIEmptyView
    - QMUIKit/QMUIComponents/QMUIPieProgressView
    - QMUIKit/QMUICore
    - QMUIKit/QMUIResources
  - QMUIKit/QMUICore (4.7.0):
    - QMUIKit/QMUILog
    - QMUIKit/QMUIWeakObjectContainer
  - QMUIKit/QMUILog (4.7.0)
  - QMUIKit/QMUIMainFrame (4.7.0):
    - QMUIKit/QMUIComponents/QMUIEmptyView
    - QMUIKit/QMUIComponents/QMUIKeyboardManager
    - QMUIKit/QMUIComponents/QMUIMultipleDelegates
    - QMUIKit/QMUIComponents/QMUINavigationTitleView
    - QMUIKit/QMUIComponents/QMUITableView
    - QMUIKit/QMUIComponents/QMUITableViewHeaderFooterView
    - QMUIKit/QMUICore
    - QMUIKit/QMUILog
  - QMUIKit/QMUIResources (4.7.0)
  - QMUIKit/QMUIWeakObjectContainer (4.7.0)
  - RxCocoa (6.6.0):
    - RxRelay (= 6.6.0)
    - RxSwift (= 6.6.0)
  - RxDataSources (5.0.0):
    - Differentiator (~> 5.0)
    - RxCocoa (~> 6.0)
    - RxSwift (~> 6.0)
  - RxRelay (6.6.0):
    - RxSwift (= 6.6.0)
  - RxSwift (6.6.0)
  - SDWebImage (5.18.5):
    - SDWebImage/Core (= 5.18.5)
  - SDWebImage/Core (5.18.5)
  - SnapKit (5.6.0)
  - SPPageMenu (3.5.0)
  - SVProgressHUD (2.3.1):
    - SVProgressHUD/Core (= 2.3.1)
  - SVProgressHUD/Core (2.3.1)
  - SwifterSwift (6.0.0):
    - SwifterSwift/AppKit (= 6.0.0)
    - SwifterSwift/Combine (= 6.0.0)
    - SwifterSwift/CoreAnimation (= 6.0.0)
    - SwifterSwift/CoreGraphics (= 6.0.0)
    - SwifterSwift/CoreLocation (= 6.0.0)
    - SwifterSwift/CryptoKit (= 6.0.0)
    - SwifterSwift/Dispatch (= 6.0.0)
    - SwifterSwift/Foundation (= 6.0.0)
    - SwifterSwift/HealthKit (= 6.0.0)
    - SwifterSwift/MapKit (= 6.0.0)
    - SwifterSwift/SceneKit (= 6.0.0)
    - SwifterSwift/SpriteKit (= 6.0.0)
    - SwifterSwift/StoreKit (= 6.0.0)
    - SwifterSwift/SwiftStdlib (= 6.0.0)
    - SwifterSwift/UIKit (= 6.0.0)
    - SwifterSwift/WebKit (= 6.0.0)
  - SwifterSwift/AppKit (6.0.0)
  - SwifterSwift/Combine (6.0.0)
  - SwifterSwift/CoreAnimation (6.0.0)
  - SwifterSwift/CoreGraphics (6.0.0)
  - SwifterSwift/CoreLocation (6.0.0)
  - SwifterSwift/CryptoKit (6.0.0)
  - SwifterSwift/Dispatch (6.0.0)
  - SwifterSwift/Foundation (6.0.0)
  - SwifterSwift/HealthKit (6.0.0)
  - SwifterSwift/MapKit (6.0.0)
  - SwifterSwift/SceneKit (6.0.0)
  - SwifterSwift/SpriteKit (6.0.0)
  - SwifterSwift/StoreKit (6.0.0)
  - SwifterSwift/SwiftStdlib (6.0.0)
  - SwifterSwift/UIKit (6.0.0)
  - SwifterSwift/WebKit (6.0.0)
  - SwiftyStoreKit (0.16.1)
  - TZImagePickerController (3.8.4):
    - TZImagePickerController/Basic (= 3.8.4)
    - TZImagePickerController/Location (= 3.8.4)
  - TZImagePickerController/Basic (3.8.4)
  - TZImagePickerController/Location (3.8.4)
  - UserDefaultsStore (1.5.0)
  - VTMagic (1.2.4):
    - VTMagic/Core (= 1.2.4)
  - VTMagic/Core (1.2.4)
  - WechatOpenSDK-XCFramework (2.0.2)
  - XCGLogger (7.0.1):
    - XCGLogger/Core (= 7.0.1)
  - XCGLogger/Core (7.0.1):
    - ObjcExceptionBridging
DEPENDENCIES:
  - Alamofire
  - CryptoSwift
  - FFPage
  - JQTools (from `/Users/yvkd/MyProject/JQTools`)
  - Lantern
  - SPPageMenu
  - SVProgressHUD
  - SwifterSwift
  - SwiftyStoreKit
  - WechatOpenSDK-XCFramework
SPEC REPOS:
  trunk:
    - Alamofire
    - CryptoSwift
    - Differentiator
    - EmptyDataSet-Swift
    - FFPage
    - HandyJSON
    - IQKeyboardManager
    - IQKeyboardManagerSwift
    - Lantern
    - MJRefresh
    - ObjcExceptionBridging
    - ObjectMapper
    - QMUIKit
    - RxCocoa
    - RxDataSources
    - RxRelay
    - RxSwift
    - SDWebImage
    - SnapKit
    - SPPageMenu
    - SVProgressHUD
    - SwifterSwift
    - SwiftyStoreKit
    - TZImagePickerController
    - UserDefaultsStore
    - VTMagic
    - WechatOpenSDK-XCFramework
    - XCGLogger
EXTERNAL SOURCES:
  JQTools:
    :path: "/Users/yvkd/MyProject/JQTools"
SPEC CHECKSUMS:
  Alamofire: 3ca42e259043ee0dc5c0cdd76c4bc568b8e42af7
  CryptoSwift: 52aaf3fce7337552863b1d952e408085f0e65030
  Differentiator: e8497ceab83c1b10ca233716d547b9af21b9344d
  EmptyDataSet-Swift: eb382c0c87a2d9c678077385a595cec52da38171
  FFPage: 481cc0f2dde0f6be84a2359b6c86272e0024dc8d
  HandyJSON: 9e4e236f5d2dbefad5155a77417bbea438201c03
  IQKeyboardManager: 024b54d7dcb765c5bc99882cb4d5ea24a8cb7c3c
  IQKeyboardManagerSwift: 12d89768845bb77b55cc092ecc2b1f9370f06b76
  JQTools: d2b720c901e39d9959c9342ba42f9eba58886a02
  Lantern: b192e7146c6d04e15e627f37281254a6a8593703
  MJRefresh: 2fe7fb43a5167ceda20bb7e63f130c04fd1814a5
  ObjcExceptionBridging: c30e00eb3700467e695faeea30e26e18bd445001
  ObjectMapper: 1eb41f610210777375fa806bf161dc39fb832b81
  QMUIKit: 2fc09ba9a31a44a4081916ed4c41467bac798821
  RxCocoa: 44a80de90e25b739b5aeaae3c8c371a32e3343cc
  RxDataSources: aa47cc1ed6c500fa0dfecac5c979b723542d79cf
  RxRelay: 45eaa5db8ee4fb50e5ebd57deec0159e97fa51e6
  RxSwift: a4b44f7d24599f674deebd1818eab82e58410632
  SDWebImage: 7ac2b7ddc5e8484c79aa90fc4e30b149d6a2c88f
  SnapKit: e01d52ebb8ddbc333eefe2132acf85c8227d9c25
  SPPageMenu: da182aafcec55719d5c326103cc7716c1e48f311
  SVProgressHUD: 4837c74bdfe2e51e8821c397825996a8d7de6e22
  SwifterSwift: 66843dc594f8fb890f5543c22545921d44acc004
  SwiftyStoreKit: 6b9c08810269f030586dac1fae8e75871a82e84a
  TZImagePickerController: f1c9f1cae6ac0e30b31aaa9698f9bf4a7cf5b84f
  UserDefaultsStore: 905e30372ff432197d199ce1f6fe51be7bf69628
  VTMagic: b49e5f456dbcbfd9a3588ba92417233a105bc193
  WechatOpenSDK-XCFramework: acdeeda129efbef9532bca8a10c24e1b4b8c7d69
  XCGLogger: 1943831ef907df55108b0b18657953f868de973b
PODFILE CHECKSUM: d08291ea186e10b071f475d96892da9674a4fc68
COCOAPODS: 1.15.2
Pods/Alamofire/LICENSE
New file
@@ -0,0 +1,19 @@
Copyright (c) 2014-2022 Alamofire Software Foundation (http://alamofire.org/)
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:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
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.
Pods/Alamofire/README.md
New file
@@ -0,0 +1,256 @@
![Alamofire: Elegant Networking in Swift](https://raw.githubusercontent.com/Alamofire/Alamofire/master/Resources/AlamofireLogo.png)
[![Swift](https://img.shields.io/badge/Swift-5.6_5.7_5.8_5.9-orange?style=flat-square)](https://img.shields.io/badge/Swift-5.6_5.7_5.8_5.9-Orange?style=flat-square)
[![Platforms](https://img.shields.io/badge/Platforms-macOS_iOS_tvOS_watchOS_visionOS_Linux_Windows_Android-yellowgreen?style=flat-square)](https://img.shields.io/badge/Platforms-macOS_iOS_tvOS_watchOS_vision_OS_Linux_Windows_Android-Green?style=flat-square)
[![CocoaPods Compatible](https://img.shields.io/cocoapods/v/Alamofire.svg?style=flat-square)](https://img.shields.io/cocoapods/v/Alamofire.svg)
[![Carthage Compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat-square)](https://github.com/Carthage/Carthage)
[![Swift Package Manager](https://img.shields.io/badge/Swift_Package_Manager-compatible-orange?style=flat-square)](https://img.shields.io/badge/Swift_Package_Manager-compatible-orange?style=flat-square)
[![Swift Forums](https://img.shields.io/badge/Swift_Forums-Alamofire-orange?style=flat-square)](https://forums.swift.org/c/related-projects/alamofire/37)
Alamofire is an HTTP networking library written in Swift.
- [Features](#features)
- [Component Libraries](#component-libraries)
- [Requirements](#requirements)
- [Migration Guides](#migration-guides)
- [Communication](#communication)
- [Installation](#installation)
- [Contributing](#contributing)
- [Usage](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#using-alamofire)
  - [**Introduction -**](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#introduction) [Making Requests](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#making-requests), [Response Handling](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#response-handling), [Response Validation](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#response-validation), [Response Caching](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#response-caching)
  - **HTTP -** [HTTP Methods](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#http-methods), [Parameters and Parameter Encoder](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md##request-parameters-and-parameter-encoders), [HTTP Headers](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#http-headers), [Authentication](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#authentication)
  - **Large Data -** [Downloading Data to a File](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#downloading-data-to-a-file), [Uploading Data to a Server](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#uploading-data-to-a-server)
  - **Tools -** [Statistical Metrics](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#statistical-metrics), [cURL Command Output](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#curl-command-output)
- [Advanced Usage](https://github.com/Alamofire/Alamofire/blob/master/Documentation/AdvancedUsage.md)
  - **URL Session -** [Session Manager](https://github.com/Alamofire/Alamofire/blob/master/Documentation/AdvancedUsage.md#session), [Session Delegate](https://github.com/Alamofire/Alamofire/blob/master/Documentation/AdvancedUsage.md#sessiondelegate), [Request](https://github.com/Alamofire/Alamofire/blob/master/Documentation/AdvancedUsage.md#request)
  - **Routing -** [Routing Requests](https://github.com/Alamofire/Alamofire/blob/master/Documentation/AdvancedUsage.md#routing-requests), [Adapting and Retrying Requests](https://github.com/Alamofire/Alamofire/blob/master/Documentation/AdvancedUsage.md#adapting-and-retrying-requests-with-requestinterceptor)
  - **Model Objects -** [Custom Response Handlers](https://github.com/Alamofire/Alamofire/blob/master/Documentation/AdvancedUsage.md#customizing-response-handlers)
  - **Advanced Concurrency -** [Swift Concurrency](https://github.com/Alamofire/Alamofire/blob/master/Documentation/AdvancedUsage.md#using-alamofire-with-swift-concurrency) and [Combine](https://github.com/Alamofire/Alamofire/blob/master/Documentation/AdvancedUsage.md#using-alamofire-with-combine)
  - **Connection -** [Security](https://github.com/Alamofire/Alamofire/blob/master/Documentation/AdvancedUsage.md#security), [Network Reachability](https://github.com/Alamofire/Alamofire/blob/master/Documentation/AdvancedUsage.md#network-reachability)
- [Open Radars](#open-radars)
- [FAQ](#faq)
- [Credits](#credits)
- [Donations](#donations)
- [License](#license)
## Features
- [x] Chainable Request / Response Methods
- [x] Swift Concurrency Support Back to iOS 13, macOS 10.15, tvOS 13, and watchOS 6.
- [x] Combine Support
- [x] URL / JSON Parameter Encoding
- [x] Upload File / Data / Stream / MultipartFormData
- [x] Download File using Request or Resume Data
- [x] Authentication with `URLCredential`
- [x] HTTP Response Validation
- [x] Upload and Download Progress Closures with Progress
- [x] cURL Command Output
- [x] Dynamically Adapt and Retry Requests
- [x] TLS Certificate and Public Key Pinning
- [x] Network Reachability
- [x] Comprehensive Unit and Integration Test Coverage
- [x] [Complete Documentation](https://alamofire.github.io/Alamofire)
## Write Requests Fast!
Alamofire's compact syntax and extensive feature set allow requests with powerful features like automatic retry to be written in just a few lines of code.
```swift
// Automatic String to URL conversion, Swift concurrency support, and automatic retry.
let response = await AF.request("https://httpbin.org/get", interceptor: .retryPolicy)
                       // Automatic HTTP Basic Auth.
                       .authenticate(username: "user", password: "pass")
                       // Caching customization.
                       .cacheResponse(using: .cache)
                       // Redirect customization.
                       .redirect(using: .follow)
                       // Validate response code and Content-Type.
                       .validate()
                       // Produce a cURL command for the request.
                       .cURLDescription { description in
                         print(description)
                       }
                       // Automatic Decodable support with background parsing.
                       .serializingDecodable(DecodableType.self)
                       // Await the full response with metrics and a parsed body.
                       .response
// Detailed response description for easy debugging.
debugPrint(response)
```
## Component Libraries
In order to keep Alamofire focused specifically on core networking implementations, additional component libraries have been created by the [Alamofire Software Foundation](https://github.com/Alamofire/Foundation) to bring additional functionality to the Alamofire ecosystem.
- [AlamofireImage](https://github.com/Alamofire/AlamofireImage) - An image library including image response serializers, `UIImage` and `UIImageView` extensions, custom image filters, an auto-purging in-memory cache, and a priority-based image downloading system.
- [AlamofireNetworkActivityIndicator](https://github.com/Alamofire/AlamofireNetworkActivityIndicator) - Controls the visibility of the network activity indicator on iOS using Alamofire. It contains configurable delay timers to help mitigate flicker and can support `URLSession` instances not managed by Alamofire.
## Requirements
| Platform                                             | Minimum Swift Version | Installation                                                                                                         | Status                   |
| ---------------------------------------------------- | --------------------- | -------------------------------------------------------------------------------------------------------------------- | ------------------------ |
| iOS 10.0+ / macOS 10.12+ / tvOS 10.0+ / watchOS 3.0+ | 5.6                   | [CocoaPods](#cocoapods), [Carthage](#carthage), [Swift Package Manager](#swift-package-manager), [Manual](#manually) | Fully Tested             |
| Linux                                                | Latest Only           | [Swift Package Manager](#swift-package-manager)                                                                      | Building But Unsupported |
| Windows                                              | Latest Only           | [Swift Package Manager](#swift-package-manager)                                                                      | Building But Unsupported |
| Android                                              | Latest Only           | [Swift Package Manager](#swift-package-manager)                                                                      | Building But Unsupported |
#### Known Issues on Linux and Windows
Alamofire builds on Linux, Windows, and Android but there are missing features and many issues in the underlying `swift-corelibs-foundation` that prevent full functionality and may cause crashes. These include:
- `ServerTrustManager` and associated certificate functionality is unavailable, so there is no certificate pinning and no client certificate support.
- Various methods of HTTP authentication may crash, including HTTP Basic and HTTP Digest. Crashes may occur if responses contain server challenges.
- Cache control through `CachedResponseHandler` and associated APIs is unavailable, as the underlying delegate methods aren't called.
- `URLSessionTaskMetrics` are never gathered.
Due to these issues, Alamofire is unsupported on Linux, Windows, and Android. Please report any crashes to the [Swift bug reporter](https://bugs.swift.org).
## Migration Guides
- [Alamofire 5.0 Migration Guide](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Alamofire%205.0%20Migration%20Guide.md)
- [Alamofire 4.0 Migration Guide](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Alamofire%204.0%20Migration%20Guide.md)
- [Alamofire 3.0 Migration Guide](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Alamofire%203.0%20Migration%20Guide.md)
- [Alamofire 2.0 Migration Guide](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Alamofire%202.0%20Migration%20Guide.md)
## Communication
- If you **need help with making network requests** using Alamofire, use [Stack Overflow](https://stackoverflow.com/questions/tagged/alamofire) and tag `alamofire`.
- If you need to **find or understand an API**, check [our documentation](http://alamofire.github.io/Alamofire/) or [Apple's documentation for `URLSession`](https://developer.apple.com/documentation/foundation/url_loading_system), on top of which Alamofire is built.
- If you need **help with an Alamofire feature**, use [our forum on swift.org](https://forums.swift.org/c/related-projects/alamofire).
- If you'd like to **discuss Alamofire best practices**, use [our forum on swift.org](https://forums.swift.org/c/related-projects/alamofire).
- If you'd like to **discuss a feature request**, use [our forum on swift.org](https://forums.swift.org/c/related-projects/alamofire).
- If you **found a bug**, open an issue here on GitHub and follow the guide. The more detail the better!
## Installation
### CocoaPods
[CocoaPods](https://cocoapods.org) is a dependency manager for Cocoa projects. For usage and installation instructions, visit their website. To integrate Alamofire into your Xcode project using CocoaPods, specify it in your `Podfile`:
```ruby
pod 'Alamofire'
```
### Carthage
[Carthage](https://github.com/Carthage/Carthage) is a decentralized dependency manager that builds your dependencies and provides you with binary frameworks. To integrate Alamofire into your Xcode project using Carthage, specify it in your `Cartfile`:
```ogdl
github "Alamofire/Alamofire"
```
### Swift Package Manager
The [Swift Package Manager](https://swift.org/package-manager/) is a tool for automating the distribution of Swift code and is integrated into the `swift` compiler.
Once you have your Swift package set up, adding Alamofire as a dependency is as easy as adding it to the `dependencies` value of your `Package.swift`.
```swift
dependencies: [
    .package(url: "https://github.com/Alamofire/Alamofire.git", .upToNextMajor(from: "5.8.1"))
]
```
### Manually
If you prefer not to use any of the aforementioned dependency managers, you can integrate Alamofire into your project manually.
#### Embedded Framework
- Open up Terminal, `cd` into your top-level project directory, and run the following command "if" your project is not initialized as a git repository:
  ```bash
  $ git init
  ```
- Add Alamofire as a git [submodule](https://git-scm.com/docs/git-submodule) by running the following command:
  ```bash
  $ git submodule add https://github.com/Alamofire/Alamofire.git
  ```
- Open the new `Alamofire` folder, and drag the `Alamofire.xcodeproj` into the Project Navigator of your application's Xcode project.
  > It should appear nested underneath your application's blue project icon. Whether it is above or below all the other Xcode groups does not matter.
- Select the `Alamofire.xcodeproj` in the Project Navigator and verify the deployment target matches that of your application target.
- Next, select your application project in the Project Navigator (blue project icon) to navigate to the target configuration window and select the application target under the "Targets" heading in the sidebar.
- In the tab bar at the top of that window, open the "General" panel.
- Click on the `+` button under the "Embedded Binaries" section.
- You will see two different `Alamofire.xcodeproj` folders each with two different versions of the `Alamofire.framework` nested inside a `Products` folder.
  > It does not matter which `Products` folder you choose from, but it does matter whether you choose the top or bottom `Alamofire.framework`.
- Select the top `Alamofire.framework` for iOS and the bottom one for macOS.
  > You can verify which one you selected by inspecting the build log for your project. The build target for `Alamofire` will be listed as `Alamofire iOS`, `Alamofire macOS`, `Alamofire tvOS`, or `Alamofire watchOS`.
- And that's it!
  > The `Alamofire.framework` is automagically added as a target dependency, linked framework and embedded framework in a copy files build phase which is all you need to build on the simulator and a device.
## Contributing
Before contributing to Alamofire, please read the instructions detailed in our [contribution guide](https://github.com/Alamofire/Alamofire/blob/master/CONTRIBUTING.md).
## Open Radars
The following radars have some effect on the current implementation of Alamofire.
- [`rdar://21349340`](http://www.openradar.me/radar?id=5517037090635776) - Compiler throwing warning due to toll-free bridging issue in the test case
- `rdar://26870455` - Background URL Session Configurations do not work in the simulator
- `rdar://26849668` - Some URLProtocol APIs do not properly handle `URLRequest`
## Resolved Radars
The following radars have been resolved over time after being filed against the Alamofire project.
- [`rdar://26761490`](http://www.openradar.me/radar?id=5010235949318144) - Swift string interpolation causing memory leak with common usage.
  - (Resolved): 9/1/17 in Xcode 9 beta 6.
- [`rdar://36082113`](http://openradar.appspot.com/radar?id=4942308441063424) - `URLSessionTaskMetrics` failing to link on watchOS 3.0+
  - (Resolved): Just add `CFNetwork` to your linked frameworks.
- `FB7624529` - `urlSession(_:task:didFinishCollecting:)` never called on watchOS
  - (Resolved): Metrics now collected on watchOS 7+.
## FAQ
### What's the origin of the name Alamofire?
Alamofire is named after the [Alamo Fire flower](https://aggie-horticulture.tamu.edu/wildseed/alamofire.html), a hybrid variant of the Bluebonnet, the official state flower of Texas.
## Credits
Alamofire is owned and maintained by the [Alamofire Software Foundation](http://alamofire.org). You can follow them on Twitter at [@AlamofireSF](https://twitter.com/AlamofireSF) for project updates and releases.
### Security Disclosure
If you believe you have identified a security vulnerability with Alamofire, you should report it as soon as possible via email to security@alamofire.org. Please do not post it to a public issue tracker.
## Sponsorship
The [ASF](https://github.com/Alamofire/Foundation#members) is looking to raise money to officially stay registered as a federal non-profit organization.
Registering will allow Foundation members to gain some legal protections and also allow us to put donations to use, tax-free.
Sponsoring the ASF will enable us to:
- Pay our yearly legal fees to keep the non-profit in good status
- Pay for our mail servers to help us stay on top of all questions and security issues
- Potentially fund test servers to make it easier for us to test the edge cases
- Potentially fund developers to work on one of our projects full-time
The community adoption of the ASF libraries has been amazing.
We are greatly humbled by your enthusiasm around the projects and want to continue to do everything we can to move the needle forward.
With your continued support, the ASF will be able to improve its reach and also provide better legal safety for the core members.
If you use any of our libraries for work, see if your employers would be interested in donating.
Any amount you can donate, whether once or monthly, to help us reach our goal would be greatly appreciated.
[Sponsor Alamofire](https://github.com/sponsors/Alamofire)
## Supporters
[MacStadium](https://macstadium.com) provides Alamofire with a free, hosted Mac mini.
![Powered by MacStadium](https://raw.githubusercontent.com/Alamofire/Alamofire/master/Resources/MacStadiumLogo.png)
## License
Alamofire is released under the MIT license. [See LICENSE](https://github.com/Alamofire/Alamofire/blob/master/LICENSE) for details.
Pods/Alamofire/Source/AFError.swift
New file
@@ -0,0 +1,874 @@
//
//  AFError.swift
//
//  Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/)
//
//  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:
//
//  The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
//
//  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.
//
import Foundation
#if canImport(Security)
import Security
#endif
/// `AFError` is the error type returned by Alamofire. It encompasses a few different types of errors, each with
/// their own associated reasons.
public enum AFError: Error {
    /// The underlying reason the `.multipartEncodingFailed` error occurred.
    public enum MultipartEncodingFailureReason {
        /// The `fileURL` provided for reading an encodable body part isn't a file `URL`.
        case bodyPartURLInvalid(url: URL)
        /// The filename of the `fileURL` provided has either an empty `lastPathComponent` or `pathExtension.
        case bodyPartFilenameInvalid(in: URL)
        /// The file at the `fileURL` provided was not reachable.
        case bodyPartFileNotReachable(at: URL)
        /// Attempting to check the reachability of the `fileURL` provided threw an error.
        case bodyPartFileNotReachableWithError(atURL: URL, error: Error)
        /// The file at the `fileURL` provided is actually a directory.
        case bodyPartFileIsDirectory(at: URL)
        /// The size of the file at the `fileURL` provided was not returned by the system.
        case bodyPartFileSizeNotAvailable(at: URL)
        /// The attempt to find the size of the file at the `fileURL` provided threw an error.
        case bodyPartFileSizeQueryFailedWithError(forURL: URL, error: Error)
        /// An `InputStream` could not be created for the provided `fileURL`.
        case bodyPartInputStreamCreationFailed(for: URL)
        /// An `OutputStream` could not be created when attempting to write the encoded data to disk.
        case outputStreamCreationFailed(for: URL)
        /// The encoded body data could not be written to disk because a file already exists at the provided `fileURL`.
        case outputStreamFileAlreadyExists(at: URL)
        /// The `fileURL` provided for writing the encoded body data to disk is not a file `URL`.
        case outputStreamURLInvalid(url: URL)
        /// The attempt to write the encoded body data to disk failed with an underlying error.
        case outputStreamWriteFailed(error: Error)
        /// The attempt to read an encoded body part `InputStream` failed with underlying system error.
        case inputStreamReadFailed(error: Error)
    }
    /// Represents unexpected input stream length that occur when encoding the `MultipartFormData`. Instances will be
    /// embedded within an `AFError.multipartEncodingFailed` `.inputStreamReadFailed` case.
    public struct UnexpectedInputStreamLength: Error {
        /// The expected byte count to read.
        public var bytesExpected: UInt64
        /// The actual byte count read.
        public var bytesRead: UInt64
    }
    /// The underlying reason the `.parameterEncodingFailed` error occurred.
    public enum ParameterEncodingFailureReason {
        /// The `URLRequest` did not have a `URL` to encode.
        case missingURL
        /// JSON serialization failed with an underlying system error during the encoding process.
        case jsonEncodingFailed(error: Error)
        /// Custom parameter encoding failed due to the associated `Error`.
        case customEncodingFailed(error: Error)
    }
    /// The underlying reason the `.parameterEncoderFailed` error occurred.
    public enum ParameterEncoderFailureReason {
        /// Possible missing components.
        public enum RequiredComponent {
            /// The `URL` was missing or unable to be extracted from the passed `URLRequest` or during encoding.
            case url
            /// The `HTTPMethod` could not be extracted from the passed `URLRequest`.
            case httpMethod(rawValue: String)
        }
        /// A `RequiredComponent` was missing during encoding.
        case missingRequiredComponent(RequiredComponent)
        /// The underlying encoder failed with the associated error.
        case encoderFailed(error: Error)
    }
    /// The underlying reason the `.responseValidationFailed` error occurred.
    public enum ResponseValidationFailureReason {
        /// The data file containing the server response did not exist.
        case dataFileNil
        /// The data file containing the server response at the associated `URL` could not be read.
        case dataFileReadFailed(at: URL)
        /// The response did not contain a `Content-Type` and the `acceptableContentTypes` provided did not contain a
        /// wildcard type.
        case missingContentType(acceptableContentTypes: [String])
        /// The response `Content-Type` did not match any type in the provided `acceptableContentTypes`.
        case unacceptableContentType(acceptableContentTypes: [String], responseContentType: String)
        /// The response status code was not acceptable.
        case unacceptableStatusCode(code: Int)
        /// Custom response validation failed due to the associated `Error`.
        case customValidationFailed(error: Error)
    }
    /// The underlying reason the response serialization error occurred.
    public enum ResponseSerializationFailureReason {
        /// The server response contained no data or the data was zero length.
        case inputDataNilOrZeroLength
        /// The file containing the server response did not exist.
        case inputFileNil
        /// The file containing the server response could not be read from the associated `URL`.
        case inputFileReadFailed(at: URL)
        /// String serialization failed using the provided `String.Encoding`.
        case stringSerializationFailed(encoding: String.Encoding)
        /// JSON serialization failed with an underlying system error.
        case jsonSerializationFailed(error: Error)
        /// A `DataDecoder` failed to decode the response due to the associated `Error`.
        case decodingFailed(error: Error)
        /// A custom response serializer failed due to the associated `Error`.
        case customSerializationFailed(error: Error)
        /// Generic serialization failed for an empty response that wasn't type `Empty` but instead the associated type.
        case invalidEmptyResponse(type: String)
    }
    #if canImport(Security)
    /// Underlying reason a server trust evaluation error occurred.
    public enum ServerTrustFailureReason {
        /// The output of a server trust evaluation.
        public struct Output {
            /// The host for which the evaluation was performed.
            public let host: String
            /// The `SecTrust` value which was evaluated.
            public let trust: SecTrust
            /// The `OSStatus` of evaluation operation.
            public let status: OSStatus
            /// The result of the evaluation operation.
            public let result: SecTrustResultType
            /// Creates an `Output` value from the provided values.
            init(_ host: String, _ trust: SecTrust, _ status: OSStatus, _ result: SecTrustResultType) {
                self.host = host
                self.trust = trust
                self.status = status
                self.result = result
            }
        }
        /// No `ServerTrustEvaluator` was found for the associated host.
        case noRequiredEvaluator(host: String)
        /// No certificates were found with which to perform the trust evaluation.
        case noCertificatesFound
        /// No public keys were found with which to perform the trust evaluation.
        case noPublicKeysFound
        /// During evaluation, application of the associated `SecPolicy` failed.
        case policyApplicationFailed(trust: SecTrust, policy: SecPolicy, status: OSStatus)
        /// During evaluation, setting the associated anchor certificates failed.
        case settingAnchorCertificatesFailed(status: OSStatus, certificates: [SecCertificate])
        /// During evaluation, creation of the revocation policy failed.
        case revocationPolicyCreationFailed
        /// `SecTrust` evaluation failed with the associated `Error`, if one was produced.
        case trustEvaluationFailed(error: Error?)
        /// Default evaluation failed with the associated `Output`.
        case defaultEvaluationFailed(output: Output)
        /// Host validation failed with the associated `Output`.
        case hostValidationFailed(output: Output)
        /// Revocation check failed with the associated `Output` and options.
        case revocationCheckFailed(output: Output, options: RevocationTrustEvaluator.Options)
        /// Certificate pinning failed.
        case certificatePinningFailed(host: String, trust: SecTrust, pinnedCertificates: [SecCertificate], serverCertificates: [SecCertificate])
        /// Public key pinning failed.
        case publicKeyPinningFailed(host: String, trust: SecTrust, pinnedKeys: [SecKey], serverKeys: [SecKey])
        /// Custom server trust evaluation failed due to the associated `Error`.
        case customEvaluationFailed(error: Error)
    }
    #endif
    /// The underlying reason the `.urlRequestValidationFailed`
    public enum URLRequestValidationFailureReason {
        /// URLRequest with GET method had body data.
        case bodyDataInGETRequest(Data)
    }
    ///  `UploadableConvertible` threw an error in `createUploadable()`.
    case createUploadableFailed(error: Error)
    ///  `URLRequestConvertible` threw an error in `asURLRequest()`.
    case createURLRequestFailed(error: Error)
    /// `SessionDelegate` threw an error while attempting to move downloaded file to destination URL.
    case downloadedFileMoveFailed(error: Error, source: URL, destination: URL)
    /// `Request` was explicitly cancelled.
    case explicitlyCancelled
    /// `URLConvertible` type failed to create a valid `URL`.
    case invalidURL(url: URLConvertible)
    /// Multipart form encoding failed.
    case multipartEncodingFailed(reason: MultipartEncodingFailureReason)
    /// `ParameterEncoding` threw an error during the encoding process.
    case parameterEncodingFailed(reason: ParameterEncodingFailureReason)
    /// `ParameterEncoder` threw an error while running the encoder.
    case parameterEncoderFailed(reason: ParameterEncoderFailureReason)
    /// `RequestAdapter` threw an error during adaptation.
    case requestAdaptationFailed(error: Error)
    /// `RequestRetrier` threw an error during the request retry process.
    case requestRetryFailed(retryError: Error, originalError: Error)
    /// Response validation failed.
    case responseValidationFailed(reason: ResponseValidationFailureReason)
    /// Response serialization failed.
    case responseSerializationFailed(reason: ResponseSerializationFailureReason)
    #if canImport(Security)
    /// `ServerTrustEvaluating` instance threw an error during trust evaluation.
    case serverTrustEvaluationFailed(reason: ServerTrustFailureReason)
    #endif
    /// `Session` which issued the `Request` was deinitialized, most likely because its reference went out of scope.
    case sessionDeinitialized
    /// `Session` was explicitly invalidated, possibly with the `Error` produced by the underlying `URLSession`.
    case sessionInvalidated(error: Error?)
    /// `URLSessionTask` completed with error.
    case sessionTaskFailed(error: Error)
    /// `URLRequest` failed validation.
    case urlRequestValidationFailed(reason: URLRequestValidationFailureReason)
}
extension Error {
    /// Returns the instance cast as an `AFError`.
    public var asAFError: AFError? {
        self as? AFError
    }
    /// Returns the instance cast as an `AFError`. If casting fails, a `fatalError` with the specified `message` is thrown.
    public func asAFError(orFailWith message: @autoclosure () -> String, file: StaticString = #file, line: UInt = #line) -> AFError {
        guard let afError = self as? AFError else {
            fatalError(message(), file: file, line: line)
        }
        return afError
    }
    /// Casts the instance as `AFError` or returns `defaultAFError`
    func asAFError(or defaultAFError: @autoclosure () -> AFError) -> AFError {
        self as? AFError ?? defaultAFError()
    }
}
// MARK: - Error Booleans
extension AFError {
    /// Returns whether the instance is `.sessionDeinitialized`.
    public var isSessionDeinitializedError: Bool {
        if case .sessionDeinitialized = self { return true }
        return false
    }
    /// Returns whether the instance is `.sessionInvalidated`.
    public var isSessionInvalidatedError: Bool {
        if case .sessionInvalidated = self { return true }
        return false
    }
    /// Returns whether the instance is `.explicitlyCancelled`.
    public var isExplicitlyCancelledError: Bool {
        if case .explicitlyCancelled = self { return true }
        return false
    }
    /// Returns whether the instance is `.invalidURL`.
    public var isInvalidURLError: Bool {
        if case .invalidURL = self { return true }
        return false
    }
    /// Returns whether the instance is `.parameterEncodingFailed`. When `true`, the `underlyingError` property will
    /// contain the associated value.
    public var isParameterEncodingError: Bool {
        if case .parameterEncodingFailed = self { return true }
        return false
    }
    /// Returns whether the instance is `.parameterEncoderFailed`. When `true`, the `underlyingError` property will
    /// contain the associated value.
    public var isParameterEncoderError: Bool {
        if case .parameterEncoderFailed = self { return true }
        return false
    }
    /// Returns whether the instance is `.multipartEncodingFailed`. When `true`, the `url` and `underlyingError`
    /// properties will contain the associated values.
    public var isMultipartEncodingError: Bool {
        if case .multipartEncodingFailed = self { return true }
        return false
    }
    /// Returns whether the instance is `.requestAdaptationFailed`. When `true`, the `underlyingError` property will
    /// contain the associated value.
    public var isRequestAdaptationError: Bool {
        if case .requestAdaptationFailed = self { return true }
        return false
    }
    /// Returns whether the instance is `.responseValidationFailed`. When `true`, the `acceptableContentTypes`,
    /// `responseContentType`,  `responseCode`, and `underlyingError` properties will contain the associated values.
    public var isResponseValidationError: Bool {
        if case .responseValidationFailed = self { return true }
        return false
    }
    /// Returns whether the instance is `.responseSerializationFailed`. When `true`, the `failedStringEncoding` and
    /// `underlyingError` properties will contain the associated values.
    public var isResponseSerializationError: Bool {
        if case .responseSerializationFailed = self { return true }
        return false
    }
    #if canImport(Security)
    /// Returns whether the instance is `.serverTrustEvaluationFailed`. When `true`, the `underlyingError` property will
    /// contain the associated value.
    public var isServerTrustEvaluationError: Bool {
        if case .serverTrustEvaluationFailed = self { return true }
        return false
    }
    #endif
    /// Returns whether the instance is `requestRetryFailed`. When `true`, the `underlyingError` property will
    /// contain the associated value.
    public var isRequestRetryError: Bool {
        if case .requestRetryFailed = self { return true }
        return false
    }
    /// Returns whether the instance is `createUploadableFailed`. When `true`, the `underlyingError` property will
    /// contain the associated value.
    public var isCreateUploadableError: Bool {
        if case .createUploadableFailed = self { return true }
        return false
    }
    /// Returns whether the instance is `createURLRequestFailed`. When `true`, the `underlyingError` property will
    /// contain the associated value.
    public var isCreateURLRequestError: Bool {
        if case .createURLRequestFailed = self { return true }
        return false
    }
    /// Returns whether the instance is `downloadedFileMoveFailed`. When `true`, the `destination` and `underlyingError` properties will
    /// contain the associated values.
    public var isDownloadedFileMoveError: Bool {
        if case .downloadedFileMoveFailed = self { return true }
        return false
    }
    /// Returns whether the instance is `createURLRequestFailed`. When `true`, the `underlyingError` property will
    /// contain the associated value.
    public var isSessionTaskError: Bool {
        if case .sessionTaskFailed = self { return true }
        return false
    }
}
// MARK: - Convenience Properties
extension AFError {
    /// The `URLConvertible` associated with the error.
    public var urlConvertible: URLConvertible? {
        guard case let .invalidURL(url) = self else { return nil }
        return url
    }
    /// The `URL` associated with the error.
    public var url: URL? {
        guard case let .multipartEncodingFailed(reason) = self else { return nil }
        return reason.url
    }
    /// The underlying `Error` responsible for generating the failure associated with `.sessionInvalidated`,
    /// `.parameterEncodingFailed`, `.parameterEncoderFailed`, `.multipartEncodingFailed`, `.requestAdaptationFailed`,
    /// `.responseSerializationFailed`, `.requestRetryFailed` errors.
    public var underlyingError: Error? {
        switch self {
        case let .multipartEncodingFailed(reason):
            return reason.underlyingError
        case let .parameterEncodingFailed(reason):
            return reason.underlyingError
        case let .parameterEncoderFailed(reason):
            return reason.underlyingError
        case let .requestAdaptationFailed(error):
            return error
        case let .requestRetryFailed(retryError, _):
            return retryError
        case let .responseValidationFailed(reason):
            return reason.underlyingError
        case let .responseSerializationFailed(reason):
            return reason.underlyingError
        #if canImport(Security)
        case let .serverTrustEvaluationFailed(reason):
            return reason.underlyingError
        #endif
        case let .sessionInvalidated(error):
            return error
        case let .createUploadableFailed(error):
            return error
        case let .createURLRequestFailed(error):
            return error
        case let .downloadedFileMoveFailed(error, _, _):
            return error
        case let .sessionTaskFailed(error):
            return error
        case .explicitlyCancelled,
             .invalidURL,
             .sessionDeinitialized,
             .urlRequestValidationFailed:
            return nil
        }
    }
    /// The acceptable `Content-Type`s of a `.responseValidationFailed` error.
    public var acceptableContentTypes: [String]? {
        guard case let .responseValidationFailed(reason) = self else { return nil }
        return reason.acceptableContentTypes
    }
    /// The response `Content-Type` of a `.responseValidationFailed` error.
    public var responseContentType: String? {
        guard case let .responseValidationFailed(reason) = self else { return nil }
        return reason.responseContentType
    }
    /// The response code of a `.responseValidationFailed` error.
    public var responseCode: Int? {
        guard case let .responseValidationFailed(reason) = self else { return nil }
        return reason.responseCode
    }
    /// The `String.Encoding` associated with a failed `.stringResponse()` call.
    public var failedStringEncoding: String.Encoding? {
        guard case let .responseSerializationFailed(reason) = self else { return nil }
        return reason.failedStringEncoding
    }
    /// The `source` URL of a `.downloadedFileMoveFailed` error.
    public var sourceURL: URL? {
        guard case let .downloadedFileMoveFailed(_, source, _) = self else { return nil }
        return source
    }
    /// The `destination` URL of a `.downloadedFileMoveFailed` error.
    public var destinationURL: URL? {
        guard case let .downloadedFileMoveFailed(_, _, destination) = self else { return nil }
        return destination
    }
    #if canImport(Security)
    /// The download resume data of any underlying network error. Only produced by `DownloadRequest`s.
    public var downloadResumeData: Data? {
        (underlyingError as? URLError)?.userInfo[NSURLSessionDownloadTaskResumeData] as? Data
    }
    #endif
}
extension AFError.ParameterEncodingFailureReason {
    var underlyingError: Error? {
        switch self {
        case let .jsonEncodingFailed(error),
             let .customEncodingFailed(error):
            return error
        case .missingURL:
            return nil
        }
    }
}
extension AFError.ParameterEncoderFailureReason {
    var underlyingError: Error? {
        switch self {
        case let .encoderFailed(error):
            return error
        case .missingRequiredComponent:
            return nil
        }
    }
}
extension AFError.MultipartEncodingFailureReason {
    var url: URL? {
        switch self {
        case let .bodyPartURLInvalid(url),
             let .bodyPartFilenameInvalid(url),
             let .bodyPartFileNotReachable(url),
             let .bodyPartFileIsDirectory(url),
             let .bodyPartFileSizeNotAvailable(url),
             let .bodyPartInputStreamCreationFailed(url),
             let .outputStreamCreationFailed(url),
             let .outputStreamFileAlreadyExists(url),
             let .outputStreamURLInvalid(url),
             let .bodyPartFileNotReachableWithError(url, _),
             let .bodyPartFileSizeQueryFailedWithError(url, _):
            return url
        case .outputStreamWriteFailed,
             .inputStreamReadFailed:
            return nil
        }
    }
    var underlyingError: Error? {
        switch self {
        case let .bodyPartFileNotReachableWithError(_, error),
             let .bodyPartFileSizeQueryFailedWithError(_, error),
             let .outputStreamWriteFailed(error),
             let .inputStreamReadFailed(error):
            return error
        case .bodyPartURLInvalid,
             .bodyPartFilenameInvalid,
             .bodyPartFileNotReachable,
             .bodyPartFileIsDirectory,
             .bodyPartFileSizeNotAvailable,
             .bodyPartInputStreamCreationFailed,
             .outputStreamCreationFailed,
             .outputStreamFileAlreadyExists,
             .outputStreamURLInvalid:
            return nil
        }
    }
}
extension AFError.ResponseValidationFailureReason {
    var acceptableContentTypes: [String]? {
        switch self {
        case let .missingContentType(types),
             let .unacceptableContentType(types, _):
            return types
        case .dataFileNil,
             .dataFileReadFailed,
             .unacceptableStatusCode,
             .customValidationFailed:
            return nil
        }
    }
    var responseContentType: String? {
        switch self {
        case let .unacceptableContentType(_, responseType):
            return responseType
        case .dataFileNil,
             .dataFileReadFailed,
             .missingContentType,
             .unacceptableStatusCode,
             .customValidationFailed:
            return nil
        }
    }
    var responseCode: Int? {
        switch self {
        case let .unacceptableStatusCode(code):
            return code
        case .dataFileNil,
             .dataFileReadFailed,
             .missingContentType,
             .unacceptableContentType,
             .customValidationFailed:
            return nil
        }
    }
    var underlyingError: Error? {
        switch self {
        case let .customValidationFailed(error):
            return error
        case .dataFileNil,
             .dataFileReadFailed,
             .missingContentType,
             .unacceptableContentType,
             .unacceptableStatusCode:
            return nil
        }
    }
}
extension AFError.ResponseSerializationFailureReason {
    var failedStringEncoding: String.Encoding? {
        switch self {
        case let .stringSerializationFailed(encoding):
            return encoding
        case .inputDataNilOrZeroLength,
             .inputFileNil,
             .inputFileReadFailed(_),
             .jsonSerializationFailed(_),
             .decodingFailed(_),
             .customSerializationFailed(_),
             .invalidEmptyResponse:
            return nil
        }
    }
    var underlyingError: Error? {
        switch self {
        case let .jsonSerializationFailed(error),
             let .decodingFailed(error),
             let .customSerializationFailed(error):
            return error
        case .inputDataNilOrZeroLength,
             .inputFileNil,
             .inputFileReadFailed,
             .stringSerializationFailed,
             .invalidEmptyResponse:
            return nil
        }
    }
}
#if canImport(Security)
extension AFError.ServerTrustFailureReason {
    var output: AFError.ServerTrustFailureReason.Output? {
        switch self {
        case let .defaultEvaluationFailed(output),
             let .hostValidationFailed(output),
             let .revocationCheckFailed(output, _):
            return output
        case .noRequiredEvaluator,
             .noCertificatesFound,
             .noPublicKeysFound,
             .policyApplicationFailed,
             .settingAnchorCertificatesFailed,
             .revocationPolicyCreationFailed,
             .trustEvaluationFailed,
             .certificatePinningFailed,
             .publicKeyPinningFailed,
             .customEvaluationFailed:
            return nil
        }
    }
    var underlyingError: Error? {
        switch self {
        case let .customEvaluationFailed(error):
            return error
        case let .trustEvaluationFailed(error):
            return error
        case .noRequiredEvaluator,
             .noCertificatesFound,
             .noPublicKeysFound,
             .policyApplicationFailed,
             .settingAnchorCertificatesFailed,
             .revocationPolicyCreationFailed,
             .defaultEvaluationFailed,
             .hostValidationFailed,
             .revocationCheckFailed,
             .certificatePinningFailed,
             .publicKeyPinningFailed:
            return nil
        }
    }
}
#endif
// MARK: - Error Descriptions
extension AFError: LocalizedError {
    public var errorDescription: String? {
        switch self {
        case .explicitlyCancelled:
            return "Request explicitly cancelled."
        case let .invalidURL(url):
            return "URL is not valid: \(url)"
        case let .parameterEncodingFailed(reason):
            return reason.localizedDescription
        case let .parameterEncoderFailed(reason):
            return reason.localizedDescription
        case let .multipartEncodingFailed(reason):
            return reason.localizedDescription
        case let .requestAdaptationFailed(error):
            return "Request adaption failed with error: \(error.localizedDescription)"
        case let .responseValidationFailed(reason):
            return reason.localizedDescription
        case let .responseSerializationFailed(reason):
            return reason.localizedDescription
        case let .requestRetryFailed(retryError, originalError):
            return """
            Request retry failed with retry error: \(retryError.localizedDescription), \
            original error: \(originalError.localizedDescription)
            """
        case .sessionDeinitialized:
            return """
            Session was invalidated without error, so it was likely deinitialized unexpectedly. \
            Be sure to retain a reference to your Session for the duration of your requests.
            """
        case let .sessionInvalidated(error):
            return "Session was invalidated with error: \(error?.localizedDescription ?? "No description.")"
        #if canImport(Security)
        case let .serverTrustEvaluationFailed(reason):
            return "Server trust evaluation failed due to reason: \(reason.localizedDescription)"
        #endif
        case let .urlRequestValidationFailed(reason):
            return "URLRequest validation failed due to reason: \(reason.localizedDescription)"
        case let .createUploadableFailed(error):
            return "Uploadable creation failed with error: \(error.localizedDescription)"
        case let .createURLRequestFailed(error):
            return "URLRequest creation failed with error: \(error.localizedDescription)"
        case let .downloadedFileMoveFailed(error, source, destination):
            return "Moving downloaded file from: \(source) to: \(destination) failed with error: \(error.localizedDescription)"
        case let .sessionTaskFailed(error):
            return "URLSessionTask failed with error: \(error.localizedDescription)"
        }
    }
}
extension AFError.ParameterEncodingFailureReason {
    var localizedDescription: String {
        switch self {
        case .missingURL:
            return "URL request to encode was missing a URL"
        case let .jsonEncodingFailed(error):
            return "JSON could not be encoded because of error:\n\(error.localizedDescription)"
        case let .customEncodingFailed(error):
            return "Custom parameter encoder failed with error: \(error.localizedDescription)"
        }
    }
}
extension AFError.ParameterEncoderFailureReason {
    var localizedDescription: String {
        switch self {
        case let .missingRequiredComponent(component):
            return "Encoding failed due to a missing request component: \(component)"
        case let .encoderFailed(error):
            return "The underlying encoder failed with the error: \(error)"
        }
    }
}
extension AFError.MultipartEncodingFailureReason {
    var localizedDescription: String {
        switch self {
        case let .bodyPartURLInvalid(url):
            return "The URL provided is not a file URL: \(url)"
        case let .bodyPartFilenameInvalid(url):
            return "The URL provided does not have a valid filename: \(url)"
        case let .bodyPartFileNotReachable(url):
            return "The URL provided is not reachable: \(url)"
        case let .bodyPartFileNotReachableWithError(url, error):
            return """
            The system returned an error while checking the provided URL for reachability.
            URL: \(url)
            Error: \(error)
            """
        case let .bodyPartFileIsDirectory(url):
            return "The URL provided is a directory: \(url)"
        case let .bodyPartFileSizeNotAvailable(url):
            return "Could not fetch the file size from the provided URL: \(url)"
        case let .bodyPartFileSizeQueryFailedWithError(url, error):
            return """
            The system returned an error while attempting to fetch the file size from the provided URL.
            URL: \(url)
            Error: \(error)
            """
        case let .bodyPartInputStreamCreationFailed(url):
            return "Failed to create an InputStream for the provided URL: \(url)"
        case let .outputStreamCreationFailed(url):
            return "Failed to create an OutputStream for URL: \(url)"
        case let .outputStreamFileAlreadyExists(url):
            return "A file already exists at the provided URL: \(url)"
        case let .outputStreamURLInvalid(url):
            return "The provided OutputStream URL is invalid: \(url)"
        case let .outputStreamWriteFailed(error):
            return "OutputStream write failed with error: \(error)"
        case let .inputStreamReadFailed(error):
            return "InputStream read failed with error: \(error)"
        }
    }
}
extension AFError.ResponseSerializationFailureReason {
    var localizedDescription: String {
        switch self {
        case .inputDataNilOrZeroLength:
            return "Response could not be serialized, input data was nil or zero length."
        case .inputFileNil:
            return "Response could not be serialized, input file was nil."
        case let .inputFileReadFailed(url):
            return "Response could not be serialized, input file could not be read: \(url)."
        case let .stringSerializationFailed(encoding):
            return "String could not be serialized with encoding: \(encoding)."
        case let .jsonSerializationFailed(error):
            return "JSON could not be serialized because of error:\n\(error.localizedDescription)"
        case let .invalidEmptyResponse(type):
            return """
            Empty response could not be serialized to type: \(type). \
            Use Empty as the expected type for such responses.
            """
        case let .decodingFailed(error):
            return "Response could not be decoded because of error:\n\(error.localizedDescription)"
        case let .customSerializationFailed(error):
            return "Custom response serializer failed with error:\n\(error.localizedDescription)"
        }
    }
}
extension AFError.ResponseValidationFailureReason {
    var localizedDescription: String {
        switch self {
        case .dataFileNil:
            return "Response could not be validated, data file was nil."
        case let .dataFileReadFailed(url):
            return "Response could not be validated, data file could not be read: \(url)."
        case let .missingContentType(types):
            return """
            Response Content-Type was missing and acceptable content types \
            (\(types.joined(separator: ","))) do not match "*/*".
            """
        case let .unacceptableContentType(acceptableTypes, responseType):
            return """
            Response Content-Type "\(responseType)" does not match any acceptable types: \
            \(acceptableTypes.joined(separator: ",")).
            """
        case let .unacceptableStatusCode(code):
            return "Response status code was unacceptable: \(code)."
        case let .customValidationFailed(error):
            return "Custom response validation failed with error: \(error.localizedDescription)"
        }
    }
}
#if canImport(Security)
extension AFError.ServerTrustFailureReason {
    var localizedDescription: String {
        switch self {
        case let .noRequiredEvaluator(host):
            return "A ServerTrustEvaluating value is required for host \(host) but none was found."
        case .noCertificatesFound:
            return "No certificates were found or provided for evaluation."
        case .noPublicKeysFound:
            return "No public keys were found or provided for evaluation."
        case .policyApplicationFailed:
            return "Attempting to set a SecPolicy failed."
        case .settingAnchorCertificatesFailed:
            return "Attempting to set the provided certificates as anchor certificates failed."
        case .revocationPolicyCreationFailed:
            return "Attempting to create a revocation policy failed."
        case let .trustEvaluationFailed(error):
            return "SecTrust evaluation failed with error: \(error?.localizedDescription ?? "None")"
        case let .defaultEvaluationFailed(output):
            return "Default evaluation failed for host \(output.host)."
        case let .hostValidationFailed(output):
            return "Host validation failed for host \(output.host)."
        case let .revocationCheckFailed(output, _):
            return "Revocation check failed for host \(output.host)."
        case let .certificatePinningFailed(host, _, _, _):
            return "Certificate pinning failed for host \(host)."
        case let .publicKeyPinningFailed(host, _, _, _):
            return "Public key pinning failed for host \(host)."
        case let .customEvaluationFailed(error):
            return "Custom trust evaluation failed with error: \(error.localizedDescription)"
        }
    }
}
#endif
extension AFError.URLRequestValidationFailureReason {
    var localizedDescription: String {
        switch self {
        case let .bodyDataInGETRequest(data):
            return """
            Invalid URLRequest: Requests with GET method cannot have body data:
            \(String(decoding: data, as: UTF8.self))
            """
        }
    }
}
Pods/Alamofire/Source/Alamofire.swift
New file
@@ -0,0 +1,40 @@
//
//  Alamofire.swift
//
//  Copyright (c) 2014-2021 Alamofire Software Foundation (http://alamofire.org/)
//
//  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:
//
//  The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
//
//  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.
//
import Dispatch
import Foundation
#if canImport(FoundationNetworking)
@_exported import FoundationNetworking
#endif
// Enforce minimum Swift version for all platforms and build systems.
#if swift(<5.5)
#error("Alamofire doesn't support Swift versions below 5.5.")
#endif
/// Reference to `Session.default` for quick bootstrapping and examples.
public let AF = Session.default
/// Current Alamofire version. Necessary since SPM doesn't use dynamic libraries. Plus this will be more accurate.
let version = "5.8.0"
Pods/Alamofire/Source/AlamofireExtended.swift
New file
@@ -0,0 +1,61 @@
//
//  AlamofireExtended.swift
//
//  Copyright (c) 2019 Alamofire Software Foundation (http://alamofire.org/)
//
//  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:
//
//  The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
//
//  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.
//
/// Type that acts as a generic extension point for all `AlamofireExtended` types.
public struct AlamofireExtension<ExtendedType> {
    /// Stores the type or meta-type of any extended type.
    public private(set) var type: ExtendedType
    /// Create an instance from the provided value.
    ///
    /// - Parameter type: Instance being extended.
    public init(_ type: ExtendedType) {
        self.type = type
    }
}
/// Protocol describing the `af` extension points for Alamofire extended types.
public protocol AlamofireExtended {
    /// Type being extended.
    associatedtype ExtendedType
    /// Static Alamofire extension point.
    static var af: AlamofireExtension<ExtendedType>.Type { get set }
    /// Instance Alamofire extension point.
    var af: AlamofireExtension<ExtendedType> { get set }
}
extension AlamofireExtended {
    /// Static Alamofire extension point.
    public static var af: AlamofireExtension<Self>.Type {
        get { AlamofireExtension<Self>.self }
        set {}
    }
    /// Instance Alamofire extension point.
    public var af: AlamofireExtension<Self> {
        get { AlamofireExtension(self) }
        set {}
    }
}
Pods/Alamofire/Source/AuthenticationInterceptor.swift
New file
@@ -0,0 +1,402 @@
//
//  AuthenticationInterceptor.swift
//
//  Copyright (c) 2020 Alamofire Software Foundation (http://alamofire.org/)
//
//  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:
//
//  The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
//
//  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.
//
import Foundation
/// Types adopting the `AuthenticationCredential` protocol can be used to authenticate `URLRequest`s.
///
/// One common example of an `AuthenticationCredential` is an OAuth2 credential containing an access token used to
/// authenticate all requests on behalf of a user. The access token generally has an expiration window of 60 minutes
/// which will then require a refresh of the credential using the refresh token to generate a new access token.
public protocol AuthenticationCredential {
    /// Whether the credential requires a refresh. This property should always return `true` when the credential is
    /// expired. It is also wise to consider returning `true` when the credential will expire in several seconds or
    /// minutes depending on the expiration window of the credential.
    ///
    /// For example, if the credential is valid for 60 minutes, then it would be wise to return `true` when the
    /// credential is only valid for 5 minutes or less. That ensures the credential will not expire as it is passed
    /// around backend services.
    var requiresRefresh: Bool { get }
}
// MARK: -
/// Types adopting the `Authenticator` protocol can be used to authenticate `URLRequest`s with an
/// `AuthenticationCredential` as well as refresh the `AuthenticationCredential` when required.
public protocol Authenticator: AnyObject {
    /// The type of credential associated with the `Authenticator` instance.
    associatedtype Credential: AuthenticationCredential
    /// Applies the `Credential` to the `URLRequest`.
    ///
    /// In the case of OAuth2, the access token of the `Credential` would be added to the `URLRequest` as a Bearer
    /// token to the `Authorization` header.
    ///
    /// - Parameters:
    ///   - credential: The `Credential`.
    ///   - urlRequest: The `URLRequest`.
    func apply(_ credential: Credential, to urlRequest: inout URLRequest)
    /// Refreshes the `Credential` and executes the `completion` closure with the `Result` once complete.
    ///
    /// Refresh can be called in one of two ways. It can be called before the `Request` is actually executed due to
    /// a `requiresRefresh` returning `true` during the adapt portion of the `Request` creation process. It can also
    /// be triggered by a failed `Request` where the authentication server denied access due to an expired or
    /// invalidated access token.
    ///
    /// In the case of OAuth2, this method would use the refresh token of the `Credential` to generate a new
    /// `Credential` using the authentication service. Once complete, the `completion` closure should be called with
    /// the new `Credential`, or the error that occurred.
    ///
    /// In general, if the refresh call fails with certain status codes from the authentication server (commonly a 401),
    /// the refresh token in the `Credential` can no longer be used to generate a valid `Credential`. In these cases,
    /// you will need to reauthenticate the user with their username / password.
    ///
    /// Please note, these are just general examples of common use cases. They are not meant to solve your specific
    /// authentication server challenges. Please work with your authentication server team to ensure your
    /// `Authenticator` logic matches their expectations.
    ///
    /// - Parameters:
    ///   - credential: The `Credential` to refresh.
    ///   - session:    The `Session` requiring the refresh.
    ///   - completion: The closure to be executed once the refresh is complete.
    func refresh(_ credential: Credential, for session: Session, completion: @escaping (Result<Credential, Error>) -> Void)
    /// Determines whether the `URLRequest` failed due to an authentication error based on the `HTTPURLResponse`.
    ///
    /// If the authentication server **CANNOT** invalidate credentials after they are issued, then simply return `false`
    /// for this method. If the authentication server **CAN** invalidate credentials due to security breaches, then you
    /// will need to work with your authentication server team to understand how to identify when this occurs.
    ///
    /// In the case of OAuth2, where an authentication server can invalidate credentials, you will need to inspect the
    /// `HTTPURLResponse` or possibly the `Error` for when this occurs. This is commonly handled by the authentication
    /// server returning a 401 status code and some additional header to indicate an OAuth2 failure occurred.
    ///
    /// It is very important to understand how your authentication server works to be able to implement this correctly.
    /// For example, if your authentication server returns a 401 when an OAuth2 error occurs, and your downstream
    /// service also returns a 401 when you are not authorized to perform that operation, how do you know which layer
    /// of the backend returned you a 401? You do not want to trigger a refresh unless you know your authentication
    /// server is actually the layer rejecting the request. Again, work with your authentication server team to understand
    /// how to identify an OAuth2 401 error vs. a downstream 401 error to avoid endless refresh loops.
    ///
    /// - Parameters:
    ///   - urlRequest: The `URLRequest`.
    ///   - response:   The `HTTPURLResponse`.
    ///   - error:      The `Error`.
    ///
    /// - Returns: `true` if the `URLRequest` failed due to an authentication error, `false` otherwise.
    func didRequest(_ urlRequest: URLRequest, with response: HTTPURLResponse, failDueToAuthenticationError error: Error) -> Bool
    /// Determines whether the `URLRequest` is authenticated with the `Credential`.
    ///
    /// If the authentication server **CANNOT** invalidate credentials after they are issued, then simply return `true`
    /// for this method. If the authentication server **CAN** invalidate credentials due to security breaches, then
    /// read on.
    ///
    /// When an authentication server can invalidate credentials, it means that you may have a non-expired credential
    /// that appears to be valid, but will be rejected by the authentication server when used. Generally when this
    /// happens, a number of requests are all sent when the application is foregrounded, and all of them will be
    /// rejected by the authentication server in the order they are received. The first failed request will trigger a
    /// refresh internally, which will update the credential, and then retry all the queued requests with the new
    /// credential. However, it is possible that some of the original requests will not return from the authentication
    /// server until the refresh has completed. This is where this method comes in.
    ///
    /// When the authentication server rejects a credential, we need to check to make sure we haven't refreshed the
    /// credential while the request was in flight. If it has already refreshed, then we don't need to trigger an
    /// additional refresh. If it hasn't refreshed, then we need to refresh.
    ///
    /// Now that it is understood how the result of this method is used in the refresh lifecyle, let's walk through how
    /// to implement it. You should return `true` in this method if the `URLRequest` is authenticated in a way that
    /// matches the values in the `Credential`. In the case of OAuth2, this would mean that the Bearer token in the
    /// `Authorization` header of the `URLRequest` matches the access token in the `Credential`. If it matches, then we
    /// know the `Credential` was used to authenticate the `URLRequest` and should return `true`. If the Bearer token
    /// did not match the access token, then you should return `false`.
    ///
    /// - Parameters:
    ///   - urlRequest: The `URLRequest`.
    ///   - credential: The `Credential`.
    ///
    /// - Returns: `true` if the `URLRequest` is authenticated with the `Credential`, `false` otherwise.
    func isRequest(_ urlRequest: URLRequest, authenticatedWith credential: Credential) -> Bool
}
// MARK: -
/// Represents various authentication failures that occur when using the `AuthenticationInterceptor`. All errors are
/// still vended from Alamofire as `AFError` types. The `AuthenticationError` instances will be embedded within
/// `AFError` `.requestAdaptationFailed` or `.requestRetryFailed` cases.
public enum AuthenticationError: Error {
    /// The credential was missing so the request could not be authenticated.
    case missingCredential
    /// The credential was refreshed too many times within the `RefreshWindow`.
    case excessiveRefresh
}
// MARK: -
/// The `AuthenticationInterceptor` class manages the queuing and threading complexity of authenticating requests.
/// It relies on an `Authenticator` type to handle the actual `URLRequest` authentication and `Credential` refresh.
public class AuthenticationInterceptor<AuthenticatorType>: RequestInterceptor where AuthenticatorType: Authenticator {
    // MARK: Typealiases
    /// Type of credential used to authenticate requests.
    public typealias Credential = AuthenticatorType.Credential
    // MARK: Helper Types
    /// Type that defines a time window used to identify excessive refresh calls. When enabled, prior to executing a
    /// refresh, the `AuthenticationInterceptor` compares the timestamp history of previous refresh calls against the
    /// `RefreshWindow`. If more refreshes have occurred within the refresh window than allowed, the refresh is
    /// cancelled and an `AuthorizationError.excessiveRefresh` error is thrown.
    public struct RefreshWindow {
        /// `TimeInterval` defining the duration of the time window before the current time in which the number of
        /// refresh attempts is compared against `maximumAttempts`. For example, if `interval` is 30 seconds, then the
        /// `RefreshWindow` represents the past 30 seconds. If more attempts occurred in the past 30 seconds than
        /// `maximumAttempts`, an `.excessiveRefresh` error will be thrown.
        public let interval: TimeInterval
        /// Total refresh attempts allowed within `interval` before throwing an `.excessiveRefresh` error.
        public let maximumAttempts: Int
        /// Creates a `RefreshWindow` instance from the specified `interval` and `maximumAttempts`.
        ///
        /// - Parameters:
        ///   - interval:        `TimeInterval` defining the duration of the time window before the current time.
        ///   - maximumAttempts: The maximum attempts allowed within the `TimeInterval`.
        public init(interval: TimeInterval = 30.0, maximumAttempts: Int = 5) {
            self.interval = interval
            self.maximumAttempts = maximumAttempts
        }
    }
    private struct AdaptOperation {
        let urlRequest: URLRequest
        let session: Session
        let completion: (Result<URLRequest, Error>) -> Void
    }
    private enum AdaptResult {
        case adapt(Credential)
        case doNotAdapt(AuthenticationError)
        case adaptDeferred
    }
    private struct MutableState {
        var credential: Credential?
        var isRefreshing = false
        var refreshTimestamps: [TimeInterval] = []
        var refreshWindow: RefreshWindow?
        var adaptOperations: [AdaptOperation] = []
        var requestsToRetry: [(RetryResult) -> Void] = []
    }
    // MARK: Properties
    /// The `Credential` used to authenticate requests.
    public var credential: Credential? {
        get { mutableState.credential }
        set { mutableState.credential = newValue }
    }
    let authenticator: AuthenticatorType
    let queue = DispatchQueue(label: "org.alamofire.authentication.inspector")
    private let mutableState: Protected<MutableState>
    // MARK: Initialization
    /// Creates an `AuthenticationInterceptor` instance from the specified parameters.
    ///
    /// A `nil` `RefreshWindow` will result in the `AuthenticationInterceptor` not checking for excessive refresh calls.
    /// It is recommended to always use a `RefreshWindow` to avoid endless refresh cycles.
    ///
    /// - Parameters:
    ///   - authenticator: The `Authenticator` type.
    ///   - credential:    The `Credential` if it exists. `nil` by default.
    ///   - refreshWindow: The `RefreshWindow` used to identify excessive refresh calls. `RefreshWindow()` by default.
    public init(authenticator: AuthenticatorType,
                credential: Credential? = nil,
                refreshWindow: RefreshWindow? = RefreshWindow()) {
        self.authenticator = authenticator
        mutableState = Protected(MutableState(credential: credential, refreshWindow: refreshWindow))
    }
    // MARK: Adapt
    public func adapt(_ urlRequest: URLRequest, for session: Session, completion: @escaping (Result<URLRequest, Error>) -> Void) {
        let adaptResult: AdaptResult = mutableState.write { mutableState in
            // Queue the adapt operation if a refresh is already in place.
            guard !mutableState.isRefreshing else {
                let operation = AdaptOperation(urlRequest: urlRequest, session: session, completion: completion)
                mutableState.adaptOperations.append(operation)
                return .adaptDeferred
            }
            // Throw missing credential error is the credential is missing.
            guard let credential = mutableState.credential else {
                let error = AuthenticationError.missingCredential
                return .doNotAdapt(error)
            }
            // Queue the adapt operation and trigger refresh operation if credential requires refresh.
            guard !credential.requiresRefresh else {
                let operation = AdaptOperation(urlRequest: urlRequest, session: session, completion: completion)
                mutableState.adaptOperations.append(operation)
                refresh(credential, for: session, insideLock: &mutableState)
                return .adaptDeferred
            }
            return .adapt(credential)
        }
        switch adaptResult {
        case let .adapt(credential):
            var authenticatedRequest = urlRequest
            authenticator.apply(credential, to: &authenticatedRequest)
            completion(.success(authenticatedRequest))
        case let .doNotAdapt(adaptError):
            completion(.failure(adaptError))
        case .adaptDeferred:
            // No-op: adapt operation captured during refresh.
            break
        }
    }
    // MARK: Retry
    public func retry(_ request: Request, for session: Session, dueTo error: Error, completion: @escaping (RetryResult) -> Void) {
        // Do not attempt retry if there was not an original request and response from the server.
        guard let urlRequest = request.request, let response = request.response else {
            completion(.doNotRetry)
            return
        }
        // Do not attempt retry unless the `Authenticator` verifies failure was due to authentication error (i.e. 401 status code).
        guard authenticator.didRequest(urlRequest, with: response, failDueToAuthenticationError: error) else {
            completion(.doNotRetry)
            return
        }
        // Do not attempt retry if there is no credential.
        guard let credential = credential else {
            let error = AuthenticationError.missingCredential
            completion(.doNotRetryWithError(error))
            return
        }
        // Retry the request if the `Authenticator` verifies it was authenticated with a previous credential.
        guard authenticator.isRequest(urlRequest, authenticatedWith: credential) else {
            completion(.retry)
            return
        }
        mutableState.write { mutableState in
            mutableState.requestsToRetry.append(completion)
            guard !mutableState.isRefreshing else { return }
            refresh(credential, for: session, insideLock: &mutableState)
        }
    }
    // MARK: Refresh
    private func refresh(_ credential: Credential, for session: Session, insideLock mutableState: inout MutableState) {
        guard !isRefreshExcessive(insideLock: &mutableState) else {
            let error = AuthenticationError.excessiveRefresh
            handleRefreshFailure(error, insideLock: &mutableState)
            return
        }
        mutableState.refreshTimestamps.append(ProcessInfo.processInfo.systemUptime)
        mutableState.isRefreshing = true
        // Dispatch to queue to hop out of the lock in case authenticator.refresh is implemented synchronously.
        queue.async {
            self.authenticator.refresh(credential, for: session) { result in
                self.mutableState.write { mutableState in
                    switch result {
                    case let .success(credential):
                        self.handleRefreshSuccess(credential, insideLock: &mutableState)
                    case let .failure(error):
                        self.handleRefreshFailure(error, insideLock: &mutableState)
                    }
                }
            }
        }
    }
    private func isRefreshExcessive(insideLock mutableState: inout MutableState) -> Bool {
        guard let refreshWindow = mutableState.refreshWindow else { return false }
        let refreshWindowMin = ProcessInfo.processInfo.systemUptime - refreshWindow.interval
        let refreshAttemptsWithinWindow = mutableState.refreshTimestamps.reduce(into: 0) { attempts, refreshTimestamp in
            guard refreshWindowMin <= refreshTimestamp else { return }
            attempts += 1
        }
        let isRefreshExcessive = refreshAttemptsWithinWindow >= refreshWindow.maximumAttempts
        return isRefreshExcessive
    }
    private func handleRefreshSuccess(_ credential: Credential, insideLock mutableState: inout MutableState) {
        mutableState.credential = credential
        let adaptOperations = mutableState.adaptOperations
        let requestsToRetry = mutableState.requestsToRetry
        mutableState.adaptOperations.removeAll()
        mutableState.requestsToRetry.removeAll()
        mutableState.isRefreshing = false
        // Dispatch to queue to hop out of the mutable state lock
        queue.async {
            adaptOperations.forEach { self.adapt($0.urlRequest, for: $0.session, completion: $0.completion) }
            requestsToRetry.forEach { $0(.retry) }
        }
    }
    private func handleRefreshFailure(_ error: Error, insideLock mutableState: inout MutableState) {
        let adaptOperations = mutableState.adaptOperations
        let requestsToRetry = mutableState.requestsToRetry
        mutableState.adaptOperations.removeAll()
        mutableState.requestsToRetry.removeAll()
        mutableState.isRefreshing = false
        // Dispatch to queue to hop out of the mutable state lock
        queue.async {
            adaptOperations.forEach { $0.completion(.failure(error)) }
            requestsToRetry.forEach { $0(.doNotRetryWithError(error)) }
        }
    }
}
Pods/Alamofire/Source/CachedResponseHandler.swift
New file
@@ -0,0 +1,107 @@
//
//  CachedResponseHandler.swift
//
//  Copyright (c) 2019 Alamofire Software Foundation (http://alamofire.org/)
//
//  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:
//
//  The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
//
//  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.
//
import Foundation
/// A type that handles whether the data task should store the HTTP response in the cache.
public protocol CachedResponseHandler {
    /// Determines whether the HTTP response should be stored in the cache.
    ///
    /// The `completion` closure should be passed one of three possible options:
    ///
    ///   1. The cached response provided by the server (this is the most common use case).
    ///   2. A modified version of the cached response (you may want to modify it in some way before caching).
    ///   3. A `nil` value to prevent the cached response from being stored in the cache.
    ///
    /// - Parameters:
    ///   - task:       The data task whose request resulted in the cached response.
    ///   - response:   The cached response to potentially store in the cache.
    ///   - completion: The closure to execute containing cached response, a modified response, or `nil`.
    func dataTask(_ task: URLSessionDataTask,
                  willCacheResponse response: CachedURLResponse,
                  completion: @escaping (CachedURLResponse?) -> Void)
}
// MARK: -
/// `ResponseCacher` is a convenience `CachedResponseHandler` making it easy to cache, not cache, or modify a cached
/// response.
public struct ResponseCacher {
    /// Defines the behavior of the `ResponseCacher` type.
    public enum Behavior {
        /// Stores the cached response in the cache.
        case cache
        /// Prevents the cached response from being stored in the cache.
        case doNotCache
        /// Modifies the cached response before storing it in the cache.
        case modify((URLSessionDataTask, CachedURLResponse) -> CachedURLResponse?)
    }
    /// Returns a `ResponseCacher` with a `.cache` `Behavior`.
    public static let cache = ResponseCacher(behavior: .cache)
    /// Returns a `ResponseCacher` with a `.doNotCache` `Behavior`.
    public static let doNotCache = ResponseCacher(behavior: .doNotCache)
    /// The `Behavior` of the `ResponseCacher`.
    public let behavior: Behavior
    /// Creates a `ResponseCacher` instance from the `Behavior`.
    ///
    /// - Parameter behavior: The `Behavior`.
    public init(behavior: Behavior) {
        self.behavior = behavior
    }
}
extension ResponseCacher: CachedResponseHandler {
    public func dataTask(_ task: URLSessionDataTask,
                         willCacheResponse response: CachedURLResponse,
                         completion: @escaping (CachedURLResponse?) -> Void) {
        switch behavior {
        case .cache:
            completion(response)
        case .doNotCache:
            completion(nil)
        case let .modify(closure):
            let response = closure(task, response)
            completion(response)
        }
    }
}
extension CachedResponseHandler where Self == ResponseCacher {
    /// Provides a `ResponseCacher` which caches the response, if allowed. Equivalent to `ResponseCacher.cache`.
    public static var cache: ResponseCacher { .cache }
    /// Provides a `ResponseCacher` which does not cache the response. Equivalent to `ResponseCacher.doNotCache`.
    public static var doNotCache: ResponseCacher { .doNotCache }
    /// Creates a `ResponseCacher` which modifies the proposed `CachedURLResponse` using the provided closure.
    ///
    /// - Parameter closure: Closure used to modify the `CachedURLResponse`.
    /// - Returns:           The `ResponseCacher`.
    public static func modify(using closure: @escaping ((URLSessionDataTask, CachedURLResponse) -> CachedURLResponse?)) -> ResponseCacher {
        ResponseCacher(behavior: .modify(closure))
    }
}
Pods/Alamofire/Source/Combine.swift
New file
@@ -0,0 +1,652 @@
//
//  Combine.swift
//
//  Copyright (c) 2020 Alamofire Software Foundation (http://alamofire.org/)
//
//  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:
//
//  The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
//
//  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.
//
#if !((os(iOS) && (arch(i386) || arch(arm))) || os(Windows) || os(Linux) || os(Android))
import Combine
import Dispatch
import Foundation
// MARK: - DataRequest / UploadRequest
/// A Combine `Publisher` that publishes the `DataResponse<Value, AFError>` of the provided `DataRequest`.
@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *)
public struct DataResponsePublisher<Value>: Publisher {
    public typealias Output = DataResponse<Value, AFError>
    public typealias Failure = Never
    private typealias Handler = (@escaping (_ response: DataResponse<Value, AFError>) -> Void) -> DataRequest
    private let request: DataRequest
    private let responseHandler: Handler
    /// Creates an instance which will serialize responses using the provided `ResponseSerializer`.
    ///
    /// - Parameters:
    ///   - request:    `DataRequest` for which to publish the response.
    ///   - queue:      `DispatchQueue` on which the `DataResponse` value will be published. `.main` by default.
    ///   - serializer: `ResponseSerializer` used to produce the published `DataResponse`.
    public init<Serializer: ResponseSerializer>(_ request: DataRequest, queue: DispatchQueue, serializer: Serializer)
        where Value == Serializer.SerializedObject {
        self.request = request
        responseHandler = { request.response(queue: queue, responseSerializer: serializer, completionHandler: $0) }
    }
    /// Creates an instance which will serialize responses using the provided `DataResponseSerializerProtocol`.
    ///
    /// - Parameters:
    ///   - request:    `DataRequest` for which to publish the response.
    ///   - queue:      `DispatchQueue` on which the `DataResponse` value will be published. `.main` by default.
    ///   - serializer: `DataResponseSerializerProtocol` used to produce the published `DataResponse`.
    public init<Serializer: DataResponseSerializerProtocol>(_ request: DataRequest,
                                                            queue: DispatchQueue,
                                                            serializer: Serializer)
        where Value == Serializer.SerializedObject {
        self.request = request
        responseHandler = { request.response(queue: queue, responseSerializer: serializer, completionHandler: $0) }
    }
    /// Publishes only the `Result` of the `DataResponse` value.
    ///
    /// - Returns: The `AnyPublisher` publishing the `Result<Value, AFError>` value.
    public func result() -> AnyPublisher<Result<Value, AFError>, Never> {
        map(\.result).eraseToAnyPublisher()
    }
    /// Publishes the `Result` of the `DataResponse` as a single `Value` or fail with the `AFError` instance.
    ///
    /// - Returns: The `AnyPublisher<Value, AFError>` publishing the stream.
    public func value() -> AnyPublisher<Value, AFError> {
        setFailureType(to: AFError.self).flatMap(\.result.publisher).eraseToAnyPublisher()
    }
    public func receive<S>(subscriber: S) where S: Subscriber, DataResponsePublisher.Failure == S.Failure, DataResponsePublisher.Output == S.Input {
        subscriber.receive(subscription: Inner(request: request,
                                               responseHandler: responseHandler,
                                               downstream: subscriber))
    }
    private final class Inner<Downstream: Subscriber>: Subscription
        where Downstream.Input == Output {
        typealias Failure = Downstream.Failure
        private let downstream: Protected<Downstream?>
        private let request: DataRequest
        private let responseHandler: Handler
        init(request: DataRequest, responseHandler: @escaping Handler, downstream: Downstream) {
            self.request = request
            self.responseHandler = responseHandler
            self.downstream = Protected(downstream)
        }
        func request(_ demand: Subscribers.Demand) {
            assert(demand > 0)
            guard let downstream = downstream.read({ $0 }) else { return }
            self.downstream.write(nil)
            responseHandler { response in
                _ = downstream.receive(response)
                downstream.receive(completion: .finished)
            }.resume()
        }
        func cancel() {
            request.cancel()
            downstream.write(nil)
        }
    }
}
@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *)
extension DataResponsePublisher where Value == Data? {
    /// Creates an instance which publishes a `DataResponse<Data?, AFError>` value without serialization.
    @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *)
    public init(_ request: DataRequest, queue: DispatchQueue) {
        self.request = request
        responseHandler = { request.response(queue: queue, completionHandler: $0) }
    }
}
extension DataRequest {
    /// Creates a `DataResponsePublisher` for this instance using the given `ResponseSerializer` and `DispatchQueue`.
    ///
    /// - Parameters:
    ///   - serializer: `ResponseSerializer` used to serialize response `Data`.
    ///   - queue:      `DispatchQueue` on which the `DataResponse` will be published. `.main` by default.
    ///
    /// - Returns:      The `DataResponsePublisher`.
    @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *)
    public func publishResponse<Serializer: ResponseSerializer, T>(using serializer: Serializer, on queue: DispatchQueue = .main) -> DataResponsePublisher<T>
        where Serializer.SerializedObject == T {
        DataResponsePublisher(self, queue: queue, serializer: serializer)
    }
    /// Creates a `DataResponsePublisher` for this instance and uses a `DataResponseSerializer` to serialize the
    /// response.
    ///
    /// - Parameters:
    ///   - queue:               `DispatchQueue` on which the `DataResponse` will be published. `.main` by default.
    ///   - preprocessor:        `DataPreprocessor` which filters the `Data` before serialization. `PassthroughPreprocessor()`
    ///                          by default.
    ///   - emptyResponseCodes:  `Set<Int>` of HTTP status codes for which empty responses are allowed. `[204, 205]` by
    ///                          default.
    ///   - emptyRequestMethods: `Set<HTTPMethod>` of `HTTPMethod`s for which empty responses are allowed, regardless of
    ///                          status code. `[.head]` by default.
    /// - Returns:               The `DataResponsePublisher`.
    @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *)
    public func publishData(queue: DispatchQueue = .main,
                            preprocessor: DataPreprocessor = DataResponseSerializer.defaultDataPreprocessor,
                            emptyResponseCodes: Set<Int> = DataResponseSerializer.defaultEmptyResponseCodes,
                            emptyRequestMethods: Set<HTTPMethod> = DataResponseSerializer.defaultEmptyRequestMethods) -> DataResponsePublisher<Data> {
        publishResponse(using: DataResponseSerializer(dataPreprocessor: preprocessor,
                                                      emptyResponseCodes: emptyResponseCodes,
                                                      emptyRequestMethods: emptyRequestMethods),
                        on: queue)
    }
    /// Creates a `DataResponsePublisher` for this instance and uses a `StringResponseSerializer` to serialize the
    /// response.
    ///
    /// - Parameters:
    ///   - queue:               `DispatchQueue` on which the `DataResponse` will be published. `.main` by default.
    ///   - preprocessor:        `DataPreprocessor` which filters the `Data` before serialization. `PassthroughPreprocessor()`
    ///                          by default.
    ///   - encoding:            `String.Encoding` to parse the response. `nil` by default, in which case the encoding
    ///                          will be determined by the server response, falling back to the default HTTP character
    ///                          set, `ISO-8859-1`.
    ///   - emptyResponseCodes:  `Set<Int>` of HTTP status codes for which empty responses are allowed. `[204, 205]` by
    ///                          default.
    ///   - emptyRequestMethods: `Set<HTTPMethod>` of `HTTPMethod`s for which empty responses are allowed, regardless of
    ///                          status code. `[.head]` by default.
    ///
    /// - Returns:               The `DataResponsePublisher`.
    @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *)
    public func publishString(queue: DispatchQueue = .main,
                              preprocessor: DataPreprocessor = StringResponseSerializer.defaultDataPreprocessor,
                              encoding: String.Encoding? = nil,
                              emptyResponseCodes: Set<Int> = StringResponseSerializer.defaultEmptyResponseCodes,
                              emptyRequestMethods: Set<HTTPMethod> = StringResponseSerializer.defaultEmptyRequestMethods) -> DataResponsePublisher<String> {
        publishResponse(using: StringResponseSerializer(dataPreprocessor: preprocessor,
                                                        encoding: encoding,
                                                        emptyResponseCodes: emptyResponseCodes,
                                                        emptyRequestMethods: emptyRequestMethods),
                        on: queue)
    }
    @_disfavoredOverload
    @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *)
    @available(*, deprecated, message: "Renamed publishDecodable(type:queue:preprocessor:decoder:emptyResponseCodes:emptyRequestMethods).")
    public func publishDecodable<T: Decodable>(type: T.Type = T.self,
                                               queue: DispatchQueue = .main,
                                               preprocessor: DataPreprocessor = DecodableResponseSerializer<T>.defaultDataPreprocessor,
                                               decoder: DataDecoder = JSONDecoder(),
                                               emptyResponseCodes: Set<Int> = DecodableResponseSerializer<T>.defaultEmptyResponseCodes,
                                               emptyResponseMethods: Set<HTTPMethod> = DecodableResponseSerializer<T>.defaultEmptyRequestMethods) -> DataResponsePublisher<T> {
        publishResponse(using: DecodableResponseSerializer(dataPreprocessor: preprocessor,
                                                           decoder: decoder,
                                                           emptyResponseCodes: emptyResponseCodes,
                                                           emptyRequestMethods: emptyResponseMethods),
                        on: queue)
    }
    /// Creates a `DataResponsePublisher` for this instance and uses a `DecodableResponseSerializer` to serialize the
    /// response.
    ///
    /// - Parameters:
    ///   - type:                `Decodable` type to which to decode response `Data`. Inferred from the context by
    ///                          default.
    ///   - queue:               `DispatchQueue` on which the `DataResponse` will be published. `.main` by default.
    ///   - preprocessor:        `DataPreprocessor` which filters the `Data` before serialization.
    ///                          `PassthroughPreprocessor()` by default.
    ///   - decoder:             `DataDecoder` instance used to decode response `Data`. `JSONDecoder()` by default.
    ///   - emptyResponseCodes:  `Set<Int>` of HTTP status codes for which empty responses are allowed. `[204, 205]` by
    ///                          default.
    ///   - emptyRequestMethods: `Set<HTTPMethod>` of `HTTPMethod`s for which empty responses are allowed, regardless of
    ///                          status code. `[.head]` by default.
    ///
    /// - Returns:               The `DataResponsePublisher`.
    @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *)
    public func publishDecodable<T: Decodable>(type: T.Type = T.self,
                                               queue: DispatchQueue = .main,
                                               preprocessor: DataPreprocessor = DecodableResponseSerializer<T>.defaultDataPreprocessor,
                                               decoder: DataDecoder = JSONDecoder(),
                                               emptyResponseCodes: Set<Int> = DecodableResponseSerializer<T>.defaultEmptyResponseCodes,
                                               emptyRequestMethods: Set<HTTPMethod> = DecodableResponseSerializer<T>.defaultEmptyRequestMethods) -> DataResponsePublisher<T> {
        publishResponse(using: DecodableResponseSerializer(dataPreprocessor: preprocessor,
                                                           decoder: decoder,
                                                           emptyResponseCodes: emptyResponseCodes,
                                                           emptyRequestMethods: emptyRequestMethods),
                        on: queue)
    }
    /// Creates a `DataResponsePublisher` for this instance which does not serialize the response before publishing.
    ///
    ///   - queue: `DispatchQueue` on which the `DataResponse` will be published. `.main` by default.
    ///
    /// - Returns: The `DataResponsePublisher`.
    @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *)
    public func publishUnserialized(queue: DispatchQueue = .main) -> DataResponsePublisher<Data?> {
        DataResponsePublisher(self, queue: queue)
    }
}
// A Combine `Publisher` that publishes a sequence of `Stream<Value, AFError>` values received by the provided `DataStreamRequest`.
@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *)
public struct DataStreamPublisher<Value>: Publisher {
    public typealias Output = DataStreamRequest.Stream<Value, AFError>
    public typealias Failure = Never
    private typealias Handler = (@escaping DataStreamRequest.Handler<Value, AFError>) -> DataStreamRequest
    private let request: DataStreamRequest
    private let streamHandler: Handler
    /// Creates an instance which will serialize responses using the provided `DataStreamSerializer`.
    ///
    /// - Parameters:
    ///   - request:    `DataStreamRequest` for which to publish the response.
    ///   - queue:      `DispatchQueue` on which the `Stream<Value, AFError>` values will be published. `.main` by
    ///                 default.
    ///   - serializer: `DataStreamSerializer` used to produce the published `Stream<Value, AFError>` values.
    public init<Serializer: DataStreamSerializer>(_ request: DataStreamRequest, queue: DispatchQueue, serializer: Serializer)
        where Value == Serializer.SerializedObject {
        self.request = request
        streamHandler = { request.responseStream(using: serializer, on: queue, stream: $0) }
    }
    /// Publishes only the `Result` of the `DataStreamRequest.Stream`'s `Event`s.
    ///
    /// - Returns: The `AnyPublisher` publishing the `Result<Value, AFError>` value.
    public func result() -> AnyPublisher<Result<Value, AFError>, Never> {
        compactMap { stream in
            switch stream.event {
            case let .stream(result):
                return result
            // If the stream has completed with an error, send the error value downstream as a `.failure`.
            case let .complete(completion):
                return completion.error.map(Result.failure)
            }
        }
        .eraseToAnyPublisher()
    }
    /// Publishes the streamed values of the `DataStreamRequest.Stream` as a sequence of `Value` or fail with the
    /// `AFError` instance.
    ///
    /// - Returns: The `AnyPublisher<Value, AFError>` publishing the stream.
    public func value() -> AnyPublisher<Value, AFError> {
        result().setFailureType(to: AFError.self).flatMap(\.publisher).eraseToAnyPublisher()
    }
    public func receive<S>(subscriber: S) where S: Subscriber, DataStreamPublisher.Failure == S.Failure, DataStreamPublisher.Output == S.Input {
        subscriber.receive(subscription: Inner(request: request,
                                               streamHandler: streamHandler,
                                               downstream: subscriber))
    }
    private final class Inner<Downstream: Subscriber>: Subscription
        where Downstream.Input == Output {
        typealias Failure = Downstream.Failure
        private let downstream: Protected<Downstream?>
        private let request: DataStreamRequest
        private let streamHandler: Handler
        init(request: DataStreamRequest, streamHandler: @escaping Handler, downstream: Downstream) {
            self.request = request
            self.streamHandler = streamHandler
            self.downstream = Protected(downstream)
        }
        func request(_ demand: Subscribers.Demand) {
            assert(demand > 0)
            guard let downstream = downstream.read({ $0 }) else { return }
            self.downstream.write(nil)
            streamHandler { stream in
                _ = downstream.receive(stream)
                if case .complete = stream.event {
                    downstream.receive(completion: .finished)
                }
            }.resume()
        }
        func cancel() {
            request.cancel()
            downstream.write(nil)
        }
    }
}
extension DataStreamRequest {
    /// Creates a `DataStreamPublisher` for this instance using the given `DataStreamSerializer` and `DispatchQueue`.
    ///
    /// - Parameters:
    ///   - serializer: `DataStreamSerializer` used to serialize the streamed `Data`.
    ///   - queue:      `DispatchQueue` on which the `DataRequest.Stream` values will be published. `.main` by default.
    /// - Returns:      The `DataStreamPublisher`.
    @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *)
    public func publishStream<Serializer: DataStreamSerializer>(using serializer: Serializer,
                                                                on queue: DispatchQueue = .main) -> DataStreamPublisher<Serializer.SerializedObject> {
        DataStreamPublisher(self, queue: queue, serializer: serializer)
    }
    /// Creates a `DataStreamPublisher` for this instance which uses a `PassthroughStreamSerializer` to stream `Data`
    /// unserialized.
    ///
    /// - Parameters:
    ///   - queue:      `DispatchQueue` on which the `DataRequest.Stream` values will be published. `.main` by default.
    /// - Returns:      The `DataStreamPublisher`.
    @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *)
    public func publishData(queue: DispatchQueue = .main) -> DataStreamPublisher<Data> {
        publishStream(using: PassthroughStreamSerializer(), on: queue)
    }
    /// Creates a `DataStreamPublisher` for this instance which uses a `StringStreamSerializer` to serialize stream
    /// `Data` values into `String` values.
    ///
    /// - Parameters:
    ///   - queue:      `DispatchQueue` on which the `DataRequest.Stream` values will be published. `.main` by default.
    /// - Returns:      The `DataStreamPublisher`.
    @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *)
    public func publishString(queue: DispatchQueue = .main) -> DataStreamPublisher<String> {
        publishStream(using: StringStreamSerializer(), on: queue)
    }
    /// Creates a `DataStreamPublisher` for this instance which uses a `DecodableStreamSerializer` with the provided
    /// parameters to serialize stream `Data` values into the provided type.
    ///
    /// - Parameters:
    ///   - type:         `Decodable` type to which to decode stream `Data`. Inferred from the context by default.
    ///   - queue:        `DispatchQueue` on which the `DataRequest.Stream` values will be published. `.main` by default.
    ///   - decoder:      `DataDecoder` instance used to decode stream `Data`. `JSONDecoder()` by default.
    ///   - preprocessor: `DataPreprocessor` which filters incoming stream `Data` before serialization.
    ///                   `PassthroughPreprocessor()` by default.
    /// - Returns:        The `DataStreamPublisher`.
    @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *)
    public func publishDecodable<T: Decodable>(type: T.Type = T.self,
                                               queue: DispatchQueue = .main,
                                               decoder: DataDecoder = JSONDecoder(),
                                               preprocessor: DataPreprocessor = PassthroughPreprocessor()) -> DataStreamPublisher<T> {
        publishStream(using: DecodableStreamSerializer(decoder: decoder,
                                                       dataPreprocessor: preprocessor),
                      on: queue)
    }
}
/// A Combine `Publisher` that publishes the `DownloadResponse<Value, AFError>` of the provided `DownloadRequest`.
@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *)
public struct DownloadResponsePublisher<Value>: Publisher {
    public typealias Output = DownloadResponse<Value, AFError>
    public typealias Failure = Never
    private typealias Handler = (@escaping (_ response: DownloadResponse<Value, AFError>) -> Void) -> DownloadRequest
    private let request: DownloadRequest
    private let responseHandler: Handler
    /// Creates an instance which will serialize responses using the provided `ResponseSerializer`.
    ///
    /// - Parameters:
    ///   - request:    `DownloadRequest` for which to publish the response.
    ///   - queue:      `DispatchQueue` on which the `DownloadResponse` value will be published. `.main` by default.
    ///   - serializer: `ResponseSerializer` used to produce the published `DownloadResponse`.
    public init<Serializer: ResponseSerializer>(_ request: DownloadRequest, queue: DispatchQueue, serializer: Serializer)
        where Value == Serializer.SerializedObject {
        self.request = request
        responseHandler = { request.response(queue: queue, responseSerializer: serializer, completionHandler: $0) }
    }
    /// Creates an instance which will serialize responses using the provided `DownloadResponseSerializerProtocol` value.
    ///
    /// - Parameters:
    ///   - request:    `DownloadRequest` for which to publish the response.
    ///   - queue:      `DispatchQueue` on which the `DataResponse` value will be published. `.main` by default.
    ///   - serializer: `DownloadResponseSerializerProtocol` used to produce the published `DownloadResponse`.
    @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *)
    public init<Serializer: DownloadResponseSerializerProtocol>(_ request: DownloadRequest,
                                                                queue: DispatchQueue,
                                                                serializer: Serializer)
        where Value == Serializer.SerializedObject {
        self.request = request
        responseHandler = { request.response(queue: queue, responseSerializer: serializer, completionHandler: $0) }
    }
    /// Publishes only the `Result` of the `DownloadResponse` value.
    ///
    /// - Returns: The `AnyPublisher` publishing the `Result<Value, AFError>` value.
    public func result() -> AnyPublisher<Result<Value, AFError>, Never> {
        map(\.result).eraseToAnyPublisher()
    }
    /// Publishes the `Result` of the `DownloadResponse` as a single `Value` or fail with the `AFError` instance.
    ///
    /// - Returns: The `AnyPublisher<Value, AFError>` publishing the stream.
    public func value() -> AnyPublisher<Value, AFError> {
        setFailureType(to: AFError.self).flatMap(\.result.publisher).eraseToAnyPublisher()
    }
    public func receive<S>(subscriber: S) where S: Subscriber, DownloadResponsePublisher.Failure == S.Failure, DownloadResponsePublisher.Output == S.Input {
        subscriber.receive(subscription: Inner(request: request,
                                               responseHandler: responseHandler,
                                               downstream: subscriber))
    }
    private final class Inner<Downstream: Subscriber>: Subscription
        where Downstream.Input == Output {
        typealias Failure = Downstream.Failure
        private let downstream: Protected<Downstream?>
        private let request: DownloadRequest
        private let responseHandler: Handler
        init(request: DownloadRequest, responseHandler: @escaping Handler, downstream: Downstream) {
            self.request = request
            self.responseHandler = responseHandler
            self.downstream = Protected(downstream)
        }
        func request(_ demand: Subscribers.Demand) {
            assert(demand > 0)
            guard let downstream = downstream.read({ $0 }) else { return }
            self.downstream.write(nil)
            responseHandler { response in
                _ = downstream.receive(response)
                downstream.receive(completion: .finished)
            }.resume()
        }
        func cancel() {
            request.cancel()
            downstream.write(nil)
        }
    }
}
extension DownloadRequest {
    /// Creates a `DownloadResponsePublisher` for this instance using the given `ResponseSerializer` and `DispatchQueue`.
    ///
    /// - Parameters:
    ///   - serializer: `ResponseSerializer` used to serialize the response `Data` from disk.
    ///   - queue:      `DispatchQueue` on which the `DownloadResponse` will be published.`.main` by default.
    ///
    /// - Returns:      The `DownloadResponsePublisher`.
    @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *)
    public func publishResponse<Serializer: ResponseSerializer, T>(using serializer: Serializer, on queue: DispatchQueue = .main) -> DownloadResponsePublisher<T>
        where Serializer.SerializedObject == T {
        DownloadResponsePublisher(self, queue: queue, serializer: serializer)
    }
    /// Creates a `DownloadResponsePublisher` for this instance using the given `DownloadResponseSerializerProtocol` and
    /// `DispatchQueue`.
    ///
    /// - Parameters:
    ///   - serializer: `DownloadResponseSerializer` used to serialize the response `Data` from disk.
    ///   - queue:      `DispatchQueue` on which the `DownloadResponse` will be published.`.main` by default.
    ///
    /// - Returns:      The `DownloadResponsePublisher`.
    @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *)
    public func publishResponse<Serializer: DownloadResponseSerializerProtocol, T>(using serializer: Serializer, on queue: DispatchQueue = .main) -> DownloadResponsePublisher<T>
        where Serializer.SerializedObject == T {
        DownloadResponsePublisher(self, queue: queue, serializer: serializer)
    }
    /// Creates a `DownloadResponsePublisher` for this instance and uses a `URLResponseSerializer` to serialize the
    /// response.
    ///
    /// - Parameter queue: `DispatchQueue` on which the `DownloadResponse` will be published. `.main` by default.
    ///
    /// - Returns:         The `DownloadResponsePublisher`.
    @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *)
    public func publishURL(queue: DispatchQueue = .main) -> DownloadResponsePublisher<URL> {
        publishResponse(using: URLResponseSerializer(), on: queue)
    }
    /// Creates a `DownloadResponsePublisher` for this instance and uses a `DataResponseSerializer` to serialize the
    /// response.
    ///
    /// - Parameters:
    ///   - queue:               `DispatchQueue` on which the `DownloadResponse` will be published. `.main` by default.
    ///   - preprocessor:        `DataPreprocessor` which filters the `Data` before serialization. `PassthroughPreprocessor()`
    ///                          by default.
    ///   - emptyResponseCodes:  `Set<Int>` of HTTP status codes for which empty responses are allowed. `[204, 205]` by
    ///                          default.
    ///   - emptyRequestMethods: `Set<HTTPMethod>` of `HTTPMethod`s for which empty responses are allowed, regardless of
    ///                          status code. `[.head]` by default.
    ///
    /// - Returns:               The `DownloadResponsePublisher`.
    @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *)
    public func publishData(queue: DispatchQueue = .main,
                            preprocessor: DataPreprocessor = DataResponseSerializer.defaultDataPreprocessor,
                            emptyResponseCodes: Set<Int> = DataResponseSerializer.defaultEmptyResponseCodes,
                            emptyRequestMethods: Set<HTTPMethod> = DataResponseSerializer.defaultEmptyRequestMethods) -> DownloadResponsePublisher<Data> {
        publishResponse(using: DataResponseSerializer(dataPreprocessor: preprocessor,
                                                      emptyResponseCodes: emptyResponseCodes,
                                                      emptyRequestMethods: emptyRequestMethods),
                        on: queue)
    }
    /// Creates a `DownloadResponsePublisher` for this instance and uses a `StringResponseSerializer` to serialize the
    /// response.
    ///
    /// - Parameters:
    ///   - queue:               `DispatchQueue` on which the `DataResponse` will be published. `.main` by default.
    ///   - preprocessor:        `DataPreprocessor` which filters the `Data` before serialization. `PassthroughPreprocessor()`
    ///                          by default.
    ///   - encoding:            `String.Encoding` to parse the response. `nil` by default, in which case the encoding
    ///                          will be determined by the server response, falling back to the default HTTP character
    ///                          set, `ISO-8859-1`.
    ///   - emptyResponseCodes:  `Set<Int>` of HTTP status codes for which empty responses are allowed. `[204, 205]` by
    ///                          default.
    ///   - emptyRequestMethods: `Set<HTTPMethod>` of `HTTPMethod`s for which empty responses are allowed, regardless of
    ///                          status code. `[.head]` by default.
    ///
    /// - Returns:               The `DownloadResponsePublisher`.
    @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *)
    public func publishString(queue: DispatchQueue = .main,
                              preprocessor: DataPreprocessor = StringResponseSerializer.defaultDataPreprocessor,
                              encoding: String.Encoding? = nil,
                              emptyResponseCodes: Set<Int> = StringResponseSerializer.defaultEmptyResponseCodes,
                              emptyRequestMethods: Set<HTTPMethod> = StringResponseSerializer.defaultEmptyRequestMethods) -> DownloadResponsePublisher<String> {
        publishResponse(using: StringResponseSerializer(dataPreprocessor: preprocessor,
                                                        encoding: encoding,
                                                        emptyResponseCodes: emptyResponseCodes,
                                                        emptyRequestMethods: emptyRequestMethods),
                        on: queue)
    }
    @_disfavoredOverload
    @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *)
    @available(*, deprecated, message: "Renamed publishDecodable(type:queue:preprocessor:decoder:emptyResponseCodes:emptyRequestMethods).")
    public func publishDecodable<T: Decodable>(type: T.Type = T.self,
                                               queue: DispatchQueue = .main,
                                               preprocessor: DataPreprocessor = DecodableResponseSerializer<T>.defaultDataPreprocessor,
                                               decoder: DataDecoder = JSONDecoder(),
                                               emptyResponseCodes: Set<Int> = DecodableResponseSerializer<T>.defaultEmptyResponseCodes,
                                               emptyResponseMethods: Set<HTTPMethod> = DecodableResponseSerializer<T>.defaultEmptyRequestMethods) -> DownloadResponsePublisher<T> {
        publishResponse(using: DecodableResponseSerializer(dataPreprocessor: preprocessor,
                                                           decoder: decoder,
                                                           emptyResponseCodes: emptyResponseCodes,
                                                           emptyRequestMethods: emptyResponseMethods),
                        on: queue)
    }
    /// Creates a `DownloadResponsePublisher` for this instance and uses a `DecodableResponseSerializer` to serialize
    /// the response.
    ///
    /// - Parameters:
    ///   - type:                `Decodable` type to which to decode response `Data`. Inferred from the context by default.
    ///   - queue:               `DispatchQueue` on which the `DataResponse` will be published. `.main` by default.
    ///   - preprocessor:        `DataPreprocessor` which filters the `Data` before serialization.
    ///                          `PassthroughPreprocessor()` by default.
    ///   - decoder:             `DataDecoder` instance used to decode response `Data`. `JSONDecoder()` by default.
    ///   - emptyResponseCodes:  `Set<Int>` of HTTP status codes for which empty responses are allowed. `[204, 205]` by
    ///                          default.
    ///   - emptyRequestMethods: `Set<HTTPMethod>` of `HTTPMethod`s for which empty responses are allowed, regardless
    ///                          of status code. `[.head]` by default.
    ///
    /// - Returns:               The `DownloadResponsePublisher`.
    @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *)
    public func publishDecodable<T: Decodable>(type: T.Type = T.self,
                                               queue: DispatchQueue = .main,
                                               preprocessor: DataPreprocessor = DecodableResponseSerializer<T>.defaultDataPreprocessor,
                                               decoder: DataDecoder = JSONDecoder(),
                                               emptyResponseCodes: Set<Int> = DecodableResponseSerializer<T>.defaultEmptyResponseCodes,
                                               emptyRequestMethods: Set<HTTPMethod> = DecodableResponseSerializer<T>.defaultEmptyRequestMethods) -> DownloadResponsePublisher<T> {
        publishResponse(using: DecodableResponseSerializer(dataPreprocessor: preprocessor,
                                                           decoder: decoder,
                                                           emptyResponseCodes: emptyResponseCodes,
                                                           emptyRequestMethods: emptyRequestMethods),
                        on: queue)
    }
}
@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *)
extension DownloadResponsePublisher where Value == URL? {
    /// Creates an instance which publishes a `DownloadResponse<URL?, AFError>` value without serialization.
    @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *)
    public init(_ request: DownloadRequest, queue: DispatchQueue) {
        self.request = request
        responseHandler = { request.response(queue: queue, completionHandler: $0) }
    }
}
extension DownloadRequest {
    /// Creates a `DownloadResponsePublisher` for this instance which does not serialize the response before publishing.
    ///
    /// - Parameter queue: `DispatchQueue` on which the `DownloadResponse` will be published. `.main` by default.
    ///
    /// - Returns:         The `DownloadResponsePublisher`.
    @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *)
    public func publishUnserialized(on queue: DispatchQueue = .main) -> DownloadResponsePublisher<URL?> {
        DownloadResponsePublisher(self, queue: queue)
    }
}
#endif
Pods/Alamofire/Source/Concurrency.swift
New file
@@ -0,0 +1,832 @@
//
//  Concurrency.swift
//
//  Copyright (c) 2021 Alamofire Software Foundation (http://alamofire.org/)
//
//  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:
//
//  The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
//
//  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.
//
#if compiler(>=5.6.0) && canImport(_Concurrency)
import Foundation
// MARK: - Request Event Streams
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
extension Request {
    /// Creates a `StreamOf<Progress>` for the instance's upload progress.
    ///
    /// - Parameter bufferingPolicy: `BufferingPolicy` that determines the stream's buffering behavior.`.unbounded` by default.
    ///
    /// - Returns:                   The `StreamOf<Progress>`.
    public func uploadProgress(bufferingPolicy: StreamOf<Progress>.BufferingPolicy = .unbounded) -> StreamOf<Progress> {
        stream(bufferingPolicy: bufferingPolicy) { [unowned self] continuation in
            uploadProgress(queue: underlyingQueue) { progress in
                continuation.yield(progress)
            }
        }
    }
    /// Creates a `StreamOf<Progress>` for the instance's download progress.
    ///
    /// - Parameter bufferingPolicy: `BufferingPolicy` that determines the stream's buffering behavior.`.unbounded` by default.
    ///
    /// - Returns:                   The `StreamOf<Progress>`.
    public func downloadProgress(bufferingPolicy: StreamOf<Progress>.BufferingPolicy = .unbounded) -> StreamOf<Progress> {
        stream(bufferingPolicy: bufferingPolicy) { [unowned self] continuation in
            downloadProgress(queue: underlyingQueue) { progress in
                continuation.yield(progress)
            }
        }
    }
    /// Creates a `StreamOf<URLRequest>` for the `URLRequest`s produced for the instance.
    ///
    /// - Parameter bufferingPolicy: `BufferingPolicy` that determines the stream's buffering behavior.`.unbounded` by default.
    ///
    /// - Returns:                   The `StreamOf<URLRequest>`.
    public func urlRequests(bufferingPolicy: StreamOf<URLRequest>.BufferingPolicy = .unbounded) -> StreamOf<URLRequest> {
        stream(bufferingPolicy: bufferingPolicy) { [unowned self] continuation in
            onURLRequestCreation(on: underlyingQueue) { request in
                continuation.yield(request)
            }
        }
    }
    /// Creates a `StreamOf<URLSessionTask>` for the `URLSessionTask`s produced for the instance.
    ///
    /// - Parameter bufferingPolicy: `BufferingPolicy` that determines the stream's buffering behavior.`.unbounded` by default.
    ///
    /// - Returns:                   The `StreamOf<URLSessionTask>`.
    public func urlSessionTasks(bufferingPolicy: StreamOf<URLSessionTask>.BufferingPolicy = .unbounded) -> StreamOf<URLSessionTask> {
        stream(bufferingPolicy: bufferingPolicy) { [unowned self] continuation in
            onURLSessionTaskCreation(on: underlyingQueue) { task in
                continuation.yield(task)
            }
        }
    }
    /// Creates a `StreamOf<String>` for the cURL descriptions produced for the instance.
    ///
    /// - Parameter bufferingPolicy: `BufferingPolicy` that determines the stream's buffering behavior.`.unbounded` by default.
    ///
    /// - Returns:                   The `StreamOf<String>`.
    public func cURLDescriptions(bufferingPolicy: StreamOf<String>.BufferingPolicy = .unbounded) -> StreamOf<String> {
        stream(bufferingPolicy: bufferingPolicy) { [unowned self] continuation in
            cURLDescription(on: underlyingQueue) { description in
                continuation.yield(description)
            }
        }
    }
    fileprivate func stream<T>(of type: T.Type = T.self,
                               bufferingPolicy: StreamOf<T>.BufferingPolicy = .unbounded,
                               yielder: @escaping (StreamOf<T>.Continuation) -> Void) -> StreamOf<T> {
        StreamOf<T>(bufferingPolicy: bufferingPolicy) { [unowned self] continuation in
            yielder(continuation)
            // Must come after serializers run in order to catch retry progress.
            onFinish {
                continuation.finish()
            }
        }
    }
}
// MARK: - DataTask
/// Value used to `await` a `DataResponse` and associated values.
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
public struct DataTask<Value> {
    /// `DataResponse` produced by the `DataRequest` and its response handler.
    public var response: DataResponse<Value, AFError> {
        get async {
            if shouldAutomaticallyCancel {
                return await withTaskCancellationHandler {
                    await task.value
                } onCancel: {
                    cancel()
                }
            } else {
                return await task.value
            }
        }
    }
    /// `Result` of any response serialization performed for the `response`.
    public var result: Result<Value, AFError> {
        get async { await response.result }
    }
    /// `Value` returned by the `response`.
    public var value: Value {
        get async throws {
            try await result.get()
        }
    }
    private let request: DataRequest
    private let task: Task<DataResponse<Value, AFError>, Never>
    private let shouldAutomaticallyCancel: Bool
    fileprivate init(request: DataRequest, task: Task<DataResponse<Value, AFError>, Never>, shouldAutomaticallyCancel: Bool) {
        self.request = request
        self.task = task
        self.shouldAutomaticallyCancel = shouldAutomaticallyCancel
    }
    /// Cancel the underlying `DataRequest` and `Task`.
    public func cancel() {
        task.cancel()
    }
    /// Resume the underlying `DataRequest`.
    public func resume() {
        request.resume()
    }
    /// Suspend the underlying `DataRequest`.
    public func suspend() {
        request.suspend()
    }
}
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
extension DataRequest {
    /// Creates a `StreamOf<HTTPURLResponse>` for the instance's responses.
    ///
    /// - Parameter bufferingPolicy: `BufferingPolicy` that determines the stream's buffering behavior.`.unbounded` by default.
    ///
    /// - Returns:                   The `StreamOf<HTTPURLResponse>`.
    public func httpResponses(bufferingPolicy: StreamOf<HTTPURLResponse>.BufferingPolicy = .unbounded) -> StreamOf<HTTPURLResponse> {
        stream(bufferingPolicy: bufferingPolicy) { [unowned self] continuation in
            onHTTPResponse(on: underlyingQueue) { response in
                continuation.yield(response)
            }
        }
    }
    #if swift(>=5.7)
    /// Sets an async closure returning a `Request.ResponseDisposition`, called whenever the `DataRequest` produces an
    /// `HTTPURLResponse`.
    ///
    /// - Note: Most requests will only produce a single response for each outgoing attempt (initial + retries).
    ///         However, some types of response may trigger multiple `HTTPURLResponse`s, such as multipart streams,
    ///         where responses after the first will contain the part headers.
    ///
    /// - Parameters:
    ///   - handler: Async closure executed when a new `HTTPURLResponse` is received and returning a
    ///              `ResponseDisposition` value. This value determines whether to continue the request or cancel it as
    ///              if `cancel()` had been called on the instance. Note, this closure is called on an arbitrary thread,
    ///              so any synchronous calls in it will execute in that context.
    ///
    /// - Returns:   The instance.
    @_disfavoredOverload
    @discardableResult
    public func onHTTPResponse(
        perform handler: @escaping @Sendable (_ response: HTTPURLResponse) async -> ResponseDisposition
    ) -> Self {
        onHTTPResponse(on: underlyingQueue) { response, completionHandler in
            Task {
                let disposition = await handler(response)
                completionHandler(disposition)
            }
        }
        return self
    }
    /// Sets an async closure called whenever the `DataRequest` produces an `HTTPURLResponse`.
    ///
    /// - Note: Most requests will only produce a single response for each outgoing attempt (initial + retries).
    ///         However, some types of response may trigger multiple `HTTPURLResponse`s, such as multipart streams,
    ///         where responses after the first will contain the part headers.
    ///
    /// - Parameters:
    ///   - handler: Async closure executed when a new `HTTPURLResponse` is received. Note, this closure is called on an
    ///              arbitrary thread, so any synchronous calls in it will execute in that context.
    ///
    /// - Returns:   The instance.
    @discardableResult
    public func onHTTPResponse(perform handler: @escaping @Sendable (_ response: HTTPURLResponse) async -> Void) -> Self {
        onHTTPResponse { response in
            await handler(response)
            return .allow
        }
        return self
    }
    #endif
    /// Creates a `DataTask` to `await` a `Data` value.
    ///
    /// - Parameters:
    ///   - shouldAutomaticallyCancel: `Bool` determining whether or not the request should be cancelled when the
    ///                                enclosing async context is cancelled. Only applies to `DataTask`'s async
    ///                                properties. `true` by default.
    ///   - dataPreprocessor:          `DataPreprocessor` which processes the received `Data` before completion.
    ///   - emptyResponseCodes:        HTTP response codes for which empty responses are allowed. `[204, 205]` by default.
    ///   - emptyRequestMethods:       `HTTPMethod`s for which empty responses are always valid. `[.head]` by default.
    ///
    /// - Returns: The `DataTask`.
    public func serializingData(automaticallyCancelling shouldAutomaticallyCancel: Bool = true,
                                dataPreprocessor: DataPreprocessor = DataResponseSerializer.defaultDataPreprocessor,
                                emptyResponseCodes: Set<Int> = DataResponseSerializer.defaultEmptyResponseCodes,
                                emptyRequestMethods: Set<HTTPMethod> = DataResponseSerializer.defaultEmptyRequestMethods) -> DataTask<Data> {
        serializingResponse(using: DataResponseSerializer(dataPreprocessor: dataPreprocessor,
                                                          emptyResponseCodes: emptyResponseCodes,
                                                          emptyRequestMethods: emptyRequestMethods),
                            automaticallyCancelling: shouldAutomaticallyCancel)
    }
    /// Creates a `DataTask` to `await` serialization of a `Decodable` value.
    ///
    /// - Parameters:
    ///   - type:                      `Decodable` type to decode from response data.
    ///   - shouldAutomaticallyCancel: `Bool` determining whether or not the request should be cancelled when the
    ///                                enclosing async context is cancelled. Only applies to `DataTask`'s async
    ///                                properties. `true` by default.
    ///   - dataPreprocessor:          `DataPreprocessor` which processes the received `Data` before calling the serializer.
    ///                                `PassthroughPreprocessor()` by default.
    ///   - decoder:                   `DataDecoder` to use to decode the response. `JSONDecoder()` by default.
    ///   - emptyResponseCodes:        HTTP status codes for which empty responses are always valid. `[204, 205]` by default.
    ///   - emptyRequestMethods:       `HTTPMethod`s for which empty responses are always valid. `[.head]` by default.
    ///
    /// - Returns: The `DataTask`.
    public func serializingDecodable<Value: Decodable>(_ type: Value.Type = Value.self,
                                                       automaticallyCancelling shouldAutomaticallyCancel: Bool = true,
                                                       dataPreprocessor: DataPreprocessor = DecodableResponseSerializer<Value>.defaultDataPreprocessor,
                                                       decoder: DataDecoder = JSONDecoder(),
                                                       emptyResponseCodes: Set<Int> = DecodableResponseSerializer<Value>.defaultEmptyResponseCodes,
                                                       emptyRequestMethods: Set<HTTPMethod> = DecodableResponseSerializer<Value>.defaultEmptyRequestMethods) -> DataTask<Value> {
        serializingResponse(using: DecodableResponseSerializer<Value>(dataPreprocessor: dataPreprocessor,
                                                                      decoder: decoder,
                                                                      emptyResponseCodes: emptyResponseCodes,
                                                                      emptyRequestMethods: emptyRequestMethods),
                            automaticallyCancelling: shouldAutomaticallyCancel)
    }
    /// Creates a `DataTask` to `await` serialization of a `String` value.
    ///
    /// - Parameters:
    ///   - shouldAutomaticallyCancel: `Bool` determining whether or not the request should be cancelled when the
    ///                                enclosing async context is cancelled. Only applies to `DataTask`'s async
    ///                                properties. `true` by default.
    ///   - dataPreprocessor:          `DataPreprocessor` which processes the received `Data` before calling the serializer.
    ///                                `PassthroughPreprocessor()` by default.
    ///   - encoding:                  `String.Encoding` to use during serialization. Defaults to `nil`, in which case
    ///                                the encoding will be determined from the server response, falling back to the
    ///                                default HTTP character set, `ISO-8859-1`.
    ///   - emptyResponseCodes:        HTTP status codes for which empty responses are always valid. `[204, 205]` by default.
    ///   - emptyRequestMethods:       `HTTPMethod`s for which empty responses are always valid. `[.head]` by default.
    ///
    /// - Returns: The `DataTask`.
    public func serializingString(automaticallyCancelling shouldAutomaticallyCancel: Bool = true,
                                  dataPreprocessor: DataPreprocessor = StringResponseSerializer.defaultDataPreprocessor,
                                  encoding: String.Encoding? = nil,
                                  emptyResponseCodes: Set<Int> = StringResponseSerializer.defaultEmptyResponseCodes,
                                  emptyRequestMethods: Set<HTTPMethod> = StringResponseSerializer.defaultEmptyRequestMethods) -> DataTask<String> {
        serializingResponse(using: StringResponseSerializer(dataPreprocessor: dataPreprocessor,
                                                            encoding: encoding,
                                                            emptyResponseCodes: emptyResponseCodes,
                                                            emptyRequestMethods: emptyRequestMethods),
                            automaticallyCancelling: shouldAutomaticallyCancel)
    }
    /// Creates a `DataTask` to `await` serialization using the provided `ResponseSerializer` instance.
    ///
    /// - Parameters:
    ///   - serializer:                `ResponseSerializer` responsible for serializing the request, response, and data.
    ///   - shouldAutomaticallyCancel: `Bool` determining whether or not the request should be cancelled when the
    ///                                enclosing async context is cancelled. Only applies to `DataTask`'s async
    ///                                properties. `true` by default.
    ///
    /// - Returns: The `DataTask`.
    public func serializingResponse<Serializer: ResponseSerializer>(using serializer: Serializer,
                                                                    automaticallyCancelling shouldAutomaticallyCancel: Bool = true)
        -> DataTask<Serializer.SerializedObject> {
        dataTask(automaticallyCancelling: shouldAutomaticallyCancel) { [self] in
            response(queue: underlyingQueue,
                     responseSerializer: serializer,
                     completionHandler: $0)
        }
    }
    /// Creates a `DataTask` to `await` serialization using the provided `DataResponseSerializerProtocol` instance.
    ///
    /// - Parameters:
    ///   - serializer:                `DataResponseSerializerProtocol` responsible for serializing the request,
    ///                                response, and data.
    ///   - shouldAutomaticallyCancel: `Bool` determining whether or not the request should be cancelled when the
    ///                                enclosing async context is cancelled. Only applies to `DataTask`'s async
    ///                                properties. `true` by default.
    ///
    /// - Returns: The `DataTask`.
    public func serializingResponse<Serializer: DataResponseSerializerProtocol>(using serializer: Serializer,
                                                                                automaticallyCancelling shouldAutomaticallyCancel: Bool = true)
        -> DataTask<Serializer.SerializedObject> {
        dataTask(automaticallyCancelling: shouldAutomaticallyCancel) { [self] in
            response(queue: underlyingQueue,
                     responseSerializer: serializer,
                     completionHandler: $0)
        }
    }
    private func dataTask<Value>(automaticallyCancelling shouldAutomaticallyCancel: Bool,
                                 forResponse onResponse: @escaping (@escaping (DataResponse<Value, AFError>) -> Void) -> Void)
        -> DataTask<Value> {
        let task = Task {
            await withTaskCancellationHandler {
                await withCheckedContinuation { continuation in
                    onResponse {
                        continuation.resume(returning: $0)
                    }
                }
            } onCancel: {
                self.cancel()
            }
        }
        return DataTask<Value>(request: self, task: task, shouldAutomaticallyCancel: shouldAutomaticallyCancel)
    }
}
// MARK: - DownloadTask
/// Value used to `await` a `DownloadResponse` and associated values.
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
public struct DownloadTask<Value> {
    /// `DownloadResponse` produced by the `DownloadRequest` and its response handler.
    public var response: DownloadResponse<Value, AFError> {
        get async {
            if shouldAutomaticallyCancel {
                return await withTaskCancellationHandler {
                    await task.value
                } onCancel: {
                    cancel()
                }
            } else {
                return await task.value
            }
        }
    }
    /// `Result` of any response serialization performed for the `response`.
    public var result: Result<Value, AFError> {
        get async { await response.result }
    }
    /// `Value` returned by the `response`.
    public var value: Value {
        get async throws {
            try await result.get()
        }
    }
    private let task: Task<AFDownloadResponse<Value>, Never>
    private let request: DownloadRequest
    private let shouldAutomaticallyCancel: Bool
    fileprivate init(request: DownloadRequest, task: Task<AFDownloadResponse<Value>, Never>, shouldAutomaticallyCancel: Bool) {
        self.request = request
        self.task = task
        self.shouldAutomaticallyCancel = shouldAutomaticallyCancel
    }
    /// Cancel the underlying `DownloadRequest` and `Task`.
    public func cancel() {
        task.cancel()
    }
    /// Resume the underlying `DownloadRequest`.
    public func resume() {
        request.resume()
    }
    /// Suspend the underlying `DownloadRequest`.
    public func suspend() {
        request.suspend()
    }
}
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
extension DownloadRequest {
    /// Creates a `DownloadTask` to `await` a `Data` value.
    ///
    /// - Parameters:
    ///   - shouldAutomaticallyCancel: `Bool` determining whether or not the request should be cancelled when the
    ///                                enclosing async context is cancelled. Only applies to `DownloadTask`'s async
    ///                                properties. `true` by default.
    ///   - dataPreprocessor:          `DataPreprocessor` which processes the received `Data` before completion.
    ///   - emptyResponseCodes:        HTTP response codes for which empty responses are allowed. `[204, 205]` by default.
    ///   - emptyRequestMethods:       `HTTPMethod`s for which empty responses are always valid. `[.head]` by default.
    ///
    /// - Returns:                   The `DownloadTask`.
    public func serializingData(automaticallyCancelling shouldAutomaticallyCancel: Bool = true,
                                dataPreprocessor: DataPreprocessor = DataResponseSerializer.defaultDataPreprocessor,
                                emptyResponseCodes: Set<Int> = DataResponseSerializer.defaultEmptyResponseCodes,
                                emptyRequestMethods: Set<HTTPMethod> = DataResponseSerializer.defaultEmptyRequestMethods) -> DownloadTask<Data> {
        serializingDownload(using: DataResponseSerializer(dataPreprocessor: dataPreprocessor,
                                                          emptyResponseCodes: emptyResponseCodes,
                                                          emptyRequestMethods: emptyRequestMethods),
                            automaticallyCancelling: shouldAutomaticallyCancel)
    }
    /// Creates a `DownloadTask` to `await` serialization of a `Decodable` value.
    ///
    /// - Note: This serializer reads the entire response into memory before parsing.
    ///
    /// - Parameters:
    ///   - type:                      `Decodable` type to decode from response data.
    ///   - shouldAutomaticallyCancel: `Bool` determining whether or not the request should be cancelled when the
    ///                                enclosing async context is cancelled. Only applies to `DownloadTask`'s async
    ///                                properties. `true` by default.
    ///   - dataPreprocessor:          `DataPreprocessor` which processes the received `Data` before calling the serializer.
    ///                                `PassthroughPreprocessor()` by default.
    ///   - decoder:                   `DataDecoder` to use to decode the response. `JSONDecoder()` by default.
    ///   - emptyResponseCodes:        HTTP status codes for which empty responses are always valid. `[204, 205]` by default.
    ///   - emptyRequestMethods:       `HTTPMethod`s for which empty responses are always valid. `[.head]` by default.
    ///
    /// - Returns:                   The `DownloadTask`.
    public func serializingDecodable<Value: Decodable>(_ type: Value.Type = Value.self,
                                                       automaticallyCancelling shouldAutomaticallyCancel: Bool = true,
                                                       dataPreprocessor: DataPreprocessor = DecodableResponseSerializer<Value>.defaultDataPreprocessor,
                                                       decoder: DataDecoder = JSONDecoder(),
                                                       emptyResponseCodes: Set<Int> = DecodableResponseSerializer<Value>.defaultEmptyResponseCodes,
                                                       emptyRequestMethods: Set<HTTPMethod> = DecodableResponseSerializer<Value>.defaultEmptyRequestMethods) -> DownloadTask<Value> {
        serializingDownload(using: DecodableResponseSerializer<Value>(dataPreprocessor: dataPreprocessor,
                                                                      decoder: decoder,
                                                                      emptyResponseCodes: emptyResponseCodes,
                                                                      emptyRequestMethods: emptyRequestMethods),
                            automaticallyCancelling: shouldAutomaticallyCancel)
    }
    /// Creates a `DownloadTask` to `await` serialization of the downloaded file's `URL` on disk.
    ///
    /// - Parameters:
    ///   - shouldAutomaticallyCancel: `Bool` determining whether or not the request should be cancelled when the
    ///                                enclosing async context is cancelled. Only applies to `DownloadTask`'s async
    ///                                properties. `true` by default.
    ///
    /// - Returns: The `DownloadTask`.
    public func serializingDownloadedFileURL(automaticallyCancelling shouldAutomaticallyCancel: Bool = true) -> DownloadTask<URL> {
        serializingDownload(using: URLResponseSerializer(),
                            automaticallyCancelling: shouldAutomaticallyCancel)
    }
    /// Creates a `DownloadTask` to `await` serialization of a `String` value.
    ///
    /// - Parameters:
    ///   - shouldAutomaticallyCancel: `Bool` determining whether or not the request should be cancelled when the
    ///                                enclosing async context is cancelled. Only applies to `DownloadTask`'s async
    ///                                properties. `true` by default.
    ///   - dataPreprocessor:          `DataPreprocessor` which processes the received `Data` before calling the
    ///                                serializer. `PassthroughPreprocessor()` by default.
    ///   - encoding:                  `String.Encoding` to use during serialization. Defaults to `nil`, in which case
    ///                                the encoding will be determined from the server response, falling back to the
    ///                                default HTTP character set, `ISO-8859-1`.
    ///   - emptyResponseCodes:        HTTP status codes for which empty responses are always valid. `[204, 205]` by default.
    ///   - emptyRequestMethods:       `HTTPMethod`s for which empty responses are always valid. `[.head]` by default.
    ///
    /// - Returns:                   The `DownloadTask`.
    public func serializingString(automaticallyCancelling shouldAutomaticallyCancel: Bool = true,
                                  dataPreprocessor: DataPreprocessor = StringResponseSerializer.defaultDataPreprocessor,
                                  encoding: String.Encoding? = nil,
                                  emptyResponseCodes: Set<Int> = StringResponseSerializer.defaultEmptyResponseCodes,
                                  emptyRequestMethods: Set<HTTPMethod> = StringResponseSerializer.defaultEmptyRequestMethods) -> DownloadTask<String> {
        serializingDownload(using: StringResponseSerializer(dataPreprocessor: dataPreprocessor,
                                                            encoding: encoding,
                                                            emptyResponseCodes: emptyResponseCodes,
                                                            emptyRequestMethods: emptyRequestMethods),
                            automaticallyCancelling: shouldAutomaticallyCancel)
    }
    /// Creates a `DownloadTask` to `await` serialization using the provided `ResponseSerializer` instance.
    ///
    /// - Parameters:
    ///   - serializer:                `ResponseSerializer` responsible for serializing the request, response, and data.
    ///   - shouldAutomaticallyCancel: `Bool` determining whether or not the request should be cancelled when the
    ///                                enclosing async context is cancelled. Only applies to `DownloadTask`'s async
    ///                                properties. `true` by default.
    ///
    /// - Returns: The `DownloadTask`.
    public func serializingDownload<Serializer: ResponseSerializer>(using serializer: Serializer,
                                                                    automaticallyCancelling shouldAutomaticallyCancel: Bool = true)
        -> DownloadTask<Serializer.SerializedObject> {
        downloadTask(automaticallyCancelling: shouldAutomaticallyCancel) { [self] in
            response(queue: underlyingQueue,
                     responseSerializer: serializer,
                     completionHandler: $0)
        }
    }
    /// Creates a `DownloadTask` to `await` serialization using the provided `DownloadResponseSerializerProtocol`
    /// instance.
    ///
    /// - Parameters:
    ///   - serializer:                `DownloadResponseSerializerProtocol` responsible for serializing the request,
    ///                                response, and data.
    ///   - shouldAutomaticallyCancel: `Bool` determining whether or not the request should be cancelled when the
    ///                                enclosing async context is cancelled. Only applies to `DownloadTask`'s async
    ///                                properties. `true` by default.
    ///
    /// - Returns: The `DownloadTask`.
    public func serializingDownload<Serializer: DownloadResponseSerializerProtocol>(using serializer: Serializer,
                                                                                    automaticallyCancelling shouldAutomaticallyCancel: Bool = true)
        -> DownloadTask<Serializer.SerializedObject> {
        downloadTask(automaticallyCancelling: shouldAutomaticallyCancel) { [self] in
            response(queue: underlyingQueue,
                     responseSerializer: serializer,
                     completionHandler: $0)
        }
    }
    private func downloadTask<Value>(automaticallyCancelling shouldAutomaticallyCancel: Bool,
                                     forResponse onResponse: @escaping (@escaping (DownloadResponse<Value, AFError>) -> Void) -> Void)
        -> DownloadTask<Value> {
        let task = Task {
            await withTaskCancellationHandler {
                await withCheckedContinuation { continuation in
                    onResponse {
                        continuation.resume(returning: $0)
                    }
                }
            } onCancel: {
                self.cancel()
            }
        }
        return DownloadTask<Value>(request: self, task: task, shouldAutomaticallyCancel: shouldAutomaticallyCancel)
    }
}
// MARK: - DataStreamTask
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
public struct DataStreamTask {
    // Type of created streams.
    public typealias Stream<Success, Failure: Error> = StreamOf<DataStreamRequest.Stream<Success, Failure>>
    private let request: DataStreamRequest
    fileprivate init(request: DataStreamRequest) {
        self.request = request
    }
    /// Creates a `Stream` of `Data` values from the underlying `DataStreamRequest`.
    ///
    /// - Parameters:
    ///   - shouldAutomaticallyCancel: `Bool` indicating whether the underlying `DataStreamRequest` should be canceled
    ///                                which observation of the stream stops. `true` by default.
    ///   - bufferingPolicy: `         BufferingPolicy` that determines the stream's buffering behavior.`.unbounded` by default.
    ///
    /// - Returns:                   The `Stream`.
    public func streamingData(automaticallyCancelling shouldAutomaticallyCancel: Bool = true, bufferingPolicy: Stream<Data, Never>.BufferingPolicy = .unbounded) -> Stream<Data, Never> {
        createStream(automaticallyCancelling: shouldAutomaticallyCancel, bufferingPolicy: bufferingPolicy) { onStream in
            request.responseStream(on: .streamCompletionQueue(forRequestID: request.id), stream: onStream)
        }
    }
    /// Creates a `Stream` of `UTF-8` `String`s from the underlying `DataStreamRequest`.
    ///
    /// - Parameters:
    ///   - shouldAutomaticallyCancel: `Bool` indicating whether the underlying `DataStreamRequest` should be canceled
    ///                                which observation of the stream stops. `true` by default.
    ///   - bufferingPolicy: `         BufferingPolicy` that determines the stream's buffering behavior.`.unbounded` by default.
    /// - Returns:
    public func streamingStrings(automaticallyCancelling shouldAutomaticallyCancel: Bool = true, bufferingPolicy: Stream<String, Never>.BufferingPolicy = .unbounded) -> Stream<String, Never> {
        createStream(automaticallyCancelling: shouldAutomaticallyCancel, bufferingPolicy: bufferingPolicy) { onStream in
            request.responseStreamString(on: .streamCompletionQueue(forRequestID: request.id), stream: onStream)
        }
    }
    /// Creates a `Stream` of `Decodable` values from the underlying `DataStreamRequest`.
    ///
    /// - Parameters:
    ///   - type:                      `Decodable` type to be serialized from stream payloads.
    ///   - shouldAutomaticallyCancel: `Bool` indicating whether the underlying `DataStreamRequest` should be canceled
    ///                                which observation of the stream stops. `true` by default.
    ///   - bufferingPolicy:           `BufferingPolicy` that determines the stream's buffering behavior.`.unbounded` by default.
    ///
    /// - Returns:            The `Stream`.
    public func streamingDecodables<T>(_ type: T.Type = T.self,
                                       automaticallyCancelling shouldAutomaticallyCancel: Bool = true,
                                       bufferingPolicy: Stream<T, AFError>.BufferingPolicy = .unbounded)
        -> Stream<T, AFError> where T: Decodable {
        streamingResponses(serializedUsing: DecodableStreamSerializer<T>(),
                           automaticallyCancelling: shouldAutomaticallyCancel,
                           bufferingPolicy: bufferingPolicy)
    }
    /// Creates a `Stream` of values using the provided `DataStreamSerializer` from the underlying `DataStreamRequest`.
    ///
    /// - Parameters:
    ///   - serializer:                `DataStreamSerializer` to use to serialize incoming `Data`.
    ///   - shouldAutomaticallyCancel: `Bool` indicating whether the underlying `DataStreamRequest` should be canceled
    ///                                which observation of the stream stops. `true` by default.
    ///   - bufferingPolicy:           `BufferingPolicy` that determines the stream's buffering behavior.`.unbounded` by default.
    ///
    /// - Returns:           The `Stream`.
    public func streamingResponses<Serializer: DataStreamSerializer>(serializedUsing serializer: Serializer,
                                                                     automaticallyCancelling shouldAutomaticallyCancel: Bool = true,
                                                                     bufferingPolicy: Stream<Serializer.SerializedObject, AFError>.BufferingPolicy = .unbounded)
        -> Stream<Serializer.SerializedObject, AFError> {
        createStream(automaticallyCancelling: shouldAutomaticallyCancel, bufferingPolicy: bufferingPolicy) { onStream in
            request.responseStream(using: serializer,
                                   on: .streamCompletionQueue(forRequestID: request.id),
                                   stream: onStream)
        }
    }
    private func createStream<Success, Failure: Error>(automaticallyCancelling shouldAutomaticallyCancel: Bool = true,
                                                       bufferingPolicy: Stream<Success, Failure>.BufferingPolicy = .unbounded,
                                                       forResponse onResponse: @escaping (@escaping (DataStreamRequest.Stream<Success, Failure>) -> Void) -> Void)
        -> Stream<Success, Failure> {
        StreamOf(bufferingPolicy: bufferingPolicy) {
            guard shouldAutomaticallyCancel,
                  request.isInitialized || request.isResumed || request.isSuspended else { return }
            cancel()
        } builder: { continuation in
            onResponse { stream in
                continuation.yield(stream)
                if case .complete = stream.event {
                    continuation.finish()
                }
            }
        }
    }
    /// Cancel the underlying `DataStreamRequest`.
    public func cancel() {
        request.cancel()
    }
    /// Resume the underlying `DataStreamRequest`.
    public func resume() {
        request.resume()
    }
    /// Suspend the underlying `DataStreamRequest`.
    public func suspend() {
        request.suspend()
    }
}
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
extension DataStreamRequest {
    /// Creates a `StreamOf<HTTPURLResponse>` for the instance's responses.
    ///
    /// - Parameter bufferingPolicy: `BufferingPolicy` that determines the stream's buffering behavior.`.unbounded` by default.
    ///
    /// - Returns:                   The `StreamOf<HTTPURLResponse>`.
    public func httpResponses(bufferingPolicy: StreamOf<HTTPURLResponse>.BufferingPolicy = .unbounded) -> StreamOf<HTTPURLResponse> {
        stream(bufferingPolicy: bufferingPolicy) { [unowned self] continuation in
            onHTTPResponse(on: underlyingQueue) { response in
                continuation.yield(response)
            }
        }
    }
    #if swift(>=5.7)
    /// Sets an async closure returning a `Request.ResponseDisposition`, called whenever the `DataStreamRequest`
    /// produces an `HTTPURLResponse`.
    ///
    /// - Note: Most requests will only produce a single response for each outgoing attempt (initial + retries).
    ///         However, some types of response may trigger multiple `HTTPURLResponse`s, such as multipart streams,
    ///         where responses after the first will contain the part headers.
    ///
    /// - Parameters:
    ///   - handler: Async closure executed when a new `HTTPURLResponse` is received and returning a
    ///              `ResponseDisposition` value. This value determines whether to continue the request or cancel it as
    ///              if `cancel()` had been called on the instance. Note, this closure is called on an arbitrary thread,
    ///              so any synchronous calls in it will execute in that context.
    ///
    /// - Returns:   The instance.
    @_disfavoredOverload
    @discardableResult
    public func onHTTPResponse(perform handler: @escaping @Sendable (HTTPURLResponse) async -> ResponseDisposition) -> Self {
        onHTTPResponse(on: underlyingQueue) { response, completionHandler in
            Task {
                let disposition = await handler(response)
                completionHandler(disposition)
            }
        }
        return self
    }
    /// Sets an async closure called whenever the `DataStreamRequest` produces an `HTTPURLResponse`.
    ///
    /// - Note: Most requests will only produce a single response for each outgoing attempt (initial + retries).
    ///         However, some types of response may trigger multiple `HTTPURLResponse`s, such as multipart streams,
    ///         where responses after the first will contain the part headers.
    ///
    /// - Parameters:
    ///   - handler: Async closure executed when a new `HTTPURLResponse` is received. Note, this closure is called on an
    ///              arbitrary thread, so any synchronous calls in it will execute in that context.
    ///
    /// - Returns:   The instance.
    @discardableResult
    public func onHTTPResponse(perform handler: @escaping @Sendable (HTTPURLResponse) async -> Void) -> Self {
        onHTTPResponse { response in
            await handler(response)
            return .allow
        }
        return self
    }
    #endif
    /// Creates a `DataStreamTask` used to `await` streams of serialized values.
    ///
    /// - Returns: The `DataStreamTask`.
    public func streamTask() -> DataStreamTask {
        DataStreamTask(request: self)
    }
}
extension DispatchQueue {
    fileprivate static let singleEventQueue = DispatchQueue(label: "org.alamofire.concurrencySingleEventQueue",
                                                            attributes: .concurrent)
    fileprivate static func streamCompletionQueue(forRequestID id: UUID) -> DispatchQueue {
        DispatchQueue(label: "org.alamofire.concurrencyStreamCompletionQueue-\(id)", target: .singleEventQueue)
    }
}
/// An asynchronous sequence generated from an underlying `AsyncStream`. Only produced by Alamofire.
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
public struct StreamOf<Element>: AsyncSequence {
    public typealias AsyncIterator = Iterator
    public typealias BufferingPolicy = AsyncStream<Element>.Continuation.BufferingPolicy
    fileprivate typealias Continuation = AsyncStream<Element>.Continuation
    private let bufferingPolicy: BufferingPolicy
    private let onTermination: (() -> Void)?
    private let builder: (Continuation) -> Void
    fileprivate init(bufferingPolicy: BufferingPolicy = .unbounded,
                     onTermination: (() -> Void)? = nil,
                     builder: @escaping (Continuation) -> Void) {
        self.bufferingPolicy = bufferingPolicy
        self.onTermination = onTermination
        self.builder = builder
    }
    public func makeAsyncIterator() -> Iterator {
        var continuation: AsyncStream<Element>.Continuation?
        let stream = AsyncStream<Element>(bufferingPolicy: bufferingPolicy) { innerContinuation in
            continuation = innerContinuation
            builder(innerContinuation)
        }
        return Iterator(iterator: stream.makeAsyncIterator()) {
            continuation?.finish()
            onTermination?()
        }
    }
    public struct Iterator: AsyncIteratorProtocol {
        private final class Token {
            private let onDeinit: () -> Void
            init(onDeinit: @escaping () -> Void) {
                self.onDeinit = onDeinit
            }
            deinit {
                onDeinit()
            }
        }
        private var iterator: AsyncStream<Element>.AsyncIterator
        private let token: Token
        init(iterator: AsyncStream<Element>.AsyncIterator, onCancellation: @escaping () -> Void) {
            self.iterator = iterator
            token = Token(onDeinit: onCancellation)
        }
        public mutating func next() async -> Element? {
            await iterator.next()
        }
    }
}
#endif
Pods/Alamofire/Source/DispatchQueue+Alamofire.swift
New file
@@ -0,0 +1,37 @@
//
//  DispatchQueue+Alamofire.swift
//
//  Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/)
//
//  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:
//
//  The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
//
//  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.
//
import Dispatch
import Foundation
extension DispatchQueue {
    /// Execute the provided closure after a `TimeInterval`.
    ///
    /// - Parameters:
    ///   - delay:   `TimeInterval` to delay execution.
    ///   - closure: Closure to execute.
    func after(_ delay: TimeInterval, execute closure: @escaping () -> Void) {
        asyncAfter(deadline: .now() + delay, execute: closure)
    }
}
Pods/Alamofire/Source/EventMonitor.swift
New file
@@ -0,0 +1,907 @@
//
//  EventMonitor.swift
//
//  Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/)
//
//  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:
//
//  The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
//
//  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.
//
import Foundation
/// Protocol outlining the lifetime events inside Alamofire. It includes both events received from the various
/// `URLSession` delegate protocols as well as various events from the lifetime of `Request` and its subclasses.
public protocol EventMonitor {
    /// The `DispatchQueue` onto which Alamofire's root `CompositeEventMonitor` will dispatch events. `.main` by default.
    var queue: DispatchQueue { get }
    // MARK: - URLSession Events
    // MARK: URLSessionDelegate Events
    /// Event called during `URLSessionDelegate`'s `urlSession(_:didBecomeInvalidWithError:)` method.
    func urlSession(_ session: URLSession, didBecomeInvalidWithError error: Error?)
    // MARK: URLSessionTaskDelegate Events
    /// Event called during `URLSessionTaskDelegate`'s `urlSession(_:task:didReceive:completionHandler:)` method.
    func urlSession(_ session: URLSession, task: URLSessionTask, didReceive challenge: URLAuthenticationChallenge)
    /// Event called during `URLSessionTaskDelegate`'s `urlSession(_:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:)` method.
    func urlSession(_ session: URLSession,
                    task: URLSessionTask,
                    didSendBodyData bytesSent: Int64,
                    totalBytesSent: Int64,
                    totalBytesExpectedToSend: Int64)
    /// Event called during `URLSessionTaskDelegate`'s `urlSession(_:task:needNewBodyStream:)` method.
    func urlSession(_ session: URLSession, taskNeedsNewBodyStream task: URLSessionTask)
    /// Event called during `URLSessionTaskDelegate`'s `urlSession(_:task:willPerformHTTPRedirection:newRequest:completionHandler:)` method.
    func urlSession(_ session: URLSession,
                    task: URLSessionTask,
                    willPerformHTTPRedirection response: HTTPURLResponse,
                    newRequest request: URLRequest)
    /// Event called during `URLSessionTaskDelegate`'s `urlSession(_:task:didFinishCollecting:)` method.
    func urlSession(_ session: URLSession, task: URLSessionTask, didFinishCollecting metrics: URLSessionTaskMetrics)
    /// Event called during `URLSessionTaskDelegate`'s `urlSession(_:task:didCompleteWithError:)` method.
    func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?)
    /// Event called during `URLSessionTaskDelegate`'s `urlSession(_:taskIsWaitingForConnectivity:)` method.
    func urlSession(_ session: URLSession, taskIsWaitingForConnectivity task: URLSessionTask)
    // MARK: URLSessionDataDelegate Events
    /// Event called during `URLSessionDataDelegate`'s `urlSession(_:dataTask:didReceive:completionHandler:)` method.
    func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse)
    /// Event called during `URLSessionDataDelegate`'s `urlSession(_:dataTask:didReceive:)` method.
    func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data)
    /// Event called during `URLSessionDataDelegate`'s `urlSession(_:dataTask:willCacheResponse:completionHandler:)` method.
    func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, willCacheResponse proposedResponse: CachedURLResponse)
    // MARK: URLSessionDownloadDelegate Events
    /// Event called during `URLSessionDownloadDelegate`'s `urlSession(_:downloadTask:didResumeAtOffset:expectedTotalBytes:)` method.
    func urlSession(_ session: URLSession,
                    downloadTask: URLSessionDownloadTask,
                    didResumeAtOffset fileOffset: Int64,
                    expectedTotalBytes: Int64)
    /// Event called during `URLSessionDownloadDelegate`'s `urlSession(_:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:)` method.
    func urlSession(_ session: URLSession,
                    downloadTask: URLSessionDownloadTask,
                    didWriteData bytesWritten: Int64,
                    totalBytesWritten: Int64,
                    totalBytesExpectedToWrite: Int64)
    /// Event called during `URLSessionDownloadDelegate`'s `urlSession(_:downloadTask:didFinishDownloadingTo:)` method.
    func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL)
    // MARK: - Request Events
    /// Event called when a `URLRequest` is first created for a `Request`. If a `RequestAdapter` is active, the
    /// `URLRequest` will be adapted before being issued.
    func request(_ request: Request, didCreateInitialURLRequest urlRequest: URLRequest)
    /// Event called when the attempt to create a `URLRequest` from a `Request`'s original `URLRequestConvertible` value fails.
    func request(_ request: Request, didFailToCreateURLRequestWithError error: AFError)
    /// Event called when a `RequestAdapter` adapts the `Request`'s initial `URLRequest`.
    func request(_ request: Request, didAdaptInitialRequest initialRequest: URLRequest, to adaptedRequest: URLRequest)
    /// Event called when a `RequestAdapter` fails to adapt the `Request`'s initial `URLRequest`.
    func request(_ request: Request, didFailToAdaptURLRequest initialRequest: URLRequest, withError error: AFError)
    /// Event called when a final `URLRequest` is created for a `Request`.
    func request(_ request: Request, didCreateURLRequest urlRequest: URLRequest)
    /// Event called when a `URLSessionTask` subclass instance is created for a `Request`.
    func request(_ request: Request, didCreateTask task: URLSessionTask)
    /// Event called when a `Request` receives a `URLSessionTaskMetrics` value.
    func request(_ request: Request, didGatherMetrics metrics: URLSessionTaskMetrics)
    /// Event called when a `Request` fails due to an error created by Alamofire. e.g. When certificate pinning fails.
    func request(_ request: Request, didFailTask task: URLSessionTask, earlyWithError error: AFError)
    /// Event called when a `Request`'s task completes, possibly with an error. A `Request` may receive this event
    /// multiple times if it is retried.
    func request(_ request: Request, didCompleteTask task: URLSessionTask, with error: AFError?)
    /// Event called when a `Request` is about to be retried.
    func requestIsRetrying(_ request: Request)
    /// Event called when a `Request` finishes and response serializers are being called.
    func requestDidFinish(_ request: Request)
    /// Event called when a `Request` receives a `resume` call.
    func requestDidResume(_ request: Request)
    /// Event called when a `Request`'s associated `URLSessionTask` is resumed.
    func request(_ request: Request, didResumeTask task: URLSessionTask)
    /// Event called when a `Request` receives a `suspend` call.
    func requestDidSuspend(_ request: Request)
    /// Event called when a `Request`'s associated `URLSessionTask` is suspended.
    func request(_ request: Request, didSuspendTask task: URLSessionTask)
    /// Event called when a `Request` receives a `cancel` call.
    func requestDidCancel(_ request: Request)
    /// Event called when a `Request`'s associated `URLSessionTask` is cancelled.
    func request(_ request: Request, didCancelTask task: URLSessionTask)
    // MARK: DataRequest Events
    /// Event called when a `DataRequest` calls a `Validation`.
    func request(_ request: DataRequest,
                 didValidateRequest urlRequest: URLRequest?,
                 response: HTTPURLResponse,
                 data: Data?,
                 withResult result: Request.ValidationResult)
    /// Event called when a `DataRequest` creates a `DataResponse<Data?>` value without calling a `ResponseSerializer`.
    func request(_ request: DataRequest, didParseResponse response: DataResponse<Data?, AFError>)
    /// Event called when a `DataRequest` calls a `ResponseSerializer` and creates a generic `DataResponse<Value, AFError>`.
    func request<Value>(_ request: DataRequest, didParseResponse response: DataResponse<Value, AFError>)
    // MARK: DataStreamRequest Events
    /// Event called when a `DataStreamRequest` calls a `Validation` closure.
    ///
    /// - Parameters:
    ///   - request:    `DataStreamRequest` which is calling the `Validation`.
    ///   - urlRequest: `URLRequest` of the request being validated.
    ///   - response:   `HTTPURLResponse` of the request being validated.
    ///   - result:      Produced `ValidationResult`.
    func request(_ request: DataStreamRequest,
                 didValidateRequest urlRequest: URLRequest?,
                 response: HTTPURLResponse,
                 withResult result: Request.ValidationResult)
    /// Event called when a `DataStreamSerializer` produces a value from streamed `Data`.
    ///
    /// - Parameters:
    ///   - request: `DataStreamRequest` for which the value was serialized.
    ///   - result:  `Result` of the serialization attempt.
    func request<Value>(_ request: DataStreamRequest, didParseStream result: Result<Value, AFError>)
    // MARK: UploadRequest Events
    /// Event called when an `UploadRequest` creates its `Uploadable` value, indicating the type of upload it represents.
    func request(_ request: UploadRequest, didCreateUploadable uploadable: UploadRequest.Uploadable)
    /// Event called when an `UploadRequest` failed to create its `Uploadable` value due to an error.
    func request(_ request: UploadRequest, didFailToCreateUploadableWithError error: AFError)
    /// Event called when an `UploadRequest` provides the `InputStream` from its `Uploadable` value. This only occurs if
    /// the `InputStream` does not wrap a `Data` value or file `URL`.
    func request(_ request: UploadRequest, didProvideInputStream stream: InputStream)
    // MARK: DownloadRequest Events
    /// Event called when a `DownloadRequest`'s `URLSessionDownloadTask` finishes and the temporary file has been moved.
    func request(_ request: DownloadRequest, didFinishDownloadingUsing task: URLSessionTask, with result: Result<URL, AFError>)
    /// Event called when a `DownloadRequest`'s `Destination` closure is called and creates the destination URL the
    /// downloaded file will be moved to.
    func request(_ request: DownloadRequest, didCreateDestinationURL url: URL)
    /// Event called when a `DownloadRequest` calls a `Validation`.
    func request(_ request: DownloadRequest,
                 didValidateRequest urlRequest: URLRequest?,
                 response: HTTPURLResponse,
                 fileURL: URL?,
                 withResult result: Request.ValidationResult)
    /// Event called when a `DownloadRequest` creates a `DownloadResponse<URL?, AFError>` without calling a `ResponseSerializer`.
    func request(_ request: DownloadRequest, didParseResponse response: DownloadResponse<URL?, AFError>)
    /// Event called when a `DownloadRequest` calls a `DownloadResponseSerializer` and creates a generic `DownloadResponse<Value, AFError>`
    func request<Value>(_ request: DownloadRequest, didParseResponse response: DownloadResponse<Value, AFError>)
}
extension EventMonitor {
    /// The default queue on which `CompositeEventMonitor`s will call the `EventMonitor` methods. `.main` by default.
    public var queue: DispatchQueue { .main }
    // MARK: Default Implementations
    public func urlSession(_ session: URLSession, didBecomeInvalidWithError error: Error?) {}
    public func urlSession(_ session: URLSession,
                           task: URLSessionTask,
                           didReceive challenge: URLAuthenticationChallenge) {}
    public func urlSession(_ session: URLSession,
                           task: URLSessionTask,
                           didSendBodyData bytesSent: Int64,
                           totalBytesSent: Int64,
                           totalBytesExpectedToSend: Int64) {}
    public func urlSession(_ session: URLSession, taskNeedsNewBodyStream task: URLSessionTask) {}
    public func urlSession(_ session: URLSession,
                           task: URLSessionTask,
                           willPerformHTTPRedirection response: HTTPURLResponse,
                           newRequest request: URLRequest) {}
    public func urlSession(_ session: URLSession,
                           task: URLSessionTask,
                           didFinishCollecting metrics: URLSessionTaskMetrics) {}
    public func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {}
    public func urlSession(_ session: URLSession, taskIsWaitingForConnectivity task: URLSessionTask) {}
    public func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse) {}
    public func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {}
    public func urlSession(_ session: URLSession,
                           dataTask: URLSessionDataTask,
                           willCacheResponse proposedResponse: CachedURLResponse) {}
    public func urlSession(_ session: URLSession,
                           downloadTask: URLSessionDownloadTask,
                           didResumeAtOffset fileOffset: Int64,
                           expectedTotalBytes: Int64) {}
    public func urlSession(_ session: URLSession,
                           downloadTask: URLSessionDownloadTask,
                           didWriteData bytesWritten: Int64,
                           totalBytesWritten: Int64,
                           totalBytesExpectedToWrite: Int64) {}
    public func urlSession(_ session: URLSession,
                           downloadTask: URLSessionDownloadTask,
                           didFinishDownloadingTo location: URL) {}
    public func request(_ request: Request, didCreateInitialURLRequest urlRequest: URLRequest) {}
    public func request(_ request: Request, didFailToCreateURLRequestWithError error: AFError) {}
    public func request(_ request: Request,
                        didAdaptInitialRequest initialRequest: URLRequest,
                        to adaptedRequest: URLRequest) {}
    public func request(_ request: Request,
                        didFailToAdaptURLRequest initialRequest: URLRequest,
                        withError error: AFError) {}
    public func request(_ request: Request, didCreateURLRequest urlRequest: URLRequest) {}
    public func request(_ request: Request, didCreateTask task: URLSessionTask) {}
    public func request(_ request: Request, didGatherMetrics metrics: URLSessionTaskMetrics) {}
    public func request(_ request: Request, didFailTask task: URLSessionTask, earlyWithError error: AFError) {}
    public func request(_ request: Request, didCompleteTask task: URLSessionTask, with error: AFError?) {}
    public func requestIsRetrying(_ request: Request) {}
    public func requestDidFinish(_ request: Request) {}
    public func requestDidResume(_ request: Request) {}
    public func request(_ request: Request, didResumeTask task: URLSessionTask) {}
    public func requestDidSuspend(_ request: Request) {}
    public func request(_ request: Request, didSuspendTask task: URLSessionTask) {}
    public func requestDidCancel(_ request: Request) {}
    public func request(_ request: Request, didCancelTask task: URLSessionTask) {}
    public func request(_ request: DataRequest,
                        didValidateRequest urlRequest: URLRequest?,
                        response: HTTPURLResponse,
                        data: Data?,
                        withResult result: Request.ValidationResult) {}
    public func request(_ request: DataRequest, didParseResponse response: DataResponse<Data?, AFError>) {}
    public func request<Value>(_ request: DataRequest, didParseResponse response: DataResponse<Value, AFError>) {}
    public func request(_ request: DataStreamRequest,
                        didValidateRequest urlRequest: URLRequest?,
                        response: HTTPURLResponse,
                        withResult result: Request.ValidationResult) {}
    public func request<Value>(_ request: DataStreamRequest, didParseStream result: Result<Value, AFError>) {}
    public func request(_ request: UploadRequest, didCreateUploadable uploadable: UploadRequest.Uploadable) {}
    public func request(_ request: UploadRequest, didFailToCreateUploadableWithError error: AFError) {}
    public func request(_ request: UploadRequest, didProvideInputStream stream: InputStream) {}
    public func request(_ request: DownloadRequest, didFinishDownloadingUsing task: URLSessionTask, with result: Result<URL, AFError>) {}
    public func request(_ request: DownloadRequest, didCreateDestinationURL url: URL) {}
    public func request(_ request: DownloadRequest,
                        didValidateRequest urlRequest: URLRequest?,
                        response: HTTPURLResponse,
                        fileURL: URL?,
                        withResult result: Request.ValidationResult) {}
    public func request(_ request: DownloadRequest, didParseResponse response: DownloadResponse<URL?, AFError>) {}
    public func request<Value>(_ request: DownloadRequest, didParseResponse response: DownloadResponse<Value, AFError>) {}
}
/// An `EventMonitor` which can contain multiple `EventMonitor`s and calls their methods on their queues.
public final class CompositeEventMonitor: EventMonitor {
    public let queue = DispatchQueue(label: "org.alamofire.compositeEventMonitor", qos: .utility)
    let monitors: [EventMonitor]
    init(monitors: [EventMonitor]) {
        self.monitors = monitors
    }
    func performEvent(_ event: @escaping (EventMonitor) -> Void) {
        queue.async {
            for monitor in self.monitors {
                monitor.queue.async { event(monitor) }
            }
        }
    }
    public func urlSession(_ session: URLSession, didBecomeInvalidWithError error: Error?) {
        performEvent { $0.urlSession(session, didBecomeInvalidWithError: error) }
    }
    public func urlSession(_ session: URLSession,
                           task: URLSessionTask,
                           didReceive challenge: URLAuthenticationChallenge) {
        performEvent { $0.urlSession(session, task: task, didReceive: challenge) }
    }
    public func urlSession(_ session: URLSession,
                           task: URLSessionTask,
                           didSendBodyData bytesSent: Int64,
                           totalBytesSent: Int64,
                           totalBytesExpectedToSend: Int64) {
        performEvent {
            $0.urlSession(session,
                          task: task,
                          didSendBodyData: bytesSent,
                          totalBytesSent: totalBytesSent,
                          totalBytesExpectedToSend: totalBytesExpectedToSend)
        }
    }
    public func urlSession(_ session: URLSession, taskNeedsNewBodyStream task: URLSessionTask) {
        performEvent {
            $0.urlSession(session, taskNeedsNewBodyStream: task)
        }
    }
    public func urlSession(_ session: URLSession,
                           task: URLSessionTask,
                           willPerformHTTPRedirection response: HTTPURLResponse,
                           newRequest request: URLRequest) {
        performEvent {
            $0.urlSession(session,
                          task: task,
                          willPerformHTTPRedirection: response,
                          newRequest: request)
        }
    }
    public func urlSession(_ session: URLSession, task: URLSessionTask, didFinishCollecting metrics: URLSessionTaskMetrics) {
        performEvent { $0.urlSession(session, task: task, didFinishCollecting: metrics) }
    }
    public func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
        performEvent { $0.urlSession(session, task: task, didCompleteWithError: error) }
    }
    @available(macOS 10.13, iOS 11.0, tvOS 11.0, watchOS 4.0, *)
    public func urlSession(_ session: URLSession, taskIsWaitingForConnectivity task: URLSessionTask) {
        performEvent { $0.urlSession(session, taskIsWaitingForConnectivity: task) }
    }
    public func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse) {
        performEvent { $0.urlSession(session, dataTask: dataTask, didReceive: response) }
    }
    public func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
        performEvent { $0.urlSession(session, dataTask: dataTask, didReceive: data) }
    }
    public func urlSession(_ session: URLSession,
                           dataTask: URLSessionDataTask,
                           willCacheResponse proposedResponse: CachedURLResponse) {
        performEvent { $0.urlSession(session, dataTask: dataTask, willCacheResponse: proposedResponse) }
    }
    public func urlSession(_ session: URLSession,
                           downloadTask: URLSessionDownloadTask,
                           didResumeAtOffset fileOffset: Int64,
                           expectedTotalBytes: Int64) {
        performEvent {
            $0.urlSession(session,
                          downloadTask: downloadTask,
                          didResumeAtOffset: fileOffset,
                          expectedTotalBytes: expectedTotalBytes)
        }
    }
    public func urlSession(_ session: URLSession,
                           downloadTask: URLSessionDownloadTask,
                           didWriteData bytesWritten: Int64,
                           totalBytesWritten: Int64,
                           totalBytesExpectedToWrite: Int64) {
        performEvent {
            $0.urlSession(session,
                          downloadTask: downloadTask,
                          didWriteData: bytesWritten,
                          totalBytesWritten: totalBytesWritten,
                          totalBytesExpectedToWrite: totalBytesExpectedToWrite)
        }
    }
    public func urlSession(_ session: URLSession,
                           downloadTask: URLSessionDownloadTask,
                           didFinishDownloadingTo location: URL) {
        performEvent { $0.urlSession(session, downloadTask: downloadTask, didFinishDownloadingTo: location) }
    }
    public func request(_ request: Request, didCreateInitialURLRequest urlRequest: URLRequest) {
        performEvent { $0.request(request, didCreateInitialURLRequest: urlRequest) }
    }
    public func request(_ request: Request, didFailToCreateURLRequestWithError error: AFError) {
        performEvent { $0.request(request, didFailToCreateURLRequestWithError: error) }
    }
    public func request(_ request: Request, didAdaptInitialRequest initialRequest: URLRequest, to adaptedRequest: URLRequest) {
        performEvent { $0.request(request, didAdaptInitialRequest: initialRequest, to: adaptedRequest) }
    }
    public func request(_ request: Request, didFailToAdaptURLRequest initialRequest: URLRequest, withError error: AFError) {
        performEvent { $0.request(request, didFailToAdaptURLRequest: initialRequest, withError: error) }
    }
    public func request(_ request: Request, didCreateURLRequest urlRequest: URLRequest) {
        performEvent { $0.request(request, didCreateURLRequest: urlRequest) }
    }
    public func request(_ request: Request, didCreateTask task: URLSessionTask) {
        performEvent { $0.request(request, didCreateTask: task) }
    }
    public func request(_ request: Request, didGatherMetrics metrics: URLSessionTaskMetrics) {
        performEvent { $0.request(request, didGatherMetrics: metrics) }
    }
    public func request(_ request: Request, didFailTask task: URLSessionTask, earlyWithError error: AFError) {
        performEvent { $0.request(request, didFailTask: task, earlyWithError: error) }
    }
    public func request(_ request: Request, didCompleteTask task: URLSessionTask, with error: AFError?) {
        performEvent { $0.request(request, didCompleteTask: task, with: error) }
    }
    public func requestIsRetrying(_ request: Request) {
        performEvent { $0.requestIsRetrying(request) }
    }
    public func requestDidFinish(_ request: Request) {
        performEvent { $0.requestDidFinish(request) }
    }
    public func requestDidResume(_ request: Request) {
        performEvent { $0.requestDidResume(request) }
    }
    public func request(_ request: Request, didResumeTask task: URLSessionTask) {
        performEvent { $0.request(request, didResumeTask: task) }
    }
    public func requestDidSuspend(_ request: Request) {
        performEvent { $0.requestDidSuspend(request) }
    }
    public func request(_ request: Request, didSuspendTask task: URLSessionTask) {
        performEvent { $0.request(request, didSuspendTask: task) }
    }
    public func requestDidCancel(_ request: Request) {
        performEvent { $0.requestDidCancel(request) }
    }
    public func request(_ request: Request, didCancelTask task: URLSessionTask) {
        performEvent { $0.request(request, didCancelTask: task) }
    }
    public func request(_ request: DataRequest,
                        didValidateRequest urlRequest: URLRequest?,
                        response: HTTPURLResponse,
                        data: Data?,
                        withResult result: Request.ValidationResult) {
        performEvent { $0.request(request,
                                  didValidateRequest: urlRequest,
                                  response: response,
                                  data: data,
                                  withResult: result)
        }
    }
    public func request(_ request: DataRequest, didParseResponse response: DataResponse<Data?, AFError>) {
        performEvent { $0.request(request, didParseResponse: response) }
    }
    public func request<Value>(_ request: DataRequest, didParseResponse response: DataResponse<Value, AFError>) {
        performEvent { $0.request(request, didParseResponse: response) }
    }
    public func request(_ request: DataStreamRequest,
                        didValidateRequest urlRequest: URLRequest?,
                        response: HTTPURLResponse,
                        withResult result: Request.ValidationResult) {
        performEvent { $0.request(request,
                                  didValidateRequest: urlRequest,
                                  response: response,
                                  withResult: result)
        }
    }
    public func request<Value>(_ request: DataStreamRequest, didParseStream result: Result<Value, AFError>) {
        performEvent { $0.request(request, didParseStream: result) }
    }
    public func request(_ request: UploadRequest, didCreateUploadable uploadable: UploadRequest.Uploadable) {
        performEvent { $0.request(request, didCreateUploadable: uploadable) }
    }
    public func request(_ request: UploadRequest, didFailToCreateUploadableWithError error: AFError) {
        performEvent { $0.request(request, didFailToCreateUploadableWithError: error) }
    }
    public func request(_ request: UploadRequest, didProvideInputStream stream: InputStream) {
        performEvent { $0.request(request, didProvideInputStream: stream) }
    }
    public func request(_ request: DownloadRequest, didFinishDownloadingUsing task: URLSessionTask, with result: Result<URL, AFError>) {
        performEvent { $0.request(request, didFinishDownloadingUsing: task, with: result) }
    }
    public func request(_ request: DownloadRequest, didCreateDestinationURL url: URL) {
        performEvent { $0.request(request, didCreateDestinationURL: url) }
    }
    public func request(_ request: DownloadRequest,
                        didValidateRequest urlRequest: URLRequest?,
                        response: HTTPURLResponse,
                        fileURL: URL?,
                        withResult result: Request.ValidationResult) {
        performEvent { $0.request(request,
                                  didValidateRequest: urlRequest,
                                  response: response,
                                  fileURL: fileURL,
                                  withResult: result) }
    }
    public func request(_ request: DownloadRequest, didParseResponse response: DownloadResponse<URL?, AFError>) {
        performEvent { $0.request(request, didParseResponse: response) }
    }
    public func request<Value>(_ request: DownloadRequest, didParseResponse response: DownloadResponse<Value, AFError>) {
        performEvent { $0.request(request, didParseResponse: response) }
    }
}
/// `EventMonitor` that allows optional closures to be set to receive events.
open class ClosureEventMonitor: EventMonitor {
    /// Closure called on the `urlSession(_:didBecomeInvalidWithError:)` event.
    open var sessionDidBecomeInvalidWithError: ((URLSession, Error?) -> Void)?
    /// Closure called on the `urlSession(_:task:didReceive:completionHandler:)`.
    open var taskDidReceiveChallenge: ((URLSession, URLSessionTask, URLAuthenticationChallenge) -> Void)?
    /// Closure that receives `urlSession(_:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:)` event.
    open var taskDidSendBodyData: ((URLSession, URLSessionTask, Int64, Int64, Int64) -> Void)?
    /// Closure called on the `urlSession(_:task:needNewBodyStream:)` event.
    open var taskNeedNewBodyStream: ((URLSession, URLSessionTask) -> Void)?
    /// Closure called on the `urlSession(_:task:willPerformHTTPRedirection:newRequest:completionHandler:)` event.
    open var taskWillPerformHTTPRedirection: ((URLSession, URLSessionTask, HTTPURLResponse, URLRequest) -> Void)?
    /// Closure called on the `urlSession(_:task:didFinishCollecting:)` event.
    open var taskDidFinishCollectingMetrics: ((URLSession, URLSessionTask, URLSessionTaskMetrics) -> Void)?
    /// Closure called on the `urlSession(_:task:didCompleteWithError:)` event.
    open var taskDidComplete: ((URLSession, URLSessionTask, Error?) -> Void)?
    /// Closure called on the `urlSession(_:taskIsWaitingForConnectivity:)` event.
    open var taskIsWaitingForConnectivity: ((URLSession, URLSessionTask) -> Void)?
    /// Closure called on the `urlSession(_:dataTask:didReceive:completionHandler:)` event.
    open var dataTaskDidReceiveResponse: ((URLSession, URLSessionDataTask, URLResponse) -> Void)?
    /// Closure that receives the `urlSession(_:dataTask:didReceive:)` event.
    open var dataTaskDidReceiveData: ((URLSession, URLSessionDataTask, Data) -> Void)?
    /// Closure called on the `urlSession(_:dataTask:willCacheResponse:completionHandler:)` event.
    open var dataTaskWillCacheResponse: ((URLSession, URLSessionDataTask, CachedURLResponse) -> Void)?
    /// Closure called on the `urlSession(_:downloadTask:didFinishDownloadingTo:)` event.
    open var downloadTaskDidFinishDownloadingToURL: ((URLSession, URLSessionDownloadTask, URL) -> Void)?
    /// Closure called on the `urlSession(_:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:)`
    /// event.
    open var downloadTaskDidWriteData: ((URLSession, URLSessionDownloadTask, Int64, Int64, Int64) -> Void)?
    /// Closure called on the `urlSession(_:downloadTask:didResumeAtOffset:expectedTotalBytes:)` event.
    open var downloadTaskDidResumeAtOffset: ((URLSession, URLSessionDownloadTask, Int64, Int64) -> Void)?
    // MARK: - Request Events
    /// Closure called on the `request(_:didCreateInitialURLRequest:)` event.
    open var requestDidCreateInitialURLRequest: ((Request, URLRequest) -> Void)?
    /// Closure called on the `request(_:didFailToCreateURLRequestWithError:)` event.
    open var requestDidFailToCreateURLRequestWithError: ((Request, AFError) -> Void)?
    /// Closure called on the `request(_:didAdaptInitialRequest:to:)` event.
    open var requestDidAdaptInitialRequestToAdaptedRequest: ((Request, URLRequest, URLRequest) -> Void)?
    /// Closure called on the `request(_:didFailToAdaptURLRequest:withError:)` event.
    open var requestDidFailToAdaptURLRequestWithError: ((Request, URLRequest, AFError) -> Void)?
    /// Closure called on the `request(_:didCreateURLRequest:)` event.
    open var requestDidCreateURLRequest: ((Request, URLRequest) -> Void)?
    /// Closure called on the `request(_:didCreateTask:)` event.
    open var requestDidCreateTask: ((Request, URLSessionTask) -> Void)?
    /// Closure called on the `request(_:didGatherMetrics:)` event.
    open var requestDidGatherMetrics: ((Request, URLSessionTaskMetrics) -> Void)?
    /// Closure called on the `request(_:didFailTask:earlyWithError:)` event.
    open var requestDidFailTaskEarlyWithError: ((Request, URLSessionTask, AFError) -> Void)?
    /// Closure called on the `request(_:didCompleteTask:with:)` event.
    open var requestDidCompleteTaskWithError: ((Request, URLSessionTask, AFError?) -> Void)?
    /// Closure called on the `requestIsRetrying(_:)` event.
    open var requestIsRetrying: ((Request) -> Void)?
    /// Closure called on the `requestDidFinish(_:)` event.
    open var requestDidFinish: ((Request) -> Void)?
    /// Closure called on the `requestDidResume(_:)` event.
    open var requestDidResume: ((Request) -> Void)?
    /// Closure called on the `request(_:didResumeTask:)` event.
    open var requestDidResumeTask: ((Request, URLSessionTask) -> Void)?
    /// Closure called on the `requestDidSuspend(_:)` event.
    open var requestDidSuspend: ((Request) -> Void)?
    /// Closure called on the `request(_:didSuspendTask:)` event.
    open var requestDidSuspendTask: ((Request, URLSessionTask) -> Void)?
    /// Closure called on the `requestDidCancel(_:)` event.
    open var requestDidCancel: ((Request) -> Void)?
    /// Closure called on the `request(_:didCancelTask:)` event.
    open var requestDidCancelTask: ((Request, URLSessionTask) -> Void)?
    /// Closure called on the `request(_:didValidateRequest:response:data:withResult:)` event.
    open var requestDidValidateRequestResponseDataWithResult: ((DataRequest, URLRequest?, HTTPURLResponse, Data?, Request.ValidationResult) -> Void)?
    /// Closure called on the `request(_:didParseResponse:)` event.
    open var requestDidParseResponse: ((DataRequest, DataResponse<Data?, AFError>) -> Void)?
    /// Closure called on the `request(_:didValidateRequest:response:withResult:)` event.
    open var requestDidValidateRequestResponseWithResult: ((DataStreamRequest, URLRequest?, HTTPURLResponse, Request.ValidationResult) -> Void)?
    /// Closure called on the `request(_:didCreateUploadable:)` event.
    open var requestDidCreateUploadable: ((UploadRequest, UploadRequest.Uploadable) -> Void)?
    /// Closure called on the `request(_:didFailToCreateUploadableWithError:)` event.
    open var requestDidFailToCreateUploadableWithError: ((UploadRequest, AFError) -> Void)?
    /// Closure called on the `request(_:didProvideInputStream:)` event.
    open var requestDidProvideInputStream: ((UploadRequest, InputStream) -> Void)?
    /// Closure called on the `request(_:didFinishDownloadingUsing:with:)` event.
    open var requestDidFinishDownloadingUsingTaskWithResult: ((DownloadRequest, URLSessionTask, Result<URL, AFError>) -> Void)?
    /// Closure called on the `request(_:didCreateDestinationURL:)` event.
    open var requestDidCreateDestinationURL: ((DownloadRequest, URL) -> Void)?
    /// Closure called on the `request(_:didValidateRequest:response:temporaryURL:destinationURL:withResult:)` event.
    open var requestDidValidateRequestResponseFileURLWithResult: ((DownloadRequest, URLRequest?, HTTPURLResponse, URL?, Request.ValidationResult) -> Void)?
    /// Closure called on the `request(_:didParseResponse:)` event.
    open var requestDidParseDownloadResponse: ((DownloadRequest, DownloadResponse<URL?, AFError>) -> Void)?
    public let queue: DispatchQueue
    /// Creates an instance using the provided queue.
    ///
    /// - Parameter queue: `DispatchQueue` on which events will fired. `.main` by default.
    public init(queue: DispatchQueue = .main) {
        self.queue = queue
    }
    open func urlSession(_ session: URLSession, didBecomeInvalidWithError error: Error?) {
        sessionDidBecomeInvalidWithError?(session, error)
    }
    open func urlSession(_ session: URLSession, task: URLSessionTask, didReceive challenge: URLAuthenticationChallenge) {
        taskDidReceiveChallenge?(session, task, challenge)
    }
    open func urlSession(_ session: URLSession,
                         task: URLSessionTask,
                         didSendBodyData bytesSent: Int64,
                         totalBytesSent: Int64,
                         totalBytesExpectedToSend: Int64) {
        taskDidSendBodyData?(session, task, bytesSent, totalBytesSent, totalBytesExpectedToSend)
    }
    open func urlSession(_ session: URLSession, taskNeedsNewBodyStream task: URLSessionTask) {
        taskNeedNewBodyStream?(session, task)
    }
    open func urlSession(_ session: URLSession,
                         task: URLSessionTask,
                         willPerformHTTPRedirection response: HTTPURLResponse,
                         newRequest request: URLRequest) {
        taskWillPerformHTTPRedirection?(session, task, response, request)
    }
    open func urlSession(_ session: URLSession, task: URLSessionTask, didFinishCollecting metrics: URLSessionTaskMetrics) {
        taskDidFinishCollectingMetrics?(session, task, metrics)
    }
    open func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
        taskDidComplete?(session, task, error)
    }
    @available(macOS 10.13, iOS 11.0, tvOS 11.0, watchOS 4.0, *)
    open func urlSession(_ session: URLSession, taskIsWaitingForConnectivity task: URLSessionTask) {
        taskIsWaitingForConnectivity?(session, task)
    }
    open func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse) {
        dataTaskDidReceiveResponse?(session, dataTask, response)
    }
    open func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
        dataTaskDidReceiveData?(session, dataTask, data)
    }
    open func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, willCacheResponse proposedResponse: CachedURLResponse) {
        dataTaskWillCacheResponse?(session, dataTask, proposedResponse)
    }
    open func urlSession(_ session: URLSession,
                         downloadTask: URLSessionDownloadTask,
                         didResumeAtOffset fileOffset: Int64,
                         expectedTotalBytes: Int64) {
        downloadTaskDidResumeAtOffset?(session, downloadTask, fileOffset, expectedTotalBytes)
    }
    open func urlSession(_ session: URLSession,
                         downloadTask: URLSessionDownloadTask,
                         didWriteData bytesWritten: Int64,
                         totalBytesWritten: Int64,
                         totalBytesExpectedToWrite: Int64) {
        downloadTaskDidWriteData?(session, downloadTask, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite)
    }
    open func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
        downloadTaskDidFinishDownloadingToURL?(session, downloadTask, location)
    }
    // MARK: Request Events
    open func request(_ request: Request, didCreateInitialURLRequest urlRequest: URLRequest) {
        requestDidCreateInitialURLRequest?(request, urlRequest)
    }
    open func request(_ request: Request, didFailToCreateURLRequestWithError error: AFError) {
        requestDidFailToCreateURLRequestWithError?(request, error)
    }
    open func request(_ request: Request, didAdaptInitialRequest initialRequest: URLRequest, to adaptedRequest: URLRequest) {
        requestDidAdaptInitialRequestToAdaptedRequest?(request, initialRequest, adaptedRequest)
    }
    open func request(_ request: Request, didFailToAdaptURLRequest initialRequest: URLRequest, withError error: AFError) {
        requestDidFailToAdaptURLRequestWithError?(request, initialRequest, error)
    }
    open func request(_ request: Request, didCreateURLRequest urlRequest: URLRequest) {
        requestDidCreateURLRequest?(request, urlRequest)
    }
    open func request(_ request: Request, didCreateTask task: URLSessionTask) {
        requestDidCreateTask?(request, task)
    }
    open func request(_ request: Request, didGatherMetrics metrics: URLSessionTaskMetrics) {
        requestDidGatherMetrics?(request, metrics)
    }
    open func request(_ request: Request, didFailTask task: URLSessionTask, earlyWithError error: AFError) {
        requestDidFailTaskEarlyWithError?(request, task, error)
    }
    open func request(_ request: Request, didCompleteTask task: URLSessionTask, with error: AFError?) {
        requestDidCompleteTaskWithError?(request, task, error)
    }
    open func requestIsRetrying(_ request: Request) {
        requestIsRetrying?(request)
    }
    open func requestDidFinish(_ request: Request) {
        requestDidFinish?(request)
    }
    open func requestDidResume(_ request: Request) {
        requestDidResume?(request)
    }
    public func request(_ request: Request, didResumeTask task: URLSessionTask) {
        requestDidResumeTask?(request, task)
    }
    open func requestDidSuspend(_ request: Request) {
        requestDidSuspend?(request)
    }
    public func request(_ request: Request, didSuspendTask task: URLSessionTask) {
        requestDidSuspendTask?(request, task)
    }
    open func requestDidCancel(_ request: Request) {
        requestDidCancel?(request)
    }
    public func request(_ request: Request, didCancelTask task: URLSessionTask) {
        requestDidCancelTask?(request, task)
    }
    open func request(_ request: DataRequest,
                      didValidateRequest urlRequest: URLRequest?,
                      response: HTTPURLResponse,
                      data: Data?,
                      withResult result: Request.ValidationResult) {
        requestDidValidateRequestResponseDataWithResult?(request, urlRequest, response, data, result)
    }
    open func request(_ request: DataRequest, didParseResponse response: DataResponse<Data?, AFError>) {
        requestDidParseResponse?(request, response)
    }
    public func request(_ request: DataStreamRequest, didValidateRequest urlRequest: URLRequest?, response: HTTPURLResponse, withResult result: Request.ValidationResult) {
        requestDidValidateRequestResponseWithResult?(request, urlRequest, response, result)
    }
    open func request(_ request: UploadRequest, didCreateUploadable uploadable: UploadRequest.Uploadable) {
        requestDidCreateUploadable?(request, uploadable)
    }
    open func request(_ request: UploadRequest, didFailToCreateUploadableWithError error: AFError) {
        requestDidFailToCreateUploadableWithError?(request, error)
    }
    open func request(_ request: UploadRequest, didProvideInputStream stream: InputStream) {
        requestDidProvideInputStream?(request, stream)
    }
    open func request(_ request: DownloadRequest, didFinishDownloadingUsing task: URLSessionTask, with result: Result<URL, AFError>) {
        requestDidFinishDownloadingUsingTaskWithResult?(request, task, result)
    }
    open func request(_ request: DownloadRequest, didCreateDestinationURL url: URL) {
        requestDidCreateDestinationURL?(request, url)
    }
    open func request(_ request: DownloadRequest,
                      didValidateRequest urlRequest: URLRequest?,
                      response: HTTPURLResponse,
                      fileURL: URL?,
                      withResult result: Request.ValidationResult) {
        requestDidValidateRequestResponseFileURLWithResult?(request,
                                                            urlRequest,
                                                            response,
                                                            fileURL,
                                                            result)
    }
    open func request(_ request: DownloadRequest, didParseResponse response: DownloadResponse<URL?, AFError>) {
        requestDidParseDownloadResponse?(request, response)
    }
}
Pods/Alamofire/Source/HTTPHeaders.swift
New file
@@ -0,0 +1,452 @@
//
//  HTTPHeaders.swift
//
//  Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/)
//
//  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:
//
//  The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
//
//  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.
//
import Foundation
/// An order-preserving and case-insensitive representation of HTTP headers.
public struct HTTPHeaders {
    private var headers: [HTTPHeader] = []
    /// Creates an empty instance.
    public init() {}
    /// Creates an instance from an array of `HTTPHeader`s. Duplicate case-insensitive names are collapsed into the last
    /// name and value encountered.
    public init(_ headers: [HTTPHeader]) {
        headers.forEach { update($0) }
    }
    /// Creates an instance from a `[String: String]`. Duplicate case-insensitive names are collapsed into the last name
    /// and value encountered.
    public init(_ dictionary: [String: String]) {
        dictionary.forEach { update(HTTPHeader(name: $0.key, value: $0.value)) }
    }
    /// Case-insensitively updates or appends an `HTTPHeader` into the instance using the provided `name` and `value`.
    ///
    /// - Parameters:
    ///   - name:  The `HTTPHeader` name.
    ///   - value: The `HTTPHeader value.
    public mutating func add(name: String, value: String) {
        update(HTTPHeader(name: name, value: value))
    }
    /// Case-insensitively updates or appends the provided `HTTPHeader` into the instance.
    ///
    /// - Parameter header: The `HTTPHeader` to update or append.
    public mutating func add(_ header: HTTPHeader) {
        update(header)
    }
    /// Case-insensitively updates or appends an `HTTPHeader` into the instance using the provided `name` and `value`.
    ///
    /// - Parameters:
    ///   - name:  The `HTTPHeader` name.
    ///   - value: The `HTTPHeader value.
    public mutating func update(name: String, value: String) {
        update(HTTPHeader(name: name, value: value))
    }
    /// Case-insensitively updates or appends the provided `HTTPHeader` into the instance.
    ///
    /// - Parameter header: The `HTTPHeader` to update or append.
    public mutating func update(_ header: HTTPHeader) {
        guard let index = headers.index(of: header.name) else {
            headers.append(header)
            return
        }
        headers.replaceSubrange(index...index, with: [header])
    }
    /// Case-insensitively removes an `HTTPHeader`, if it exists, from the instance.
    ///
    /// - Parameter name: The name of the `HTTPHeader` to remove.
    public mutating func remove(name: String) {
        guard let index = headers.index(of: name) else { return }
        headers.remove(at: index)
    }
    /// Sort the current instance by header name, case insensitively.
    public mutating func sort() {
        headers.sort { $0.name.lowercased() < $1.name.lowercased() }
    }
    /// Returns an instance sorted by header name.
    ///
    /// - Returns: A copy of the current instance sorted by name.
    public func sorted() -> HTTPHeaders {
        var headers = self
        headers.sort()
        return headers
    }
    /// Case-insensitively find a header's value by name.
    ///
    /// - Parameter name: The name of the header to search for, case-insensitively.
    ///
    /// - Returns:        The value of header, if it exists.
    public func value(for name: String) -> String? {
        guard let index = headers.index(of: name) else { return nil }
        return headers[index].value
    }
    /// Case-insensitively access the header with the given name.
    ///
    /// - Parameter name: The name of the header.
    public subscript(_ name: String) -> String? {
        get { value(for: name) }
        set {
            if let value = newValue {
                update(name: name, value: value)
            } else {
                remove(name: name)
            }
        }
    }
    /// The dictionary representation of all headers.
    ///
    /// This representation does not preserve the current order of the instance.
    public var dictionary: [String: String] {
        let namesAndValues = headers.map { ($0.name, $0.value) }
        return Dictionary(namesAndValues, uniquingKeysWith: { _, last in last })
    }
}
extension HTTPHeaders: ExpressibleByDictionaryLiteral {
    public init(dictionaryLiteral elements: (String, String)...) {
        elements.forEach { update(name: $0.0, value: $0.1) }
    }
}
extension HTTPHeaders: ExpressibleByArrayLiteral {
    public init(arrayLiteral elements: HTTPHeader...) {
        self.init(elements)
    }
}
extension HTTPHeaders: Sequence {
    public func makeIterator() -> IndexingIterator<[HTTPHeader]> {
        headers.makeIterator()
    }
}
extension HTTPHeaders: Collection {
    public var startIndex: Int {
        headers.startIndex
    }
    public var endIndex: Int {
        headers.endIndex
    }
    public subscript(position: Int) -> HTTPHeader {
        headers[position]
    }
    public func index(after i: Int) -> Int {
        headers.index(after: i)
    }
}
extension HTTPHeaders: CustomStringConvertible {
    public var description: String {
        headers.map(\.description)
            .joined(separator: "\n")
    }
}
// MARK: - HTTPHeader
/// A representation of a single HTTP header's name / value pair.
public struct HTTPHeader: Hashable {
    /// Name of the header.
    public let name: String
    /// Value of the header.
    public let value: String
    /// Creates an instance from the given `name` and `value`.
    ///
    /// - Parameters:
    ///   - name:  The name of the header.
    ///   - value: The value of the header.
    public init(name: String, value: String) {
        self.name = name
        self.value = value
    }
}
extension HTTPHeader: CustomStringConvertible {
    public var description: String {
        "\(name): \(value)"
    }
}
extension HTTPHeader {
    /// Returns an `Accept` header.
    ///
    /// - Parameter value: The `Accept` value.
    /// - Returns:         The header.
    public static func accept(_ value: String) -> HTTPHeader {
        HTTPHeader(name: "Accept", value: value)
    }
    /// Returns an `Accept-Charset` header.
    ///
    /// - Parameter value: The `Accept-Charset` value.
    /// - Returns:         The header.
    public static func acceptCharset(_ value: String) -> HTTPHeader {
        HTTPHeader(name: "Accept-Charset", value: value)
    }
    /// Returns an `Accept-Language` header.
    ///
    /// Alamofire offers a default Accept-Language header that accumulates and encodes the system's preferred languages.
    /// Use `HTTPHeader.defaultAcceptLanguage`.
    ///
    /// - Parameter value: The `Accept-Language` value.
    ///
    /// - Returns:         The header.
    public static func acceptLanguage(_ value: String) -> HTTPHeader {
        HTTPHeader(name: "Accept-Language", value: value)
    }
    /// Returns an `Accept-Encoding` header.
    ///
    /// Alamofire offers a default accept encoding value that provides the most common values. Use
    /// `HTTPHeader.defaultAcceptEncoding`.
    ///
    /// - Parameter value: The `Accept-Encoding` value.
    ///
    /// - Returns:         The header
    public static func acceptEncoding(_ value: String) -> HTTPHeader {
        HTTPHeader(name: "Accept-Encoding", value: value)
    }
    /// Returns a `Basic` `Authorization` header using the `username` and `password` provided.
    ///
    /// - Parameters:
    ///   - username: The username of the header.
    ///   - password: The password of the header.
    ///
    /// - Returns:    The header.
    public static func authorization(username: String, password: String) -> HTTPHeader {
        let credential = Data("\(username):\(password)".utf8).base64EncodedString()
        return authorization("Basic \(credential)")
    }
    /// Returns a `Bearer` `Authorization` header using the `bearerToken` provided
    ///
    /// - Parameter bearerToken: The bearer token.
    ///
    /// - Returns:               The header.
    public static func authorization(bearerToken: String) -> HTTPHeader {
        authorization("Bearer \(bearerToken)")
    }
    /// Returns an `Authorization` header.
    ///
    /// Alamofire provides built-in methods to produce `Authorization` headers. For a Basic `Authorization` header use
    /// `HTTPHeader.authorization(username:password:)`. For a Bearer `Authorization` header, use
    /// `HTTPHeader.authorization(bearerToken:)`.
    ///
    /// - Parameter value: The `Authorization` value.
    ///
    /// - Returns:         The header.
    public static func authorization(_ value: String) -> HTTPHeader {
        HTTPHeader(name: "Authorization", value: value)
    }
    /// Returns a `Content-Disposition` header.
    ///
    /// - Parameter value: The `Content-Disposition` value.
    ///
    /// - Returns:         The header.
    public static func contentDisposition(_ value: String) -> HTTPHeader {
        HTTPHeader(name: "Content-Disposition", value: value)
    }
    /// Returns a `Content-Encoding` header.
    ///
    /// - Parameter value: The `Content-Encoding`.
    ///
    /// - Returns:         The header.
    public static func contentEncoding(_ value: String) -> HTTPHeader {
        HTTPHeader(name: "Content-Encoding", value: value)
    }
    /// Returns a `Content-Type` header.
    ///
    /// All Alamofire `ParameterEncoding`s and `ParameterEncoder`s set the `Content-Type` of the request, so it may not
    /// be necessary to manually set this value.
    ///
    /// - Parameter value: The `Content-Type` value.
    ///
    /// - Returns:         The header.
    public static func contentType(_ value: String) -> HTTPHeader {
        HTTPHeader(name: "Content-Type", value: value)
    }
    /// Returns a `User-Agent` header.
    ///
    /// - Parameter value: The `User-Agent` value.
    ///
    /// - Returns:         The header.
    public static func userAgent(_ value: String) -> HTTPHeader {
        HTTPHeader(name: "User-Agent", value: value)
    }
}
extension Array where Element == HTTPHeader {
    /// Case-insensitively finds the index of an `HTTPHeader` with the provided name, if it exists.
    func index(of name: String) -> Int? {
        let lowercasedName = name.lowercased()
        return firstIndex { $0.name.lowercased() == lowercasedName }
    }
}
// MARK: - Defaults
extension HTTPHeaders {
    /// The default set of `HTTPHeaders` used by Alamofire. Includes `Accept-Encoding`, `Accept-Language`, and
    /// `User-Agent`.
    public static let `default`: HTTPHeaders = [.defaultAcceptEncoding,
                                                .defaultAcceptLanguage,
                                                .defaultUserAgent]
}
extension HTTPHeader {
    /// Returns Alamofire's default `Accept-Encoding` header, appropriate for the encodings supported by particular OS
    /// versions.
    ///
    /// See the [Accept-Encoding HTTP header documentation](https://tools.ietf.org/html/rfc7230#section-4.2.3) .
    public static let defaultAcceptEncoding: HTTPHeader = {
        let encodings: [String]
        if #available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, *) {
            encodings = ["br", "gzip", "deflate"]
        } else {
            encodings = ["gzip", "deflate"]
        }
        return .acceptEncoding(encodings.qualityEncoded())
    }()
    /// Returns Alamofire's default `Accept-Language` header, generated by querying `Locale` for the user's
    /// `preferredLanguages`.
    ///
    /// See the [Accept-Language HTTP header documentation](https://tools.ietf.org/html/rfc7231#section-5.3.5).
    public static let defaultAcceptLanguage: HTTPHeader = .acceptLanguage(Locale.preferredLanguages.prefix(6).qualityEncoded())
    /// Returns Alamofire's default `User-Agent` header.
    ///
    /// See the [User-Agent header documentation](https://tools.ietf.org/html/rfc7231#section-5.5.3).
    ///
    /// Example: `iOS Example/1.0 (org.alamofire.iOS-Example; build:1; iOS 13.0.0) Alamofire/5.0.0`
    public static let defaultUserAgent: HTTPHeader = {
        let info = Bundle.main.infoDictionary
        let executable = (info?["CFBundleExecutable"] as? String) ??
            (ProcessInfo.processInfo.arguments.first?.split(separator: "/").last.map(String.init)) ??
            "Unknown"
        let bundle = info?["CFBundleIdentifier"] as? String ?? "Unknown"
        let appVersion = info?["CFBundleShortVersionString"] as? String ?? "Unknown"
        let appBuild = info?["CFBundleVersion"] as? String ?? "Unknown"
        let osNameVersion: String = {
            let version = ProcessInfo.processInfo.operatingSystemVersion
            let versionString = "\(version.majorVersion).\(version.minorVersion).\(version.patchVersion)"
            let osName: String = {
                #if os(iOS)
                #if targetEnvironment(macCatalyst)
                return "macOS(Catalyst)"
                #else
                return "iOS"
                #endif
                #elseif os(watchOS)
                return "watchOS"
                #elseif os(tvOS)
                return "tvOS"
                #elseif os(macOS)
                return "macOS"
                #elseif os(Linux)
                return "Linux"
                #elseif os(Windows)
                return "Windows"
                #elseif os(Android)
                return "Android"
                #else
                return "Unknown"
                #endif
            }()
            return "\(osName) \(versionString)"
        }()
        let alamofireVersion = "Alamofire/\(version)"
        let userAgent = "\(executable)/\(appVersion) (\(bundle); build:\(appBuild); \(osNameVersion)) \(alamofireVersion)"
        return .userAgent(userAgent)
    }()
}
extension Collection where Element == String {
    func qualityEncoded() -> String {
        enumerated().map { index, encoding in
            let quality = 1.0 - (Double(index) * 0.1)
            return "\(encoding);q=\(quality)"
        }.joined(separator: ", ")
    }
}
// MARK: - System Type Extensions
extension URLRequest {
    /// Returns `allHTTPHeaderFields` as `HTTPHeaders`.
    public var headers: HTTPHeaders {
        get { allHTTPHeaderFields.map(HTTPHeaders.init) ?? HTTPHeaders() }
        set { allHTTPHeaderFields = newValue.dictionary }
    }
}
extension HTTPURLResponse {
    /// Returns `allHeaderFields` as `HTTPHeaders`.
    public var headers: HTTPHeaders {
        (allHeaderFields as? [String: String]).map(HTTPHeaders.init) ?? HTTPHeaders()
    }
}
extension URLSessionConfiguration {
    /// Returns `httpAdditionalHeaders` as `HTTPHeaders`.
    public var headers: HTTPHeaders {
        get { (httpAdditionalHeaders as? [String: String]).map(HTTPHeaders.init) ?? HTTPHeaders() }
        set { httpAdditionalHeaders = newValue.dictionary }
    }
}
Pods/Alamofire/Source/HTTPMethod.swift
New file
@@ -0,0 +1,56 @@
//
//  HTTPMethod.swift
//
//  Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/)
//
//  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:
//
//  The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
//
//  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.
//
/// Type representing HTTP methods. Raw `String` value is stored and compared case-sensitively, so
/// `HTTPMethod.get != HTTPMethod(rawValue: "get")`.
///
/// See https://tools.ietf.org/html/rfc7231#section-4.3
public struct HTTPMethod: RawRepresentable, Equatable, Hashable {
    /// `CONNECT` method.
    public static let connect = HTTPMethod(rawValue: "CONNECT")
    /// `DELETE` method.
    public static let delete = HTTPMethod(rawValue: "DELETE")
    /// `GET` method.
    public static let get = HTTPMethod(rawValue: "GET")
    /// `HEAD` method.
    public static let head = HTTPMethod(rawValue: "HEAD")
    /// `OPTIONS` method.
    public static let options = HTTPMethod(rawValue: "OPTIONS")
    /// `PATCH` method.
    public static let patch = HTTPMethod(rawValue: "PATCH")
    /// `POST` method.
    public static let post = HTTPMethod(rawValue: "POST")
    /// `PUT` method.
    public static let put = HTTPMethod(rawValue: "PUT")
    /// `QUERY` method.
    public static let query = HTTPMethod(rawValue: "QUERY")
    /// `TRACE` method.
    public static let trace = HTTPMethod(rawValue: "TRACE")
    public let rawValue: String
    public init(rawValue: String) {
        self.rawValue = rawValue
    }
}
Pods/Alamofire/Source/MultipartFormData.swift
New file
@@ -0,0 +1,601 @@
//
//  MultipartFormData.swift
//
//  Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/)
//
//  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:
//
//  The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
//
//  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.
//
import Foundation
#if canImport(MobileCoreServices)
import MobileCoreServices
#elseif canImport(CoreServices)
import CoreServices
#endif
/// Constructs `multipart/form-data` for uploads within an HTTP or HTTPS body. There are currently two ways to encode
/// multipart form data. The first way is to encode the data directly in memory. This is very efficient, but can lead
/// to memory issues if the dataset is too large. The second way is designed for larger datasets and will write all the
/// data to a single file on disk with all the proper boundary segmentation. The second approach MUST be used for
/// larger datasets such as video content, otherwise your app may run out of memory when trying to encode the dataset.
///
/// For more information on `multipart/form-data` in general, please refer to the RFC-2388 and RFC-2045 specs as well
/// and the w3 form documentation.
///
/// - https://www.ietf.org/rfc/rfc2388.txt
/// - https://www.ietf.org/rfc/rfc2045.txt
/// - https://www.w3.org/TR/html401/interact/forms.html#h-17.13
open class MultipartFormData {
    // MARK: - Helper Types
    enum EncodingCharacters {
        static let crlf = "\r\n"
    }
    enum BoundaryGenerator {
        enum BoundaryType {
            case initial, encapsulated, final
        }
        static func randomBoundary() -> String {
            let first = UInt32.random(in: UInt32.min...UInt32.max)
            let second = UInt32.random(in: UInt32.min...UInt32.max)
            return String(format: "alamofire.boundary.%08x%08x", first, second)
        }
        static func boundaryData(forBoundaryType boundaryType: BoundaryType, boundary: String) -> Data {
            let boundaryText: String
            switch boundaryType {
            case .initial:
                boundaryText = "--\(boundary)\(EncodingCharacters.crlf)"
            case .encapsulated:
                boundaryText = "\(EncodingCharacters.crlf)--\(boundary)\(EncodingCharacters.crlf)"
            case .final:
                boundaryText = "\(EncodingCharacters.crlf)--\(boundary)--\(EncodingCharacters.crlf)"
            }
            return Data(boundaryText.utf8)
        }
    }
    class BodyPart {
        let headers: HTTPHeaders
        let bodyStream: InputStream
        let bodyContentLength: UInt64
        var hasInitialBoundary = false
        var hasFinalBoundary = false
        init(headers: HTTPHeaders, bodyStream: InputStream, bodyContentLength: UInt64) {
            self.headers = headers
            self.bodyStream = bodyStream
            self.bodyContentLength = bodyContentLength
        }
    }
    // MARK: - Properties
    /// Default memory threshold used when encoding `MultipartFormData`, in bytes.
    public static let encodingMemoryThreshold: UInt64 = 10_000_000
    /// The `Content-Type` header value containing the boundary used to generate the `multipart/form-data`.
    open lazy var contentType: String = "multipart/form-data; boundary=\(self.boundary)"
    /// The content length of all body parts used to generate the `multipart/form-data` not including the boundaries.
    public var contentLength: UInt64 { bodyParts.reduce(0) { $0 + $1.bodyContentLength } }
    /// The boundary used to separate the body parts in the encoded form data.
    public let boundary: String
    let fileManager: FileManager
    private var bodyParts: [BodyPart]
    private var bodyPartError: AFError?
    private let streamBufferSize: Int
    // MARK: - Lifecycle
    /// Creates an instance.
    ///
    /// - Parameters:
    ///   - fileManager: `FileManager` to use for file operations, if needed.
    ///   - boundary: Boundary `String` used to separate body parts.
    public init(fileManager: FileManager = .default, boundary: String? = nil) {
        self.fileManager = fileManager
        self.boundary = boundary ?? BoundaryGenerator.randomBoundary()
        bodyParts = []
        //
        // The optimal read/write buffer size in bytes for input and output streams is 1024 (1KB). For more
        // information, please refer to the following article:
        //   - https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Streams/Articles/ReadingInputStreams.html
        //
        streamBufferSize = 1024
    }
    // MARK: - Body Parts
    /// Creates a body part from the data and appends it to the instance.
    ///
    /// The body part data will be encoded using the following format:
    ///
    /// - `Content-Disposition: form-data; name=#{name}; filename=#{filename}` (HTTP Header)
    /// - `Content-Type: #{mimeType}` (HTTP Header)
    /// - Encoded file data
    /// - Multipart form boundary
    ///
    /// - Parameters:
    ///   - data:     `Data` to encoding into the instance.
    ///   - name:     Name to associate with the `Data` in the `Content-Disposition` HTTP header.
    ///   - fileName: Filename to associate with the `Data` in the `Content-Disposition` HTTP header.
    ///   - mimeType: MIME type to associate with the data in the `Content-Type` HTTP header.
    public func append(_ data: Data, withName name: String, fileName: String? = nil, mimeType: String? = nil) {
        let headers = contentHeaders(withName: name, fileName: fileName, mimeType: mimeType)
        let stream = InputStream(data: data)
        let length = UInt64(data.count)
        append(stream, withLength: length, headers: headers)
    }
    /// Creates a body part from the file and appends it to the instance.
    ///
    /// The body part data will be encoded using the following format:
    ///
    /// - `Content-Disposition: form-data; name=#{name}; filename=#{generated filename}` (HTTP Header)
    /// - `Content-Type: #{generated mimeType}` (HTTP Header)
    /// - Encoded file data
    /// - Multipart form boundary
    ///
    /// The filename in the `Content-Disposition` HTTP header is generated from the last path component of the
    /// `fileURL`. The `Content-Type` HTTP header MIME type is generated by mapping the `fileURL` extension to the
    /// system associated MIME type.
    ///
    /// - Parameters:
    ///   - fileURL: `URL` of the file whose content will be encoded into the instance.
    ///   - name:    Name to associate with the file content in the `Content-Disposition` HTTP header.
    public func append(_ fileURL: URL, withName name: String) {
        let fileName = fileURL.lastPathComponent
        let pathExtension = fileURL.pathExtension
        if !fileName.isEmpty && !pathExtension.isEmpty {
            let mime = mimeType(forPathExtension: pathExtension)
            append(fileURL, withName: name, fileName: fileName, mimeType: mime)
        } else {
            setBodyPartError(withReason: .bodyPartFilenameInvalid(in: fileURL))
        }
    }
    /// Creates a body part from the file and appends it to the instance.
    ///
    /// The body part data will be encoded using the following format:
    ///
    /// - Content-Disposition: form-data; name=#{name}; filename=#{filename} (HTTP Header)
    /// - Content-Type: #{mimeType} (HTTP Header)
    /// - Encoded file data
    /// - Multipart form boundary
    ///
    /// - Parameters:
    ///   - fileURL:  `URL` of the file whose content will be encoded into the instance.
    ///   - name:     Name to associate with the file content in the `Content-Disposition` HTTP header.
    ///   - fileName: Filename to associate with the file content in the `Content-Disposition` HTTP header.
    ///   - mimeType: MIME type to associate with the file content in the `Content-Type` HTTP header.
    public func append(_ fileURL: URL, withName name: String, fileName: String, mimeType: String) {
        let headers = contentHeaders(withName: name, fileName: fileName, mimeType: mimeType)
        //============================================================
        //                 Check 1 - is file URL?
        //============================================================
        guard fileURL.isFileURL else {
            setBodyPartError(withReason: .bodyPartURLInvalid(url: fileURL))
            return
        }
        //============================================================
        //              Check 2 - is file URL reachable?
        //============================================================
        #if !(os(Linux) || os(Windows) || os(Android))
        do {
            let isReachable = try fileURL.checkPromisedItemIsReachable()
            guard isReachable else {
                setBodyPartError(withReason: .bodyPartFileNotReachable(at: fileURL))
                return
            }
        } catch {
            setBodyPartError(withReason: .bodyPartFileNotReachableWithError(atURL: fileURL, error: error))
            return
        }
        #endif
        //============================================================
        //            Check 3 - is file URL a directory?
        //============================================================
        var isDirectory: ObjCBool = false
        let path = fileURL.path
        guard fileManager.fileExists(atPath: path, isDirectory: &isDirectory) && !isDirectory.boolValue else {
            setBodyPartError(withReason: .bodyPartFileIsDirectory(at: fileURL))
            return
        }
        //============================================================
        //          Check 4 - can the file size be extracted?
        //============================================================
        let bodyContentLength: UInt64
        do {
            guard let fileSize = try fileManager.attributesOfItem(atPath: path)[.size] as? NSNumber else {
                setBodyPartError(withReason: .bodyPartFileSizeNotAvailable(at: fileURL))
                return
            }
            bodyContentLength = fileSize.uint64Value
        } catch {
            setBodyPartError(withReason: .bodyPartFileSizeQueryFailedWithError(forURL: fileURL, error: error))
            return
        }
        //============================================================
        //       Check 5 - can a stream be created from file URL?
        //============================================================
        guard let stream = InputStream(url: fileURL) else {
            setBodyPartError(withReason: .bodyPartInputStreamCreationFailed(for: fileURL))
            return
        }
        append(stream, withLength: bodyContentLength, headers: headers)
    }
    /// Creates a body part from the stream and appends it to the instance.
    ///
    /// The body part data will be encoded using the following format:
    ///
    /// - `Content-Disposition: form-data; name=#{name}; filename=#{filename}` (HTTP Header)
    /// - `Content-Type: #{mimeType}` (HTTP Header)
    /// - Encoded stream data
    /// - Multipart form boundary
    ///
    /// - Parameters:
    ///   - stream:   `InputStream` to encode into the instance.
    ///   - length:   Length, in bytes, of the stream.
    ///   - name:     Name to associate with the stream content in the `Content-Disposition` HTTP header.
    ///   - fileName: Filename to associate with the stream content in the `Content-Disposition` HTTP header.
    ///   - mimeType: MIME type to associate with the stream content in the `Content-Type` HTTP header.
    public func append(_ stream: InputStream,
                       withLength length: UInt64,
                       name: String,
                       fileName: String,
                       mimeType: String) {
        let headers = contentHeaders(withName: name, fileName: fileName, mimeType: mimeType)
        append(stream, withLength: length, headers: headers)
    }
    /// Creates a body part with the stream, length, and headers and appends it to the instance.
    ///
    /// The body part data will be encoded using the following format:
    ///
    /// - HTTP headers
    /// - Encoded stream data
    /// - Multipart form boundary
    ///
    /// - Parameters:
    ///   - stream:  `InputStream` to encode into the instance.
    ///   - length:  Length, in bytes, of the stream.
    ///   - headers: `HTTPHeaders` for the body part.
    public func append(_ stream: InputStream, withLength length: UInt64, headers: HTTPHeaders) {
        let bodyPart = BodyPart(headers: headers, bodyStream: stream, bodyContentLength: length)
        bodyParts.append(bodyPart)
    }
    // MARK: - Data Encoding
    /// Encodes all appended body parts into a single `Data` value.
    ///
    /// - Note: This method will load all the appended body parts into memory all at the same time. This method should
    ///         only be used when the encoded data will have a small memory footprint. For large data cases, please use
    ///         the `writeEncodedData(to:))` method.
    ///
    /// - Returns: The encoded `Data`, if encoding is successful.
    /// - Throws:  An `AFError` if encoding encounters an error.
    public func encode() throws -> Data {
        if let bodyPartError = bodyPartError {
            throw bodyPartError
        }
        var encoded = Data()
        bodyParts.first?.hasInitialBoundary = true
        bodyParts.last?.hasFinalBoundary = true
        for bodyPart in bodyParts {
            let encodedData = try encode(bodyPart)
            encoded.append(encodedData)
        }
        return encoded
    }
    /// Writes all appended body parts to the given file `URL`.
    ///
    /// This process is facilitated by reading and writing with input and output streams, respectively. Thus,
    /// this approach is very memory efficient and should be used for large body part data.
    ///
    /// - Parameter fileURL: File `URL` to which to write the form data.
    /// - Throws:            An `AFError` if encoding encounters an error.
    public func writeEncodedData(to fileURL: URL) throws {
        if let bodyPartError = bodyPartError {
            throw bodyPartError
        }
        if fileManager.fileExists(atPath: fileURL.path) {
            throw AFError.multipartEncodingFailed(reason: .outputStreamFileAlreadyExists(at: fileURL))
        } else if !fileURL.isFileURL {
            throw AFError.multipartEncodingFailed(reason: .outputStreamURLInvalid(url: fileURL))
        }
        guard let outputStream = OutputStream(url: fileURL, append: false) else {
            throw AFError.multipartEncodingFailed(reason: .outputStreamCreationFailed(for: fileURL))
        }
        outputStream.open()
        defer { outputStream.close() }
        bodyParts.first?.hasInitialBoundary = true
        bodyParts.last?.hasFinalBoundary = true
        for bodyPart in bodyParts {
            try write(bodyPart, to: outputStream)
        }
    }
    // MARK: - Private - Body Part Encoding
    private func encode(_ bodyPart: BodyPart) throws -> Data {
        var encoded = Data()
        let initialData = bodyPart.hasInitialBoundary ? initialBoundaryData() : encapsulatedBoundaryData()
        encoded.append(initialData)
        let headerData = encodeHeaders(for: bodyPart)
        encoded.append(headerData)
        let bodyStreamData = try encodeBodyStream(for: bodyPart)
        encoded.append(bodyStreamData)
        if bodyPart.hasFinalBoundary {
            encoded.append(finalBoundaryData())
        }
        return encoded
    }
    private func encodeHeaders(for bodyPart: BodyPart) -> Data {
        let headerText = bodyPart.headers.map { "\($0.name): \($0.value)\(EncodingCharacters.crlf)" }
            .joined()
            + EncodingCharacters.crlf
        return Data(headerText.utf8)
    }
    private func encodeBodyStream(for bodyPart: BodyPart) throws -> Data {
        let inputStream = bodyPart.bodyStream
        inputStream.open()
        defer { inputStream.close() }
        var encoded = Data()
        while inputStream.hasBytesAvailable {
            var buffer = [UInt8](repeating: 0, count: streamBufferSize)
            let bytesRead = inputStream.read(&buffer, maxLength: streamBufferSize)
            if let error = inputStream.streamError {
                throw AFError.multipartEncodingFailed(reason: .inputStreamReadFailed(error: error))
            }
            if bytesRead > 0 {
                encoded.append(buffer, count: bytesRead)
            } else {
                break
            }
        }
        guard UInt64(encoded.count) == bodyPart.bodyContentLength else {
            let error = AFError.UnexpectedInputStreamLength(bytesExpected: bodyPart.bodyContentLength,
                                                            bytesRead: UInt64(encoded.count))
            throw AFError.multipartEncodingFailed(reason: .inputStreamReadFailed(error: error))
        }
        return encoded
    }
    // MARK: - Private - Writing Body Part to Output Stream
    private func write(_ bodyPart: BodyPart, to outputStream: OutputStream) throws {
        try writeInitialBoundaryData(for: bodyPart, to: outputStream)
        try writeHeaderData(for: bodyPart, to: outputStream)
        try writeBodyStream(for: bodyPart, to: outputStream)
        try writeFinalBoundaryData(for: bodyPart, to: outputStream)
    }
    private func writeInitialBoundaryData(for bodyPart: BodyPart, to outputStream: OutputStream) throws {
        let initialData = bodyPart.hasInitialBoundary ? initialBoundaryData() : encapsulatedBoundaryData()
        return try write(initialData, to: outputStream)
    }
    private func writeHeaderData(for bodyPart: BodyPart, to outputStream: OutputStream) throws {
        let headerData = encodeHeaders(for: bodyPart)
        return try write(headerData, to: outputStream)
    }
    private func writeBodyStream(for bodyPart: BodyPart, to outputStream: OutputStream) throws {
        let inputStream = bodyPart.bodyStream
        inputStream.open()
        defer { inputStream.close() }
        var bytesLeftToRead = bodyPart.bodyContentLength
        while inputStream.hasBytesAvailable && bytesLeftToRead > 0 {
            let bufferSize = min(streamBufferSize, Int(bytesLeftToRead))
            var buffer = [UInt8](repeating: 0, count: bufferSize)
            let bytesRead = inputStream.read(&buffer, maxLength: bufferSize)
            if let streamError = inputStream.streamError {
                throw AFError.multipartEncodingFailed(reason: .inputStreamReadFailed(error: streamError))
            }
            if bytesRead > 0 {
                if buffer.count != bytesRead {
                    buffer = Array(buffer[0..<bytesRead])
                }
                try write(&buffer, to: outputStream)
                bytesLeftToRead -= UInt64(bytesRead)
            } else {
                break
            }
        }
    }
    private func writeFinalBoundaryData(for bodyPart: BodyPart, to outputStream: OutputStream) throws {
        if bodyPart.hasFinalBoundary {
            return try write(finalBoundaryData(), to: outputStream)
        }
    }
    // MARK: - Private - Writing Buffered Data to Output Stream
    private func write(_ data: Data, to outputStream: OutputStream) throws {
        var buffer = [UInt8](repeating: 0, count: data.count)
        data.copyBytes(to: &buffer, count: data.count)
        return try write(&buffer, to: outputStream)
    }
    private func write(_ buffer: inout [UInt8], to outputStream: OutputStream) throws {
        var bytesToWrite = buffer.count
        while bytesToWrite > 0, outputStream.hasSpaceAvailable {
            let bytesWritten = outputStream.write(buffer, maxLength: bytesToWrite)
            if let error = outputStream.streamError {
                throw AFError.multipartEncodingFailed(reason: .outputStreamWriteFailed(error: error))
            }
            bytesToWrite -= bytesWritten
            if bytesToWrite > 0 {
                buffer = Array(buffer[bytesWritten..<buffer.count])
            }
        }
    }
    // MARK: - Private - Content Headers
    private func contentHeaders(withName name: String, fileName: String? = nil, mimeType: String? = nil) -> HTTPHeaders {
        var disposition = "form-data; name=\"\(name)\""
        if let fileName = fileName { disposition += "; filename=\"\(fileName)\"" }
        var headers: HTTPHeaders = [.contentDisposition(disposition)]
        if let mimeType = mimeType { headers.add(.contentType(mimeType)) }
        return headers
    }
    // MARK: - Private - Boundary Encoding
    private func initialBoundaryData() -> Data {
        BoundaryGenerator.boundaryData(forBoundaryType: .initial, boundary: boundary)
    }
    private func encapsulatedBoundaryData() -> Data {
        BoundaryGenerator.boundaryData(forBoundaryType: .encapsulated, boundary: boundary)
    }
    private func finalBoundaryData() -> Data {
        BoundaryGenerator.boundaryData(forBoundaryType: .final, boundary: boundary)
    }
    // MARK: - Private - Errors
    private func setBodyPartError(withReason reason: AFError.MultipartEncodingFailureReason) {
        guard bodyPartError == nil else { return }
        bodyPartError = AFError.multipartEncodingFailed(reason: reason)
    }
}
#if canImport(UniformTypeIdentifiers)
import UniformTypeIdentifiers
extension MultipartFormData {
    // MARK: - Private - Mime Type
    private func mimeType(forPathExtension pathExtension: String) -> String {
        #if swift(>=5.9)
        if #available(iOS 14, macOS 11, tvOS 14, watchOS 7, visionOS 1, *) {
            return UTType(filenameExtension: pathExtension)?.preferredMIMEType ?? "application/octet-stream"
        } else {
            if
                let id = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, pathExtension as CFString, nil)?.takeRetainedValue(),
                let contentType = UTTypeCopyPreferredTagWithClass(id, kUTTagClassMIMEType)?.takeRetainedValue() {
                return contentType as String
            }
            return "application/octet-stream"
        }
        #else
        if #available(iOS 14, macOS 11, tvOS 14, watchOS 7, *) {
            return UTType(filenameExtension: pathExtension)?.preferredMIMEType ?? "application/octet-stream"
        } else {
            if
                let id = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, pathExtension as CFString, nil)?.takeRetainedValue(),
                let contentType = UTTypeCopyPreferredTagWithClass(id, kUTTagClassMIMEType)?.takeRetainedValue() {
                return contentType as String
            }
            return "application/octet-stream"
        }
        #endif
    }
}
#else
extension MultipartFormData {
    // MARK: - Private - Mime Type
    private func mimeType(forPathExtension pathExtension: String) -> String {
        #if canImport(CoreServices) || canImport(MobileCoreServices)
        if
            let id = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, pathExtension as CFString, nil)?.takeRetainedValue(),
            let contentType = UTTypeCopyPreferredTagWithClass(id, kUTTagClassMIMEType)?.takeRetainedValue() {
            return contentType as String
        }
        #endif
        return "application/octet-stream"
    }
}
#endif
Pods/Alamofire/Source/MultipartUpload.swift
New file
@@ -0,0 +1,89 @@
//
//  MultipartUpload.swift
//
//  Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/)
//
//  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:
//
//  The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
//
//  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.
//
import Foundation
/// Internal type which encapsulates a `MultipartFormData` upload.
final class MultipartUpload {
    lazy var result = Result { try build() }
    private let multipartFormData: Protected<MultipartFormData>
    let encodingMemoryThreshold: UInt64
    let request: URLRequestConvertible
    let fileManager: FileManager
    init(encodingMemoryThreshold: UInt64,
         request: URLRequestConvertible,
         multipartFormData: MultipartFormData) {
        self.encodingMemoryThreshold = encodingMemoryThreshold
        self.request = request
        fileManager = multipartFormData.fileManager
        self.multipartFormData = Protected(multipartFormData)
    }
    func build() throws -> UploadRequest.Uploadable {
        let uploadable: UploadRequest.Uploadable
        if multipartFormData.contentLength < encodingMemoryThreshold {
            let data = try multipartFormData.read { try $0.encode() }
            uploadable = .data(data)
        } else {
            let tempDirectoryURL = fileManager.temporaryDirectory
            let directoryURL = tempDirectoryURL.appendingPathComponent("org.alamofire.manager/multipart.form.data")
            let fileName = UUID().uuidString
            let fileURL = directoryURL.appendingPathComponent(fileName)
            try fileManager.createDirectory(at: directoryURL, withIntermediateDirectories: true, attributes: nil)
            do {
                try multipartFormData.read { try $0.writeEncodedData(to: fileURL) }
            } catch {
                // Cleanup after attempted write if it fails.
                try? fileManager.removeItem(at: fileURL)
                throw error
            }
            uploadable = .file(fileURL, shouldRemove: true)
        }
        return uploadable
    }
}
extension MultipartUpload: UploadConvertible {
    func asURLRequest() throws -> URLRequest {
        var urlRequest = try request.asURLRequest()
        multipartFormData.read { multipartFormData in
            urlRequest.headers.add(.contentType(multipartFormData.contentType))
        }
        return urlRequest
    }
    func createUploadable() throws -> UploadRequest.Uploadable {
        try result.get()
    }
}
Pods/Alamofire/Source/NetworkReachabilityManager.swift
New file
@@ -0,0 +1,292 @@
//
//  NetworkReachabilityManager.swift
//
//  Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/)
//
//  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:
//
//  The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
//
//  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.
//
#if canImport(SystemConfiguration)
import Foundation
import SystemConfiguration
/// The `NetworkReachabilityManager` class listens for reachability changes of hosts and addresses for both cellular and
/// WiFi network interfaces.
///
/// Reachability can be used to determine background information about why a network operation failed, or to retry
/// network requests when a connection is established. It should not be used to prevent a user from initiating a network
/// request, as it's possible that an initial request may be required to establish reachability.
open class NetworkReachabilityManager {
    /// Defines the various states of network reachability.
    public enum NetworkReachabilityStatus {
        /// It is unknown whether the network is reachable.
        case unknown
        /// The network is not reachable.
        case notReachable
        /// The network is reachable on the associated `ConnectionType`.
        case reachable(ConnectionType)
        init(_ flags: SCNetworkReachabilityFlags) {
            guard flags.isActuallyReachable else { self = .notReachable; return }
            var networkStatus: NetworkReachabilityStatus = .reachable(.ethernetOrWiFi)
            if flags.isCellular { networkStatus = .reachable(.cellular) }
            self = networkStatus
        }
        /// Defines the various connection types detected by reachability flags.
        public enum ConnectionType {
            /// The connection type is either over Ethernet or WiFi.
            case ethernetOrWiFi
            /// The connection type is a cellular connection.
            case cellular
        }
    }
    /// A closure executed when the network reachability status changes. The closure takes a single argument: the
    /// network reachability status.
    public typealias Listener = (NetworkReachabilityStatus) -> Void
    /// Default `NetworkReachabilityManager` for the zero address and a `listenerQueue` of `.main`.
    public static let `default` = NetworkReachabilityManager()
    // MARK: - Properties
    /// Whether the network is currently reachable.
    open var isReachable: Bool { isReachableOnCellular || isReachableOnEthernetOrWiFi }
    /// Whether the network is currently reachable over the cellular interface.
    ///
    /// - Note: Using this property to decide whether to make a high or low bandwidth request is not recommended.
    ///         Instead, set the `allowsCellularAccess` on any `URLRequest`s being issued.
    ///
    open var isReachableOnCellular: Bool { status == .reachable(.cellular) }
    /// Whether the network is currently reachable over Ethernet or WiFi interface.
    open var isReachableOnEthernetOrWiFi: Bool { status == .reachable(.ethernetOrWiFi) }
    /// `DispatchQueue` on which reachability will update.
    public let reachabilityQueue = DispatchQueue(label: "org.alamofire.reachabilityQueue")
    /// Flags of the current reachability type, if any.
    open var flags: SCNetworkReachabilityFlags? {
        var flags = SCNetworkReachabilityFlags()
        return SCNetworkReachabilityGetFlags(reachability, &flags) ? flags : nil
    }
    /// The current network reachability status.
    open var status: NetworkReachabilityStatus {
        flags.map(NetworkReachabilityStatus.init) ?? .unknown
    }
    /// Mutable state storage.
    struct MutableState {
        /// A closure executed when the network reachability status changes.
        var listener: Listener?
        /// `DispatchQueue` on which listeners will be called.
        var listenerQueue: DispatchQueue?
        /// Previously calculated status.
        var previousStatus: NetworkReachabilityStatus?
    }
    /// `SCNetworkReachability` instance providing notifications.
    private let reachability: SCNetworkReachability
    /// Protected storage for mutable state.
    private let mutableState = Protected(MutableState())
    // MARK: - Initialization
    /// Creates an instance with the specified host.
    ///
    /// - Note: The `host` value must *not* contain a scheme, just the hostname.
    ///
    /// - Parameters:
    ///   - host:          Host used to evaluate network reachability. Must *not* include the scheme (e.g. `https`).
    public convenience init?(host: String) {
        guard let reachability = SCNetworkReachabilityCreateWithName(nil, host) else { return nil }
        self.init(reachability: reachability)
    }
    /// Creates an instance that monitors the address 0.0.0.0.
    ///
    /// Reachability treats the 0.0.0.0 address as a special token that causes it to monitor the general routing
    /// status of the device, both IPv4 and IPv6.
    public convenience init?() {
        var zero = sockaddr()
        zero.sa_len = UInt8(MemoryLayout<sockaddr>.size)
        zero.sa_family = sa_family_t(AF_INET)
        guard let reachability = SCNetworkReachabilityCreateWithAddress(nil, &zero) else { return nil }
        self.init(reachability: reachability)
    }
    private init(reachability: SCNetworkReachability) {
        self.reachability = reachability
    }
    deinit {
        stopListening()
    }
    // MARK: - Listening
    /// Starts listening for changes in network reachability status.
    ///
    /// - Note: Stops and removes any existing listener.
    ///
    /// - Parameters:
    ///   - queue:    `DispatchQueue` on which to call the `listener` closure. `.main` by default.
    ///   - listener: `Listener` closure called when reachability changes.
    ///
    /// - Returns: `true` if listening was started successfully, `false` otherwise.
    @discardableResult
    open func startListening(onQueue queue: DispatchQueue = .main,
                             onUpdatePerforming listener: @escaping Listener) -> Bool {
        stopListening()
        mutableState.write { state in
            state.listenerQueue = queue
            state.listener = listener
        }
        let weakManager = WeakManager(manager: self)
        var context = SCNetworkReachabilityContext(
            version: 0,
            info: Unmanaged.passUnretained(weakManager).toOpaque(),
            retain: { info in
                let unmanaged = Unmanaged<WeakManager>.fromOpaque(info)
                _ = unmanaged.retain()
                return UnsafeRawPointer(unmanaged.toOpaque())
            },
            release: { info in
                let unmanaged = Unmanaged<WeakManager>.fromOpaque(info)
                unmanaged.release()
            },
            copyDescription: { info in
                let unmanaged = Unmanaged<WeakManager>.fromOpaque(info)
                let weakManager = unmanaged.takeUnretainedValue()
                let description = weakManager.manager?.flags?.readableDescription ?? "nil"
                return Unmanaged.passRetained(description as CFString)
            }
        )
        let callback: SCNetworkReachabilityCallBack = { _, flags, info in
            guard let info = info else { return }
            let weakManager = Unmanaged<WeakManager>.fromOpaque(info).takeUnretainedValue()
            weakManager.manager?.notifyListener(flags)
        }
        let queueAdded = SCNetworkReachabilitySetDispatchQueue(reachability, reachabilityQueue)
        let callbackAdded = SCNetworkReachabilitySetCallback(reachability, callback, &context)
        // Manually call listener to give initial state, since the framework may not.
        if let currentFlags = flags {
            reachabilityQueue.async {
                self.notifyListener(currentFlags)
            }
        }
        return callbackAdded && queueAdded
    }
    /// Stops listening for changes in network reachability status.
    open func stopListening() {
        SCNetworkReachabilitySetCallback(reachability, nil, nil)
        SCNetworkReachabilitySetDispatchQueue(reachability, nil)
        mutableState.write { state in
            state.listener = nil
            state.listenerQueue = nil
            state.previousStatus = nil
        }
    }
    // MARK: - Internal - Listener Notification
    /// Calls the `listener` closure of the `listenerQueue` if the computed status hasn't changed.
    ///
    /// - Note: Should only be called from the `reachabilityQueue`.
    ///
    /// - Parameter flags: `SCNetworkReachabilityFlags` to use to calculate the status.
    func notifyListener(_ flags: SCNetworkReachabilityFlags) {
        let newStatus = NetworkReachabilityStatus(flags)
        mutableState.write { state in
            guard state.previousStatus != newStatus else { return }
            state.previousStatus = newStatus
            let listener = state.listener
            state.listenerQueue?.async { listener?(newStatus) }
        }
    }
    private final class WeakManager {
        weak var manager: NetworkReachabilityManager?
        init(manager: NetworkReachabilityManager?) {
            self.manager = manager
        }
    }
}
// MARK: -
extension NetworkReachabilityManager.NetworkReachabilityStatus: Equatable {}
extension SCNetworkReachabilityFlags {
    var isReachable: Bool { contains(.reachable) }
    var isConnectionRequired: Bool { contains(.connectionRequired) }
    var canConnectAutomatically: Bool { contains(.connectionOnDemand) || contains(.connectionOnTraffic) }
    var canConnectWithoutUserInteraction: Bool { canConnectAutomatically && !contains(.interventionRequired) }
    var isActuallyReachable: Bool { isReachable && (!isConnectionRequired || canConnectWithoutUserInteraction) }
    var isCellular: Bool {
        #if os(iOS) || os(tvOS) || (swift(>=5.9) && os(visionOS))
        return contains(.isWWAN)
        #else
        return false
        #endif
    }
    /// Human readable `String` for all states, to help with debugging.
    var readableDescription: String {
        let W = isCellular ? "W" : "-"
        let R = isReachable ? "R" : "-"
        let c = isConnectionRequired ? "c" : "-"
        let t = contains(.transientConnection) ? "t" : "-"
        let i = contains(.interventionRequired) ? "i" : "-"
        let C = contains(.connectionOnTraffic) ? "C" : "-"
        let D = contains(.connectionOnDemand) ? "D" : "-"
        let l = contains(.isLocalAddress) ? "l" : "-"
        let d = contains(.isDirect) ? "d" : "-"
        let a = contains(.connectionAutomatic) ? "a" : "-"
        return "\(W)\(R) \(c)\(t)\(i)\(C)\(D)\(l)\(d)\(a)"
    }
}
#endif
Pods/Alamofire/Source/Notifications.swift
New file
@@ -0,0 +1,115 @@
//
//  Notifications.swift
//
//  Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/)
//
//  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:
//
//  The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
//
//  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.
//
import Foundation
extension Request {
    /// Posted when a `Request` is resumed. The `Notification` contains the resumed `Request`.
    public static let didResumeNotification = Notification.Name(rawValue: "org.alamofire.notification.name.request.didResume")
    /// Posted when a `Request` is suspended. The `Notification` contains the suspended `Request`.
    public static let didSuspendNotification = Notification.Name(rawValue: "org.alamofire.notification.name.request.didSuspend")
    /// Posted when a `Request` is cancelled. The `Notification` contains the cancelled `Request`.
    public static let didCancelNotification = Notification.Name(rawValue: "org.alamofire.notification.name.request.didCancel")
    /// Posted when a `Request` is finished. The `Notification` contains the completed `Request`.
    public static let didFinishNotification = Notification.Name(rawValue: "org.alamofire.notification.name.request.didFinish")
    /// Posted when a `URLSessionTask` is resumed. The `Notification` contains the `Request` associated with the `URLSessionTask`.
    public static let didResumeTaskNotification = Notification.Name(rawValue: "org.alamofire.notification.name.request.didResumeTask")
    /// Posted when a `URLSessionTask` is suspended. The `Notification` contains the `Request` associated with the `URLSessionTask`.
    public static let didSuspendTaskNotification = Notification.Name(rawValue: "org.alamofire.notification.name.request.didSuspendTask")
    /// Posted when a `URLSessionTask` is cancelled. The `Notification` contains the `Request` associated with the `URLSessionTask`.
    public static let didCancelTaskNotification = Notification.Name(rawValue: "org.alamofire.notification.name.request.didCancelTask")
    /// Posted when a `URLSessionTask` is completed. The `Notification` contains the `Request` associated with the `URLSessionTask`.
    public static let didCompleteTaskNotification = Notification.Name(rawValue: "org.alamofire.notification.name.request.didCompleteTask")
}
// MARK: -
extension Notification {
    /// The `Request` contained by the instance's `userInfo`, `nil` otherwise.
    public var request: Request? {
        userInfo?[String.requestKey] as? Request
    }
    /// Convenience initializer for a `Notification` containing a `Request` payload.
    ///
    /// - Parameters:
    ///   - name:    The name of the notification.
    ///   - request: The `Request` payload.
    init(name: Notification.Name, request: Request) {
        self.init(name: name, object: nil, userInfo: [String.requestKey: request])
    }
}
extension NotificationCenter {
    /// Convenience function for posting notifications with `Request` payloads.
    ///
    /// - Parameters:
    ///   - name:    The name of the notification.
    ///   - request: The `Request` payload.
    func postNotification(named name: Notification.Name, with request: Request) {
        let notification = Notification(name: name, request: request)
        post(notification)
    }
}
extension String {
    /// User info dictionary key representing the `Request` associated with the notification.
    fileprivate static let requestKey = "org.alamofire.notification.key.request"
}
/// `EventMonitor` that provides Alamofire's notifications.
public final class AlamofireNotifications: EventMonitor {
    public func requestDidResume(_ request: Request) {
        NotificationCenter.default.postNotification(named: Request.didResumeNotification, with: request)
    }
    public func requestDidSuspend(_ request: Request) {
        NotificationCenter.default.postNotification(named: Request.didSuspendNotification, with: request)
    }
    public func requestDidCancel(_ request: Request) {
        NotificationCenter.default.postNotification(named: Request.didCancelNotification, with: request)
    }
    public func requestDidFinish(_ request: Request) {
        NotificationCenter.default.postNotification(named: Request.didFinishNotification, with: request)
    }
    public func request(_ request: Request, didResumeTask task: URLSessionTask) {
        NotificationCenter.default.postNotification(named: Request.didResumeTaskNotification, with: request)
    }
    public func request(_ request: Request, didSuspendTask task: URLSessionTask) {
        NotificationCenter.default.postNotification(named: Request.didSuspendTaskNotification, with: request)
    }
    public func request(_ request: Request, didCancelTask task: URLSessionTask) {
        NotificationCenter.default.postNotification(named: Request.didCancelTaskNotification, with: request)
    }
    public func request(_ request: Request, didCompleteTask task: URLSessionTask, with error: AFError?) {
        NotificationCenter.default.postNotification(named: Request.didCompleteTaskNotification, with: request)
    }
}
Pods/Alamofire/Source/OperationQueue+Alamofire.swift
New file
@@ -0,0 +1,49 @@
//
//  OperationQueue+Alamofire.swift
//
//  Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/)
//
//  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:
//
//  The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
//
//  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.
//
import Foundation
extension OperationQueue {
    /// Creates an instance using the provided parameters.
    ///
    /// - Parameters:
    ///   - qualityOfService:            `QualityOfService` to be applied to the queue. `.default` by default.
    ///   - maxConcurrentOperationCount: Maximum concurrent operations.
    ///                                  `OperationQueue.defaultMaxConcurrentOperationCount` by default.
    ///   - underlyingQueue: Underlying  `DispatchQueue`. `nil` by default.
    ///   - name:                        Name for the queue. `nil` by default.
    ///   - startSuspended:              Whether the queue starts suspended. `false` by default.
    convenience init(qualityOfService: QualityOfService = .default,
                     maxConcurrentOperationCount: Int = OperationQueue.defaultMaxConcurrentOperationCount,
                     underlyingQueue: DispatchQueue? = nil,
                     name: String? = nil,
                     startSuspended: Bool = false) {
        self.init()
        self.qualityOfService = qualityOfService
        self.maxConcurrentOperationCount = maxConcurrentOperationCount
        self.underlyingQueue = underlyingQueue
        self.name = name
        isSuspended = startSuspended
    }
}
Pods/Alamofire/Source/ParameterEncoder.swift
New file
@@ -0,0 +1,213 @@
//
//  ParameterEncoder.swift
//
//  Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/)
//
//  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:
//
//  The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
//
//  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.
//
import Foundation
/// A type that can encode any `Encodable` type into a `URLRequest`.
public protocol ParameterEncoder {
    /// Encode the provided `Encodable` parameters into `request`.
    ///
    /// - Parameters:
    ///   - parameters: The `Encodable` parameter value.
    ///   - request:    The `URLRequest` into which to encode the parameters.
    ///
    /// - Returns:      A `URLRequest` with the result of the encoding.
    /// - Throws:       An `Error` when encoding fails. For Alamofire provided encoders, this will be an instance of
    ///                 `AFError.parameterEncoderFailed` with an associated `ParameterEncoderFailureReason`.
    func encode<Parameters: Encodable>(_ parameters: Parameters?, into request: URLRequest) throws -> URLRequest
}
/// A `ParameterEncoder` that encodes types as JSON body data.
///
/// If no `Content-Type` header is already set on the provided `URLRequest`s, it's set to `application/json`.
open class JSONParameterEncoder: ParameterEncoder {
    /// Returns an encoder with default parameters.
    public static var `default`: JSONParameterEncoder { JSONParameterEncoder() }
    /// Returns an encoder with `JSONEncoder.outputFormatting` set to `.prettyPrinted`.
    public static var prettyPrinted: JSONParameterEncoder {
        let encoder = JSONEncoder()
        encoder.outputFormatting = .prettyPrinted
        return JSONParameterEncoder(encoder: encoder)
    }
    /// Returns an encoder with `JSONEncoder.outputFormatting` set to `.sortedKeys`.
    @available(macOS 10.13, iOS 11.0, tvOS 11.0, watchOS 4.0, *)
    public static var sortedKeys: JSONParameterEncoder {
        let encoder = JSONEncoder()
        encoder.outputFormatting = .sortedKeys
        return JSONParameterEncoder(encoder: encoder)
    }
    /// `JSONEncoder` used to encode parameters.
    public let encoder: JSONEncoder
    /// Creates an instance with the provided `JSONEncoder`.
    ///
    /// - Parameter encoder: The `JSONEncoder`. `JSONEncoder()` by default.
    public init(encoder: JSONEncoder = JSONEncoder()) {
        self.encoder = encoder
    }
    open func encode<Parameters: Encodable>(_ parameters: Parameters?,
                                            into request: URLRequest) throws -> URLRequest {
        guard let parameters = parameters else { return request }
        var request = request
        do {
            let data = try encoder.encode(parameters)
            request.httpBody = data
            if request.headers["Content-Type"] == nil {
                request.headers.update(.contentType("application/json"))
            }
        } catch {
            throw AFError.parameterEncodingFailed(reason: .jsonEncodingFailed(error: error))
        }
        return request
    }
}
extension ParameterEncoder where Self == JSONParameterEncoder {
    /// Provides a default `JSONParameterEncoder` instance.
    public static var json: JSONParameterEncoder { JSONParameterEncoder() }
    /// Creates a `JSONParameterEncoder` using the provided `JSONEncoder`.
    ///
    /// - Parameter encoder: `JSONEncoder` used to encode parameters. `JSONEncoder()` by default.
    /// - Returns:           The `JSONParameterEncoder`.
    public static func json(encoder: JSONEncoder = JSONEncoder()) -> JSONParameterEncoder {
        JSONParameterEncoder(encoder: encoder)
    }
}
/// A `ParameterEncoder` that encodes types as URL-encoded query strings to be set on the URL or as body data, depending
/// on the `Destination` set.
///
/// If no `Content-Type` header is already set on the provided `URLRequest`s, it will be set to
/// `application/x-www-form-urlencoded; charset=utf-8`.
///
/// Encoding behavior can be customized by passing an instance of `URLEncodedFormEncoder` to the initializer.
open class URLEncodedFormParameterEncoder: ParameterEncoder {
    /// Defines where the URL-encoded string should be set for each `URLRequest`.
    public enum Destination {
        /// Applies the encoded query string to any existing query string for `.get`, `.head`, and `.delete` request.
        /// Sets it to the `httpBody` for all other methods.
        case methodDependent
        /// Applies the encoded query string to any existing query string from the `URLRequest`.
        case queryString
        /// Applies the encoded query string to the `httpBody` of the `URLRequest`.
        case httpBody
        /// Determines whether the URL-encoded string should be applied to the `URLRequest`'s `url`.
        ///
        /// - Parameter method: The `HTTPMethod`.
        ///
        /// - Returns:          Whether the URL-encoded string should be applied to a `URL`.
        func encodesParametersInURL(for method: HTTPMethod) -> Bool {
            switch self {
            case .methodDependent: return [.get, .head, .delete].contains(method)
            case .queryString: return true
            case .httpBody: return false
            }
        }
    }
    /// Returns an encoder with default parameters.
    public static var `default`: URLEncodedFormParameterEncoder { URLEncodedFormParameterEncoder() }
    /// The `URLEncodedFormEncoder` to use.
    public let encoder: URLEncodedFormEncoder
    /// The `Destination` for the URL-encoded string.
    public let destination: Destination
    /// Creates an instance with the provided `URLEncodedFormEncoder` instance and `Destination` value.
    ///
    /// - Parameters:
    ///   - encoder:     The `URLEncodedFormEncoder`. `URLEncodedFormEncoder()` by default.
    ///   - destination: The `Destination`. `.methodDependent` by default.
    public init(encoder: URLEncodedFormEncoder = URLEncodedFormEncoder(), destination: Destination = .methodDependent) {
        self.encoder = encoder
        self.destination = destination
    }
    open func encode<Parameters: Encodable>(_ parameters: Parameters?,
                                            into request: URLRequest) throws -> URLRequest {
        guard let parameters = parameters else { return request }
        var request = request
        guard let url = request.url else {
            throw AFError.parameterEncoderFailed(reason: .missingRequiredComponent(.url))
        }
        guard let method = request.method else {
            let rawValue = request.method?.rawValue ?? "nil"
            throw AFError.parameterEncoderFailed(reason: .missingRequiredComponent(.httpMethod(rawValue: rawValue)))
        }
        if destination.encodesParametersInURL(for: method),
           var components = URLComponents(url: url, resolvingAgainstBaseURL: false) {
            let query: String = try Result<String, Error> { try encoder.encode(parameters) }
                .mapError { AFError.parameterEncoderFailed(reason: .encoderFailed(error: $0)) }.get()
            let newQueryString = [components.percentEncodedQuery, query].compactMap { $0 }.joinedWithAmpersands()
            components.percentEncodedQuery = newQueryString.isEmpty ? nil : newQueryString
            guard let newURL = components.url else {
                throw AFError.parameterEncoderFailed(reason: .missingRequiredComponent(.url))
            }
            request.url = newURL
        } else {
            if request.headers["Content-Type"] == nil {
                request.headers.update(.contentType("application/x-www-form-urlencoded; charset=utf-8"))
            }
            request.httpBody = try Result<Data, Error> { try encoder.encode(parameters) }
                .mapError { AFError.parameterEncoderFailed(reason: .encoderFailed(error: $0)) }.get()
        }
        return request
    }
}
extension ParameterEncoder where Self == URLEncodedFormParameterEncoder {
    /// Provides a default `URLEncodedFormParameterEncoder` instance.
    public static var urlEncodedForm: URLEncodedFormParameterEncoder { URLEncodedFormParameterEncoder() }
    /// Creates a `URLEncodedFormParameterEncoder` with the provided encoder and destination.
    ///
    /// - Parameters:
    ///   - encoder:     `URLEncodedFormEncoder` used to encode the parameters. `URLEncodedFormEncoder()` by default.
    ///   - destination: `Destination` to which to encode the parameters. `.methodDependent` by default.
    /// - Returns:       The `URLEncodedFormParameterEncoder`.
    public static func urlEncodedForm(encoder: URLEncodedFormEncoder = URLEncodedFormEncoder(),
                                      destination: URLEncodedFormParameterEncoder.Destination = .methodDependent) -> URLEncodedFormParameterEncoder {
        URLEncodedFormParameterEncoder(encoder: encoder, destination: destination)
    }
}
Pods/Alamofire/Source/ParameterEncoding.swift
New file
@@ -0,0 +1,346 @@
//
//  ParameterEncoding.swift
//
//  Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/)
//
//  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:
//
//  The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
//
//  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.
//
import Foundation
/// A dictionary of parameters to apply to a `URLRequest`.
public typealias Parameters = [String: Any]
/// A type used to define how a set of parameters are applied to a `URLRequest`.
public protocol ParameterEncoding {
    /// Creates a `URLRequest` by encoding parameters and applying them on the passed request.
    ///
    /// - Parameters:
    ///   - urlRequest: `URLRequestConvertible` value onto which parameters will be encoded.
    ///   - parameters: `Parameters` to encode onto the request.
    ///
    /// - Returns:      The encoded `URLRequest`.
    /// - Throws:       Any `Error` produced during parameter encoding.
    func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest
}
// MARK: -
/// Creates a url-encoded query string to be set as or appended to any existing URL query string or set as the HTTP
/// body of the URL request. Whether the query string is set or appended to any existing URL query string or set as
/// the HTTP body depends on the destination of the encoding.
///
/// The `Content-Type` HTTP header field of an encoded request with HTTP body is set to
/// `application/x-www-form-urlencoded; charset=utf-8`.
///
/// There is no published specification for how to encode collection types. By default the convention of appending
/// `[]` to the key for array values (`foo[]=1&foo[]=2`), and appending the key surrounded by square brackets for
/// nested dictionary values (`foo[bar]=baz`) is used. Optionally, `ArrayEncoding` can be used to omit the
/// square brackets appended to array keys.
///
/// `BoolEncoding` can be used to configure how boolean values are encoded. The default behavior is to encode
/// `true` as 1 and `false` as 0.
public struct URLEncoding: ParameterEncoding {
    // MARK: Helper Types
    /// Defines whether the url-encoded query string is applied to the existing query string or HTTP body of the
    /// resulting URL request.
    public enum Destination {
        /// Applies encoded query string result to existing query string for `GET`, `HEAD` and `DELETE` requests and
        /// sets as the HTTP body for requests with any other HTTP method.
        case methodDependent
        /// Sets or appends encoded query string result to existing query string.
        case queryString
        /// Sets encoded query string result as the HTTP body of the URL request.
        case httpBody
        func encodesParametersInURL(for method: HTTPMethod) -> Bool {
            switch self {
            case .methodDependent: return [.get, .head, .delete].contains(method)
            case .queryString: return true
            case .httpBody: return false
            }
        }
    }
    /// Configures how `Array` parameters are encoded.
    public enum ArrayEncoding {
        /// An empty set of square brackets is appended to the key for every value. This is the default behavior.
        case brackets
        /// No brackets are appended. The key is encoded as is.
        case noBrackets
        /// Brackets containing the item index are appended. This matches the jQuery and Node.js behavior.
        case indexInBrackets
        /// Provide a custom array key encoding with the given closure.
        case custom((_ key: String, _ index: Int) -> String)
        func encode(key: String, atIndex index: Int) -> String {
            switch self {
            case .brackets:
                return "\(key)[]"
            case .noBrackets:
                return key
            case .indexInBrackets:
                return "\(key)[\(index)]"
            case let .custom(encoding):
                return encoding(key, index)
            }
        }
    }
    /// Configures how `Bool` parameters are encoded.
    public enum BoolEncoding {
        /// Encode `true` as `1` and `false` as `0`. This is the default behavior.
        case numeric
        /// Encode `true` and `false` as string literals.
        case literal
        func encode(value: Bool) -> String {
            switch self {
            case .numeric:
                return value ? "1" : "0"
            case .literal:
                return value ? "true" : "false"
            }
        }
    }
    // MARK: Properties
    /// Returns a default `URLEncoding` instance with a `.methodDependent` destination.
    public static var `default`: URLEncoding { URLEncoding() }
    /// Returns a `URLEncoding` instance with a `.queryString` destination.
    public static var queryString: URLEncoding { URLEncoding(destination: .queryString) }
    /// Returns a `URLEncoding` instance with an `.httpBody` destination.
    public static var httpBody: URLEncoding { URLEncoding(destination: .httpBody) }
    /// The destination defining where the encoded query string is to be applied to the URL request.
    public let destination: Destination
    /// The encoding to use for `Array` parameters.
    public let arrayEncoding: ArrayEncoding
    /// The encoding to use for `Bool` parameters.
    public let boolEncoding: BoolEncoding
    // MARK: Initialization
    /// Creates an instance using the specified parameters.
    ///
    /// - Parameters:
    ///   - destination:   `Destination` defining where the encoded query string will be applied. `.methodDependent` by
    ///                    default.
    ///   - arrayEncoding: `ArrayEncoding` to use. `.brackets` by default.
    ///   - boolEncoding:  `BoolEncoding` to use. `.numeric` by default.
    public init(destination: Destination = .methodDependent,
                arrayEncoding: ArrayEncoding = .brackets,
                boolEncoding: BoolEncoding = .numeric) {
        self.destination = destination
        self.arrayEncoding = arrayEncoding
        self.boolEncoding = boolEncoding
    }
    // MARK: Encoding
    public func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest {
        var urlRequest = try urlRequest.asURLRequest()
        guard let parameters = parameters else { return urlRequest }
        if let method = urlRequest.method, destination.encodesParametersInURL(for: method) {
            guard let url = urlRequest.url else {
                throw AFError.parameterEncodingFailed(reason: .missingURL)
            }
            if var urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false), !parameters.isEmpty {
                let percentEncodedQuery = (urlComponents.percentEncodedQuery.map { $0 + "&" } ?? "") + query(parameters)
                urlComponents.percentEncodedQuery = percentEncodedQuery
                urlRequest.url = urlComponents.url
            }
        } else {
            if urlRequest.headers["Content-Type"] == nil {
                urlRequest.headers.update(.contentType("application/x-www-form-urlencoded; charset=utf-8"))
            }
            urlRequest.httpBody = Data(query(parameters).utf8)
        }
        return urlRequest
    }
    /// Creates a percent-escaped, URL encoded query string components from the given key-value pair recursively.
    ///
    /// - Parameters:
    ///   - key:   Key of the query component.
    ///   - value: Value of the query component.
    ///
    /// - Returns: The percent-escaped, URL encoded query string components.
    public func queryComponents(fromKey key: String, value: Any) -> [(String, String)] {
        var components: [(String, String)] = []
        switch value {
        case let dictionary as [String: Any]:
            for (nestedKey, value) in dictionary {
                components += queryComponents(fromKey: "\(key)[\(nestedKey)]", value: value)
            }
        case let array as [Any]:
            for (index, value) in array.enumerated() {
                components += queryComponents(fromKey: arrayEncoding.encode(key: key, atIndex: index), value: value)
            }
        case let number as NSNumber:
            if number.isBool {
                components.append((escape(key), escape(boolEncoding.encode(value: number.boolValue))))
            } else {
                components.append((escape(key), escape("\(number)")))
            }
        case let bool as Bool:
            components.append((escape(key), escape(boolEncoding.encode(value: bool))))
        default:
            components.append((escape(key), escape("\(value)")))
        }
        return components
    }
    /// Creates a percent-escaped string following RFC 3986 for a query string key or value.
    ///
    /// - Parameter string: `String` to be percent-escaped.
    ///
    /// - Returns:          The percent-escaped `String`.
    public func escape(_ string: String) -> String {
        string.addingPercentEncoding(withAllowedCharacters: .afURLQueryAllowed) ?? string
    }
    private func query(_ parameters: [String: Any]) -> String {
        var components: [(String, String)] = []
        for key in parameters.keys.sorted(by: <) {
            let value = parameters[key]!
            components += queryComponents(fromKey: key, value: value)
        }
        return components.map { "\($0)=\($1)" }.joined(separator: "&")
    }
}
// MARK: -
/// Uses `JSONSerialization` to create a JSON representation of the parameters object, which is set as the body of the
/// request. The `Content-Type` HTTP header field of an encoded request is set to `application/json`.
public struct JSONEncoding: ParameterEncoding {
    public enum Error: Swift.Error {
        case invalidJSONObject
    }
    // MARK: Properties
    /// Returns a `JSONEncoding` instance with default writing options.
    public static var `default`: JSONEncoding { JSONEncoding() }
    /// Returns a `JSONEncoding` instance with `.prettyPrinted` writing options.
    public static var prettyPrinted: JSONEncoding { JSONEncoding(options: .prettyPrinted) }
    /// The options for writing the parameters as JSON data.
    public let options: JSONSerialization.WritingOptions
    // MARK: Initialization
    /// Creates an instance using the specified `WritingOptions`.
    ///
    /// - Parameter options: `JSONSerialization.WritingOptions` to use.
    public init(options: JSONSerialization.WritingOptions = []) {
        self.options = options
    }
    // MARK: Encoding
    public func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest {
        var urlRequest = try urlRequest.asURLRequest()
        guard let parameters = parameters else { return urlRequest }
        guard JSONSerialization.isValidJSONObject(parameters) else {
            throw AFError.parameterEncodingFailed(reason: .jsonEncodingFailed(error: Error.invalidJSONObject))
        }
        do {
            let data = try JSONSerialization.data(withJSONObject: parameters, options: options)
            if urlRequest.headers["Content-Type"] == nil {
                urlRequest.headers.update(.contentType("application/json"))
            }
            urlRequest.httpBody = data
        } catch {
            throw AFError.parameterEncodingFailed(reason: .jsonEncodingFailed(error: error))
        }
        return urlRequest
    }
    /// Encodes any JSON compatible object into a `URLRequest`.
    ///
    /// - Parameters:
    ///   - urlRequest: `URLRequestConvertible` value into which the object will be encoded.
    ///   - jsonObject: `Any` value (must be JSON compatible` to be encoded into the `URLRequest`. `nil` by default.
    ///
    /// - Returns:      The encoded `URLRequest`.
    /// - Throws:       Any `Error` produced during encoding.
    public func encode(_ urlRequest: URLRequestConvertible, withJSONObject jsonObject: Any? = nil) throws -> URLRequest {
        var urlRequest = try urlRequest.asURLRequest()
        guard let jsonObject = jsonObject else { return urlRequest }
        guard JSONSerialization.isValidJSONObject(jsonObject) else {
            throw AFError.parameterEncodingFailed(reason: .jsonEncodingFailed(error: Error.invalidJSONObject))
        }
        do {
            let data = try JSONSerialization.data(withJSONObject: jsonObject, options: options)
            if urlRequest.headers["Content-Type"] == nil {
                urlRequest.headers.update(.contentType("application/json"))
            }
            urlRequest.httpBody = data
        } catch {
            throw AFError.parameterEncodingFailed(reason: .jsonEncodingFailed(error: error))
        }
        return urlRequest
    }
}
extension JSONEncoding.Error {
    public var localizedDescription: String {
        """
        Invalid JSON object provided for parameter or object encoding. \
        This is most likely due to a value which can't be represented in Objective-C.
        """
    }
}
// MARK: -
extension NSNumber {
    fileprivate var isBool: Bool {
        // Use Obj-C type encoding to check whether the underlying type is a `Bool`, as it's guaranteed as part of
        // swift-corelibs-foundation, per [this discussion on the Swift forums](https://forums.swift.org/t/alamofire-on-linux-possible-but-not-release-ready/34553/22).
        String(cString: objCType) == "c"
    }
}
Pods/Alamofire/Source/Protected.swift
New file
@@ -0,0 +1,168 @@
//
//  Protected.swift
//
//  Copyright (c) 2014-2020 Alamofire Software Foundation (http://alamofire.org/)
//
//  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:
//
//  The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
//
//  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.
//
import Foundation
private protocol Lock {
    func lock()
    func unlock()
}
extension Lock {
    /// Executes a closure returning a value while acquiring the lock.
    ///
    /// - Parameter closure: The closure to run.
    ///
    /// - Returns:           The value the closure generated.
    func around<T>(_ closure: () throws -> T) rethrows -> T {
        lock(); defer { unlock() }
        return try closure()
    }
    /// Execute a closure while acquiring the lock.
    ///
    /// - Parameter closure: The closure to run.
    func around(_ closure: () throws -> Void) rethrows {
        lock(); defer { unlock() }
        try closure()
    }
}
#if canImport(Darwin)
/// An `os_unfair_lock` wrapper.
final class UnfairLock: Lock {
    private let unfairLock: os_unfair_lock_t
    init() {
        unfairLock = .allocate(capacity: 1)
        unfairLock.initialize(to: os_unfair_lock())
    }
    deinit {
        unfairLock.deinitialize(count: 1)
        unfairLock.deallocate()
    }
    fileprivate func lock() {
        os_unfair_lock_lock(unfairLock)
    }
    fileprivate func unlock() {
        os_unfair_lock_unlock(unfairLock)
    }
}
#elseif canImport(Foundation)
extension NSLock: Lock {}
#else
#error("This platform needs a Lock-conforming type without Foundation.")
#endif
/// A thread-safe wrapper around a value.
@dynamicMemberLookup
final class Protected<Value> {
    #if canImport(Darwin)
    private let lock = UnfairLock()
    #elseif canImport(Foundation)
    private let lock = NSLock()
    #else
    #error("This platform needs a Lock-conforming type without Foundation.")
    #endif
    private var value: Value
    init(_ value: Value) {
        self.value = value
    }
    /// Synchronously read or transform the contained value.
    ///
    /// - Parameter closure: The closure to execute.
    ///
    /// - Returns:           The return value of the closure passed.
    func read<U>(_ closure: (Value) throws -> U) rethrows -> U {
        try lock.around { try closure(self.value) }
    }
    /// Synchronously modify the protected value.
    ///
    /// - Parameter closure: The closure to execute.
    ///
    /// - Returns:           The modified value.
    @discardableResult
    func write<U>(_ closure: (inout Value) throws -> U) rethrows -> U {
        try lock.around { try closure(&self.value) }
    }
    /// Synchronously update the protected value.
    ///
    /// - Parameter value: The `Value`.
    func write(_ value: Value) {
        write { $0 = value }
    }
    subscript<Property>(dynamicMember keyPath: WritableKeyPath<Value, Property>) -> Property {
        get { lock.around { value[keyPath: keyPath] } }
        set { lock.around { value[keyPath: keyPath] = newValue } }
    }
    subscript<Property>(dynamicMember keyPath: KeyPath<Value, Property>) -> Property {
        lock.around { value[keyPath: keyPath] }
    }
}
extension Protected where Value == Request.MutableState {
    /// Attempts to transition to the passed `State`.
    ///
    /// - Parameter state: The `State` to attempt transition to.
    ///
    /// - Returns:         Whether the transition occurred.
    func attemptToTransitionTo(_ state: Request.State) -> Bool {
        lock.around {
            guard value.state.canTransitionTo(state) else { return false }
            value.state = state
            return true
        }
    }
    /// Perform a closure while locked with the provided `Request.State`.
    ///
    /// - Parameter perform: The closure to perform while locked.
    func withState(perform: (Request.State) -> Void) {
        lock.around { perform(value.state) }
    }
}
extension Protected: Equatable where Value: Equatable {
    static func ==(lhs: Protected<Value>, rhs: Protected<Value>) -> Bool {
        lhs.read { left in rhs.read { right in left == right }}
    }
}
extension Protected: Hashable where Value: Hashable {
    func hash(into hasher: inout Hasher) {
        read { hasher.combine($0) }
    }
}
Pods/Alamofire/Source/RedirectHandler.swift
New file
@@ -0,0 +1,111 @@
//
//  RedirectHandler.swift
//
//  Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/)
//
//  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:
//
//  The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
//
//  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.
//
import Foundation
/// A type that handles how an HTTP redirect response from a remote server should be redirected to the new request.
public protocol RedirectHandler {
    /// Determines how the HTTP redirect response should be redirected to the new request.
    ///
    /// The `completion` closure should be passed one of three possible options:
    ///
    ///   1. The new request specified by the redirect (this is the most common use case).
    ///   2. A modified version of the new request (you may want to route it somewhere else).
    ///   3. A `nil` value to deny the redirect request and return the body of the redirect response.
    ///
    /// - Parameters:
    ///   - task:       The `URLSessionTask` whose request resulted in a redirect.
    ///   - request:    The `URLRequest` to the new location specified by the redirect response.
    ///   - response:   The `HTTPURLResponse` containing the server's response to the original request.
    ///   - completion: The closure to execute containing the new `URLRequest`, a modified `URLRequest`, or `nil`.
    func task(_ task: URLSessionTask,
              willBeRedirectedTo request: URLRequest,
              for response: HTTPURLResponse,
              completion: @escaping (URLRequest?) -> Void)
}
// MARK: -
/// `Redirector` is a convenience `RedirectHandler` making it easy to follow, not follow, or modify a redirect.
public struct Redirector {
    /// Defines the behavior of the `Redirector` type.
    public enum Behavior {
        /// Follow the redirect as defined in the response.
        case follow
        /// Do not follow the redirect defined in the response.
        case doNotFollow
        /// Modify the redirect request defined in the response.
        case modify((URLSessionTask, URLRequest, HTTPURLResponse) -> URLRequest?)
    }
    /// Returns a `Redirector` with a `.follow` `Behavior`.
    public static let follow = Redirector(behavior: .follow)
    /// Returns a `Redirector` with a `.doNotFollow` `Behavior`.
    public static let doNotFollow = Redirector(behavior: .doNotFollow)
    /// The `Behavior` of the `Redirector`.
    public let behavior: Behavior
    /// Creates a `Redirector` instance from the `Behavior`.
    ///
    /// - Parameter behavior: The `Behavior`.
    public init(behavior: Behavior) {
        self.behavior = behavior
    }
}
// MARK: -
extension Redirector: RedirectHandler {
    public func task(_ task: URLSessionTask,
                     willBeRedirectedTo request: URLRequest,
                     for response: HTTPURLResponse,
                     completion: @escaping (URLRequest?) -> Void) {
        switch behavior {
        case .follow:
            completion(request)
        case .doNotFollow:
            completion(nil)
        case let .modify(closure):
            let request = closure(task, request, response)
            completion(request)
        }
    }
}
extension RedirectHandler where Self == Redirector {
    /// Provides a `Redirector` which follows redirects. Equivalent to `Redirector.follow`.
    public static var follow: Redirector { .follow }
    /// Provides a `Redirector` which does not follow redirects. Equivalent to `Redirector.doNotFollow`.
    public static var doNotFollow: Redirector { .doNotFollow }
    /// Creates a `Redirector` which modifies the redirected `URLRequest` using the provided closure.
    ///
    /// - Parameter closure: Closure used to modify the redirect.
    /// - Returns:           The `Redirector`.
    public static func modify(using closure: @escaping (URLSessionTask, URLRequest, HTTPURLResponse) -> URLRequest?) -> Redirector {
        Redirector(behavior: .modify(closure))
    }
}
Pods/Alamofire/Source/Request.swift
New file
@@ -0,0 +1,2066 @@
//
//  Request.swift
//
//  Copyright (c) 2014-2020 Alamofire Software Foundation (http://alamofire.org/)
//
//  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:
//
//  The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
//
//  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.
//
import Foundation
/// `Request` is the common superclass of all Alamofire request types and provides common state, delegate, and callback
/// handling.
public class Request {
    /// State of the `Request`, with managed transitions between states set when calling `resume()`, `suspend()`, or
    /// `cancel()` on the `Request`.
    public enum State {
        /// Initial state of the `Request`.
        case initialized
        /// `State` set when `resume()` is called. Any tasks created for the `Request` will have `resume()` called on
        /// them in this state.
        case resumed
        /// `State` set when `suspend()` is called. Any tasks created for the `Request` will have `suspend()` called on
        /// them in this state.
        case suspended
        /// `State` set when `cancel()` is called. Any tasks created for the `Request` will have `cancel()` called on
        /// them. Unlike `resumed` or `suspended`, once in the `cancelled` state, the `Request` can no longer transition
        /// to any other state.
        case cancelled
        /// `State` set when all response serialization completion closures have been cleared on the `Request` and
        /// enqueued on their respective queues.
        case finished
        /// Determines whether `self` can be transitioned to the provided `State`.
        func canTransitionTo(_ state: State) -> Bool {
            switch (self, state) {
            case (.initialized, _):
                return true
            case (_, .initialized), (.cancelled, _), (.finished, _):
                return false
            case (.resumed, .cancelled), (.suspended, .cancelled), (.resumed, .suspended), (.suspended, .resumed):
                return true
            case (.suspended, .suspended), (.resumed, .resumed):
                return false
            case (_, .finished):
                return true
            }
        }
    }
    // MARK: - Initial State
    /// `UUID` providing a unique identifier for the `Request`, used in the `Hashable` and `Equatable` conformances.
    public let id: UUID
    /// The serial queue for all internal async actions.
    public let underlyingQueue: DispatchQueue
    /// The queue used for all serialization actions. By default it's a serial queue that targets `underlyingQueue`.
    public let serializationQueue: DispatchQueue
    /// `EventMonitor` used for event callbacks.
    public let eventMonitor: EventMonitor?
    /// The `Request`'s interceptor.
    public let interceptor: RequestInterceptor?
    /// The `Request`'s delegate.
    public private(set) weak var delegate: RequestDelegate?
    // MARK: - Mutable State
    /// Type encapsulating all mutable state that may need to be accessed from anything other than the `underlyingQueue`.
    struct MutableState {
        /// State of the `Request`.
        var state: State = .initialized
        /// `ProgressHandler` and `DispatchQueue` provided for upload progress callbacks.
        var uploadProgressHandler: (handler: ProgressHandler, queue: DispatchQueue)?
        /// `ProgressHandler` and `DispatchQueue` provided for download progress callbacks.
        var downloadProgressHandler: (handler: ProgressHandler, queue: DispatchQueue)?
        /// `RedirectHandler` provided for to handle request redirection.
        var redirectHandler: RedirectHandler?
        /// `CachedResponseHandler` provided to handle response caching.
        var cachedResponseHandler: CachedResponseHandler?
        /// Queue and closure called when the `Request` is able to create a cURL description of itself.
        var cURLHandler: (queue: DispatchQueue, handler: (String) -> Void)?
        /// Queue and closure called when the `Request` creates a `URLRequest`.
        var urlRequestHandler: (queue: DispatchQueue, handler: (URLRequest) -> Void)?
        /// Queue and closure called when the `Request` creates a `URLSessionTask`.
        var urlSessionTaskHandler: (queue: DispatchQueue, handler: (URLSessionTask) -> Void)?
        /// Response serialization closures that handle response parsing.
        var responseSerializers: [() -> Void] = []
        /// Response serialization completion closures executed once all response serializers are complete.
        var responseSerializerCompletions: [() -> Void] = []
        /// Whether response serializer processing is finished.
        var responseSerializerProcessingFinished = false
        /// `URLCredential` used for authentication challenges.
        var credential: URLCredential?
        /// All `URLRequest`s created by Alamofire on behalf of the `Request`.
        var requests: [URLRequest] = []
        /// All `URLSessionTask`s created by Alamofire on behalf of the `Request`.
        var tasks: [URLSessionTask] = []
        /// All `URLSessionTaskMetrics` values gathered by Alamofire on behalf of the `Request`. Should correspond
        /// exactly the the `tasks` created.
        var metrics: [URLSessionTaskMetrics] = []
        /// Number of times any retriers provided retried the `Request`.
        var retryCount = 0
        /// Final `AFError` for the `Request`, whether from various internal Alamofire calls or as a result of a `task`.
        var error: AFError?
        /// Whether the instance has had `finish()` called and is running the serializers. Should be replaced with a
        /// representation in the state machine in the future.
        var isFinishing = false
        /// Actions to run when requests are finished. Use for concurrency support.
        var finishHandlers: [() -> Void] = []
    }
    /// Protected `MutableState` value that provides thread-safe access to state values.
    fileprivate let mutableState = Protected(MutableState())
    /// `State` of the `Request`.
    public var state: State { mutableState.state }
    /// Returns whether `state` is `.initialized`.
    public var isInitialized: Bool { state == .initialized }
    /// Returns whether `state is `.resumed`.
    public var isResumed: Bool { state == .resumed }
    /// Returns whether `state` is `.suspended`.
    public var isSuspended: Bool { state == .suspended }
    /// Returns whether `state` is `.cancelled`.
    public var isCancelled: Bool { state == .cancelled }
    /// Returns whether `state` is `.finished`.
    public var isFinished: Bool { state == .finished }
    // MARK: Progress
    /// Closure type executed when monitoring the upload or download progress of a request.
    public typealias ProgressHandler = (Progress) -> Void
    /// `Progress` of the upload of the body of the executed `URLRequest`. Reset to `0` if the `Request` is retried.
    public let uploadProgress = Progress(totalUnitCount: 0)
    /// `Progress` of the download of any response data. Reset to `0` if the `Request` is retried.
    public let downloadProgress = Progress(totalUnitCount: 0)
    /// `ProgressHandler` called when `uploadProgress` is updated, on the provided `DispatchQueue`.
    private var uploadProgressHandler: (handler: ProgressHandler, queue: DispatchQueue)? {
        get { mutableState.uploadProgressHandler }
        set { mutableState.uploadProgressHandler = newValue }
    }
    /// `ProgressHandler` called when `downloadProgress` is updated, on the provided `DispatchQueue`.
    fileprivate var downloadProgressHandler: (handler: ProgressHandler, queue: DispatchQueue)? {
        get { mutableState.downloadProgressHandler }
        set { mutableState.downloadProgressHandler = newValue }
    }
    // MARK: Redirect Handling
    /// `RedirectHandler` set on the instance.
    public private(set) var redirectHandler: RedirectHandler? {
        get { mutableState.redirectHandler }
        set { mutableState.redirectHandler = newValue }
    }
    // MARK: Cached Response Handling
    /// `CachedResponseHandler` set on the instance.
    public private(set) var cachedResponseHandler: CachedResponseHandler? {
        get { mutableState.cachedResponseHandler }
        set { mutableState.cachedResponseHandler = newValue }
    }
    // MARK: URLCredential
    /// `URLCredential` used for authentication challenges. Created by calling one of the `authenticate` methods.
    public private(set) var credential: URLCredential? {
        get { mutableState.credential }
        set { mutableState.credential = newValue }
    }
    // MARK: Validators
    /// `Validator` callback closures that store the validation calls enqueued.
    fileprivate let validators = Protected<[() -> Void]>([])
    // MARK: URLRequests
    /// All `URLRequests` created on behalf of the `Request`, including original and adapted requests.
    public var requests: [URLRequest] { mutableState.requests }
    /// First `URLRequest` created on behalf of the `Request`. May not be the first one actually executed.
    public var firstRequest: URLRequest? { requests.first }
    /// Last `URLRequest` created on behalf of the `Request`.
    public var lastRequest: URLRequest? { requests.last }
    /// Current `URLRequest` created on behalf of the `Request`.
    public var request: URLRequest? { lastRequest }
    /// `URLRequest`s from all of the `URLSessionTask`s executed on behalf of the `Request`. May be different from
    /// `requests` due to `URLSession` manipulation.
    public var performedRequests: [URLRequest] { mutableState.read { $0.tasks.compactMap(\.currentRequest) } }
    // MARK: HTTPURLResponse
    /// `HTTPURLResponse` received from the server, if any. If the `Request` was retried, this is the response of the
    /// last `URLSessionTask`.
    public var response: HTTPURLResponse? { lastTask?.response as? HTTPURLResponse }
    // MARK: Tasks
    /// All `URLSessionTask`s created on behalf of the `Request`.
    public var tasks: [URLSessionTask] { mutableState.tasks }
    /// First `URLSessionTask` created on behalf of the `Request`.
    public var firstTask: URLSessionTask? { tasks.first }
    /// Last `URLSessionTask` created on behalf of the `Request`.
    public var lastTask: URLSessionTask? { tasks.last }
    /// Current `URLSessionTask` created on behalf of the `Request`.
    public var task: URLSessionTask? { lastTask }
    // MARK: Metrics
    /// All `URLSessionTaskMetrics` gathered on behalf of the `Request`. Should correspond to the `tasks` created.
    public var allMetrics: [URLSessionTaskMetrics] { mutableState.metrics }
    /// First `URLSessionTaskMetrics` gathered on behalf of the `Request`.
    public var firstMetrics: URLSessionTaskMetrics? { allMetrics.first }
    /// Last `URLSessionTaskMetrics` gathered on behalf of the `Request`.
    public var lastMetrics: URLSessionTaskMetrics? { allMetrics.last }
    /// Current `URLSessionTaskMetrics` gathered on behalf of the `Request`.
    public var metrics: URLSessionTaskMetrics? { lastMetrics }
    // MARK: Retry Count
    /// Number of times the `Request` has been retried.
    public var retryCount: Int { mutableState.retryCount }
    // MARK: Error
    /// `Error` returned from Alamofire internally, from the network request directly, or any validators executed.
    public fileprivate(set) var error: AFError? {
        get { mutableState.error }
        set { mutableState.error = newValue }
    }
    /// Default initializer for the `Request` superclass.
    ///
    /// - Parameters:
    ///   - id:                 `UUID` used for the `Hashable` and `Equatable` implementations. `UUID()` by default.
    ///   - underlyingQueue:    `DispatchQueue` on which all internal `Request` work is performed.
    ///   - serializationQueue: `DispatchQueue` on which all serialization work is performed. By default targets
    ///                         `underlyingQueue`, but can be passed another queue from a `Session`.
    ///   - eventMonitor:       `EventMonitor` called for event callbacks from internal `Request` actions.
    ///   - interceptor:        `RequestInterceptor` used throughout the request lifecycle.
    ///   - delegate:           `RequestDelegate` that provides an interface to actions not performed by the `Request`.
    init(id: UUID = UUID(),
         underlyingQueue: DispatchQueue,
         serializationQueue: DispatchQueue,
         eventMonitor: EventMonitor?,
         interceptor: RequestInterceptor?,
         delegate: RequestDelegate) {
        self.id = id
        self.underlyingQueue = underlyingQueue
        self.serializationQueue = serializationQueue
        self.eventMonitor = eventMonitor
        self.interceptor = interceptor
        self.delegate = delegate
    }
    // MARK: - Internal Event API
    // All API must be called from underlyingQueue.
    /// Called when an initial `URLRequest` has been created on behalf of the instance. If a `RequestAdapter` is active,
    /// the `URLRequest` will be adapted before being issued.
    ///
    /// - Parameter request: The `URLRequest` created.
    func didCreateInitialURLRequest(_ request: URLRequest) {
        dispatchPrecondition(condition: .onQueue(underlyingQueue))
        mutableState.write { $0.requests.append(request) }
        eventMonitor?.request(self, didCreateInitialURLRequest: request)
    }
    /// Called when initial `URLRequest` creation has failed, typically through a `URLRequestConvertible`.
    ///
    /// - Note: Triggers retry.
    ///
    /// - Parameter error: `AFError` thrown from the failed creation.
    func didFailToCreateURLRequest(with error: AFError) {
        dispatchPrecondition(condition: .onQueue(underlyingQueue))
        self.error = error
        eventMonitor?.request(self, didFailToCreateURLRequestWithError: error)
        callCURLHandlerIfNecessary()
        retryOrFinish(error: error)
    }
    /// Called when a `RequestAdapter` has successfully adapted a `URLRequest`.
    ///
    /// - Parameters:
    ///   - initialRequest: The `URLRequest` that was adapted.
    ///   - adaptedRequest: The `URLRequest` returned by the `RequestAdapter`.
    func didAdaptInitialRequest(_ initialRequest: URLRequest, to adaptedRequest: URLRequest) {
        dispatchPrecondition(condition: .onQueue(underlyingQueue))
        mutableState.write { $0.requests.append(adaptedRequest) }
        eventMonitor?.request(self, didAdaptInitialRequest: initialRequest, to: adaptedRequest)
    }
    /// Called when a `RequestAdapter` fails to adapt a `URLRequest`.
    ///
    /// - Note: Triggers retry.
    ///
    /// - Parameters:
    ///   - request: The `URLRequest` the adapter was called with.
    ///   - error:   The `AFError` returned by the `RequestAdapter`.
    func didFailToAdaptURLRequest(_ request: URLRequest, withError error: AFError) {
        dispatchPrecondition(condition: .onQueue(underlyingQueue))
        self.error = error
        eventMonitor?.request(self, didFailToAdaptURLRequest: request, withError: error)
        callCURLHandlerIfNecessary()
        retryOrFinish(error: error)
    }
    /// Final `URLRequest` has been created for the instance.
    ///
    /// - Parameter request: The `URLRequest` created.
    func didCreateURLRequest(_ request: URLRequest) {
        dispatchPrecondition(condition: .onQueue(underlyingQueue))
        mutableState.read { state in
            state.urlRequestHandler?.queue.async { state.urlRequestHandler?.handler(request) }
        }
        eventMonitor?.request(self, didCreateURLRequest: request)
        callCURLHandlerIfNecessary()
    }
    /// Asynchronously calls any stored `cURLHandler` and then removes it from `mutableState`.
    private func callCURLHandlerIfNecessary() {
        mutableState.write { mutableState in
            guard let cURLHandler = mutableState.cURLHandler else { return }
            cURLHandler.queue.async { cURLHandler.handler(self.cURLDescription()) }
            mutableState.cURLHandler = nil
        }
    }
    /// Called when a `URLSessionTask` is created on behalf of the instance.
    ///
    /// - Parameter task: The `URLSessionTask` created.
    func didCreateTask(_ task: URLSessionTask) {
        dispatchPrecondition(condition: .onQueue(underlyingQueue))
        mutableState.write { state in
            state.tasks.append(task)
            guard let urlSessionTaskHandler = state.urlSessionTaskHandler else { return }
            urlSessionTaskHandler.queue.async { urlSessionTaskHandler.handler(task) }
        }
        eventMonitor?.request(self, didCreateTask: task)
    }
    /// Called when resumption is completed.
    func didResume() {
        dispatchPrecondition(condition: .onQueue(underlyingQueue))
        eventMonitor?.requestDidResume(self)
    }
    /// Called when a `URLSessionTask` is resumed on behalf of the instance.
    ///
    /// - Parameter task: The `URLSessionTask` resumed.
    func didResumeTask(_ task: URLSessionTask) {
        dispatchPrecondition(condition: .onQueue(underlyingQueue))
        eventMonitor?.request(self, didResumeTask: task)
    }
    /// Called when suspension is completed.
    func didSuspend() {
        dispatchPrecondition(condition: .onQueue(underlyingQueue))
        eventMonitor?.requestDidSuspend(self)
    }
    /// Called when a `URLSessionTask` is suspended on behalf of the instance.
    ///
    /// - Parameter task: The `URLSessionTask` suspended.
    func didSuspendTask(_ task: URLSessionTask) {
        dispatchPrecondition(condition: .onQueue(underlyingQueue))
        eventMonitor?.request(self, didSuspendTask: task)
    }
    /// Called when cancellation is completed, sets `error` to `AFError.explicitlyCancelled`.
    func didCancel() {
        dispatchPrecondition(condition: .onQueue(underlyingQueue))
        mutableState.write { mutableState in
            mutableState.error = mutableState.error ?? AFError.explicitlyCancelled
        }
        eventMonitor?.requestDidCancel(self)
    }
    /// Called when a `URLSessionTask` is cancelled on behalf of the instance.
    ///
    /// - Parameter task: The `URLSessionTask` cancelled.
    func didCancelTask(_ task: URLSessionTask) {
        dispatchPrecondition(condition: .onQueue(underlyingQueue))
        eventMonitor?.request(self, didCancelTask: task)
    }
    /// Called when a `URLSessionTaskMetrics` value is gathered on behalf of the instance.
    ///
    /// - Parameter metrics: The `URLSessionTaskMetrics` gathered.
    func didGatherMetrics(_ metrics: URLSessionTaskMetrics) {
        dispatchPrecondition(condition: .onQueue(underlyingQueue))
        mutableState.write { $0.metrics.append(metrics) }
        eventMonitor?.request(self, didGatherMetrics: metrics)
    }
    /// Called when a `URLSessionTask` fails before it is finished, typically during certificate pinning.
    ///
    /// - Parameters:
    ///   - task:  The `URLSessionTask` which failed.
    ///   - error: The early failure `AFError`.
    func didFailTask(_ task: URLSessionTask, earlyWithError error: AFError) {
        dispatchPrecondition(condition: .onQueue(underlyingQueue))
        self.error = error
        // Task will still complete, so didCompleteTask(_:with:) will handle retry.
        eventMonitor?.request(self, didFailTask: task, earlyWithError: error)
    }
    /// Called when a `URLSessionTask` completes. All tasks will eventually call this method.
    ///
    /// - Note: Response validation is synchronously triggered in this step.
    ///
    /// - Parameters:
    ///   - task:  The `URLSessionTask` which completed.
    ///   - error: The `AFError` `task` may have completed with. If `error` has already been set on the instance, this
    ///            value is ignored.
    func didCompleteTask(_ task: URLSessionTask, with error: AFError?) {
        dispatchPrecondition(condition: .onQueue(underlyingQueue))
        self.error = self.error ?? error
        let validators = validators.read { $0 }
        validators.forEach { $0() }
        eventMonitor?.request(self, didCompleteTask: task, with: error)
        retryOrFinish(error: self.error)
    }
    /// Called when the `RequestDelegate` is going to retry this `Request`. Calls `reset()`.
    func prepareForRetry() {
        dispatchPrecondition(condition: .onQueue(underlyingQueue))
        mutableState.write { $0.retryCount += 1 }
        reset()
        eventMonitor?.requestIsRetrying(self)
    }
    /// Called to determine whether retry will be triggered for the particular error, or whether the instance should
    /// call `finish()`.
    ///
    /// - Parameter error: The possible `AFError` which may trigger retry.
    func retryOrFinish(error: AFError?) {
        dispatchPrecondition(condition: .onQueue(underlyingQueue))
        guard !isCancelled, let error = error, let delegate = delegate else { finish(); return }
        delegate.retryResult(for: self, dueTo: error) { retryResult in
            switch retryResult {
            case .doNotRetry:
                self.finish()
            case let .doNotRetryWithError(retryError):
                self.finish(error: retryError.asAFError(orFailWith: "Received retryError was not already AFError"))
            case .retry, .retryWithDelay:
                delegate.retryRequest(self, withDelay: retryResult.delay)
            }
        }
    }
    /// Finishes this `Request` and starts the response serializers.
    ///
    /// - Parameter error: The possible `Error` with which the instance will finish.
    func finish(error: AFError? = nil) {
        dispatchPrecondition(condition: .onQueue(underlyingQueue))
        guard !mutableState.isFinishing else { return }
        mutableState.isFinishing = true
        if let error = error { self.error = error }
        // Start response handlers
        processNextResponseSerializer()
        eventMonitor?.requestDidFinish(self)
    }
    /// Appends the response serialization closure to the instance.
    ///
    ///  - Note: This method will also `resume` the instance if `delegate.startImmediately` returns `true`.
    ///
    /// - Parameter closure: The closure containing the response serialization call.
    func appendResponseSerializer(_ closure: @escaping () -> Void) {
        mutableState.write { mutableState in
            mutableState.responseSerializers.append(closure)
            if mutableState.state == .finished {
                mutableState.state = .resumed
            }
            if mutableState.responseSerializerProcessingFinished {
                underlyingQueue.async { self.processNextResponseSerializer() }
            }
            if mutableState.state.canTransitionTo(.resumed) {
                underlyingQueue.async { if self.delegate?.startImmediately == true { self.resume() } }
            }
        }
    }
    /// Returns the next response serializer closure to execute if there's one left.
    ///
    /// - Returns: The next response serialization closure, if there is one.
    func nextResponseSerializer() -> (() -> Void)? {
        var responseSerializer: (() -> Void)?
        mutableState.write { mutableState in
            let responseSerializerIndex = mutableState.responseSerializerCompletions.count
            if responseSerializerIndex < mutableState.responseSerializers.count {
                responseSerializer = mutableState.responseSerializers[responseSerializerIndex]
            }
        }
        return responseSerializer
    }
    /// Processes the next response serializer and calls all completions if response serialization is complete.
    func processNextResponseSerializer() {
        guard let responseSerializer = nextResponseSerializer() else {
            // Execute all response serializer completions and clear them
            var completions: [() -> Void] = []
            mutableState.write { mutableState in
                completions = mutableState.responseSerializerCompletions
                // Clear out all response serializers and response serializer completions in mutable state since the
                // request is complete. It's important to do this prior to calling the completion closures in case
                // the completions call back into the request triggering a re-processing of the response serializers.
                // An example of how this can happen is by calling cancel inside a response completion closure.
                mutableState.responseSerializers.removeAll()
                mutableState.responseSerializerCompletions.removeAll()
                if mutableState.state.canTransitionTo(.finished) {
                    mutableState.state = .finished
                }
                mutableState.responseSerializerProcessingFinished = true
                mutableState.isFinishing = false
            }
            completions.forEach { $0() }
            // Cleanup the request
            cleanup()
            return
        }
        serializationQueue.async { responseSerializer() }
    }
    /// Notifies the `Request` that the response serializer is complete.
    ///
    /// - Parameter completion: The completion handler provided with the response serializer, called when all serializers
    ///                         are complete.
    func responseSerializerDidComplete(completion: @escaping () -> Void) {
        mutableState.write { $0.responseSerializerCompletions.append(completion) }
        processNextResponseSerializer()
    }
    /// Resets all task and response serializer related state for retry.
    func reset() {
        error = nil
        uploadProgress.totalUnitCount = 0
        uploadProgress.completedUnitCount = 0
        downloadProgress.totalUnitCount = 0
        downloadProgress.completedUnitCount = 0
        mutableState.write { state in
            state.isFinishing = false
            state.responseSerializerCompletions = []
        }
    }
    /// Called when updating the upload progress.
    ///
    /// - Parameters:
    ///   - totalBytesSent: Total bytes sent so far.
    ///   - totalBytesExpectedToSend: Total bytes expected to send.
    func updateUploadProgress(totalBytesSent: Int64, totalBytesExpectedToSend: Int64) {
        uploadProgress.totalUnitCount = totalBytesExpectedToSend
        uploadProgress.completedUnitCount = totalBytesSent
        uploadProgressHandler?.queue.async { self.uploadProgressHandler?.handler(self.uploadProgress) }
    }
    /// Perform a closure on the current `state` while locked.
    ///
    /// - Parameter perform: The closure to perform.
    func withState(perform: (State) -> Void) {
        mutableState.withState(perform: perform)
    }
    // MARK: Task Creation
    /// Called when creating a `URLSessionTask` for this `Request`. Subclasses must override.
    ///
    /// - Parameters:
    ///   - request: `URLRequest` to use to create the `URLSessionTask`.
    ///   - session: `URLSession` which creates the `URLSessionTask`.
    ///
    /// - Returns:   The `URLSessionTask` created.
    func task(for request: URLRequest, using session: URLSession) -> URLSessionTask {
        fatalError("Subclasses must override.")
    }
    // MARK: - Public API
    // These APIs are callable from any queue.
    // MARK: State
    /// Cancels the instance. Once cancelled, a `Request` can no longer be resumed or suspended.
    ///
    /// - Returns: The instance.
    @discardableResult
    public func cancel() -> Self {
        mutableState.write { mutableState in
            guard mutableState.state.canTransitionTo(.cancelled) else { return }
            mutableState.state = .cancelled
            underlyingQueue.async { self.didCancel() }
            guard let task = mutableState.tasks.last, task.state != .completed else {
                underlyingQueue.async { self.finish() }
                return
            }
            // Resume to ensure metrics are gathered.
            task.resume()
            task.cancel()
            underlyingQueue.async { self.didCancelTask(task) }
        }
        return self
    }
    /// Suspends the instance.
    ///
    /// - Returns: The instance.
    @discardableResult
    public func suspend() -> Self {
        mutableState.write { mutableState in
            guard mutableState.state.canTransitionTo(.suspended) else { return }
            mutableState.state = .suspended
            underlyingQueue.async { self.didSuspend() }
            guard let task = mutableState.tasks.last, task.state != .completed else { return }
            task.suspend()
            underlyingQueue.async { self.didSuspendTask(task) }
        }
        return self
    }
    /// Resumes the instance.
    ///
    /// - Returns: The instance.
    @discardableResult
    public func resume() -> Self {
        mutableState.write { mutableState in
            guard mutableState.state.canTransitionTo(.resumed) else { return }
            mutableState.state = .resumed
            underlyingQueue.async { self.didResume() }
            guard let task = mutableState.tasks.last, task.state != .completed else { return }
            task.resume()
            underlyingQueue.async { self.didResumeTask(task) }
        }
        return self
    }
    // MARK: - Closure API
    /// Associates a credential using the provided values with the instance.
    ///
    /// - Parameters:
    ///   - username:    The username.
    ///   - password:    The password.
    ///   - persistence: The `URLCredential.Persistence` for the created `URLCredential`. `.forSession` by default.
    ///
    /// - Returns:       The instance.
    @discardableResult
    public func authenticate(username: String, password: String, persistence: URLCredential.Persistence = .forSession) -> Self {
        let credential = URLCredential(user: username, password: password, persistence: persistence)
        return authenticate(with: credential)
    }
    /// Associates the provided credential with the instance.
    ///
    /// - Parameter credential: The `URLCredential`.
    ///
    /// - Returns:              The instance.
    @discardableResult
    public func authenticate(with credential: URLCredential) -> Self {
        mutableState.credential = credential
        return self
    }
    /// Sets a closure to be called periodically during the lifecycle of the instance as data is read from the server.
    ///
    /// - Note: Only the last closure provided is used.
    ///
    /// - Parameters:
    ///   - queue:   The `DispatchQueue` to execute the closure on. `.main` by default.
    ///   - closure: The closure to be executed periodically as data is read from the server.
    ///
    /// - Returns:   The instance.
    @discardableResult
    public func downloadProgress(queue: DispatchQueue = .main, closure: @escaping ProgressHandler) -> Self {
        mutableState.downloadProgressHandler = (handler: closure, queue: queue)
        return self
    }
    /// Sets a closure to be called periodically during the lifecycle of the instance as data is sent to the server.
    ///
    /// - Note: Only the last closure provided is used.
    ///
    /// - Parameters:
    ///   - queue:   The `DispatchQueue` to execute the closure on. `.main` by default.
    ///   - closure: The closure to be executed periodically as data is sent to the server.
    ///
    /// - Returns:   The instance.
    @discardableResult
    public func uploadProgress(queue: DispatchQueue = .main, closure: @escaping ProgressHandler) -> Self {
        mutableState.uploadProgressHandler = (handler: closure, queue: queue)
        return self
    }
    // MARK: Redirects
    /// Sets the redirect handler for the instance which will be used if a redirect response is encountered.
    ///
    /// - Note: Attempting to set the redirect handler more than once is a logic error and will crash.
    ///
    /// - Parameter handler: The `RedirectHandler`.
    ///
    /// - Returns:           The instance.
    @discardableResult
    public func redirect(using handler: RedirectHandler) -> Self {
        mutableState.write { mutableState in
            precondition(mutableState.redirectHandler == nil, "Redirect handler has already been set.")
            mutableState.redirectHandler = handler
        }
        return self
    }
    // MARK: Cached Responses
    /// Sets the cached response handler for the `Request` which will be used when attempting to cache a response.
    ///
    /// - Note: Attempting to set the cache handler more than once is a logic error and will crash.
    ///
    /// - Parameter handler: The `CachedResponseHandler`.
    ///
    /// - Returns:           The instance.
    @discardableResult
    public func cacheResponse(using handler: CachedResponseHandler) -> Self {
        mutableState.write { mutableState in
            precondition(mutableState.cachedResponseHandler == nil, "Cached response handler has already been set.")
            mutableState.cachedResponseHandler = handler
        }
        return self
    }
    // MARK: - Lifetime APIs
    /// Sets a handler to be called when the cURL description of the request is available.
    ///
    /// - Note: When waiting for a `Request`'s `URLRequest` to be created, only the last `handler` will be called.
    ///
    /// - Parameters:
    ///   - queue:   `DispatchQueue` on which `handler` will be called.
    ///   - handler: Closure to be called when the cURL description is available.
    ///
    /// - Returns:           The instance.
    @discardableResult
    public func cURLDescription(on queue: DispatchQueue, calling handler: @escaping (String) -> Void) -> Self {
        mutableState.write { mutableState in
            if mutableState.requests.last != nil {
                queue.async { handler(self.cURLDescription()) }
            } else {
                mutableState.cURLHandler = (queue, handler)
            }
        }
        return self
    }
    /// Sets a handler to be called when the cURL description of the request is available.
    ///
    /// - Note: When waiting for a `Request`'s `URLRequest` to be created, only the last `handler` will be called.
    ///
    /// - Parameter handler: Closure to be called when the cURL description is available. Called on the instance's
    ///                      `underlyingQueue` by default.
    ///
    /// - Returns:           The instance.
    @discardableResult
    public func cURLDescription(calling handler: @escaping (String) -> Void) -> Self {
        cURLDescription(on: underlyingQueue, calling: handler)
        return self
    }
    /// Sets a closure to called whenever Alamofire creates a `URLRequest` for this instance.
    ///
    /// - Note: This closure will be called multiple times if the instance adapts incoming `URLRequest`s or is retried.
    ///
    /// - Parameters:
    ///   - queue:   `DispatchQueue` on which `handler` will be called. `.main` by default.
    ///   - handler: Closure to be called when a `URLRequest` is available.
    ///
    /// - Returns:   The instance.
    @discardableResult
    public func onURLRequestCreation(on queue: DispatchQueue = .main, perform handler: @escaping (URLRequest) -> Void) -> Self {
        mutableState.write { state in
            if let request = state.requests.last {
                queue.async { handler(request) }
            }
            state.urlRequestHandler = (queue, handler)
        }
        return self
    }
    /// Sets a closure to be called whenever the instance creates a `URLSessionTask`.
    ///
    /// - Note: This API should only be used to provide `URLSessionTask`s to existing API, like `NSFileProvider`. It
    ///         **SHOULD NOT** be used to interact with tasks directly, as that may be break Alamofire features.
    ///         Additionally, this closure may be called multiple times if the instance is retried.
    ///
    /// - Parameters:
    ///   - queue:   `DispatchQueue` on which `handler` will be called. `.main` by default.
    ///   - handler: Closure to be called when the `URLSessionTask` is available.
    ///
    /// - Returns:   The instance.
    @discardableResult
    public func onURLSessionTaskCreation(on queue: DispatchQueue = .main, perform handler: @escaping (URLSessionTask) -> Void) -> Self {
        mutableState.write { state in
            if let task = state.tasks.last {
                queue.async { handler(task) }
            }
            state.urlSessionTaskHandler = (queue, handler)
        }
        return self
    }
    // MARK: Cleanup
    /// Adds a `finishHandler` closure to be called when the request completes.
    ///
    /// - Parameter closure: Closure to be called when the request finishes.
    func onFinish(perform finishHandler: @escaping () -> Void) {
        guard !isFinished else { finishHandler(); return }
        mutableState.write { state in
            state.finishHandlers.append(finishHandler)
        }
    }
    /// Final cleanup step executed when the instance finishes response serialization.
    func cleanup() {
        let handlers = mutableState.finishHandlers
        handlers.forEach { $0() }
        mutableState.write { state in
            state.finishHandlers.removeAll()
        }
        delegate?.cleanup(after: self)
    }
}
extension Request {
    /// Type indicating how a `DataRequest` or `DataStreamRequest` should proceed after receiving an `HTTPURLResponse`.
    public enum ResponseDisposition {
        /// Allow the request to continue normally.
        case allow
        /// Cancel the request, similar to calling `cancel()`.
        case cancel
        var sessionDisposition: URLSession.ResponseDisposition {
            switch self {
            case .allow: return .allow
            case .cancel: return .cancel
            }
        }
    }
}
// MARK: - Protocol Conformances
extension Request: Equatable {
    public static func ==(lhs: Request, rhs: Request) -> Bool {
        lhs.id == rhs.id
    }
}
extension Request: Hashable {
    public func hash(into hasher: inout Hasher) {
        hasher.combine(id)
    }
}
extension Request: CustomStringConvertible {
    /// A textual representation of this instance, including the `HTTPMethod` and `URL` if the `URLRequest` has been
    /// created, as well as the response status code, if a response has been received.
    public var description: String {
        guard let request = performedRequests.last ?? lastRequest,
              let url = request.url,
              let method = request.httpMethod else { return "No request created yet." }
        let requestDescription = "\(method) \(url.absoluteString)"
        return response.map { "\(requestDescription) (\($0.statusCode))" } ?? requestDescription
    }
}
extension Request {
    /// cURL representation of the instance.
    ///
    /// - Returns: The cURL equivalent of the instance.
    public func cURLDescription() -> String {
        guard
            let request = lastRequest,
            let url = request.url,
            let host = url.host,
            let method = request.httpMethod else { return "$ curl command could not be created" }
        var components = ["$ curl -v"]
        components.append("-X \(method)")
        if let credentialStorage = delegate?.sessionConfiguration.urlCredentialStorage {
            let protectionSpace = URLProtectionSpace(host: host,
                                                     port: url.port ?? 0,
                                                     protocol: url.scheme,
                                                     realm: host,
                                                     authenticationMethod: NSURLAuthenticationMethodHTTPBasic)
            if let credentials = credentialStorage.credentials(for: protectionSpace)?.values {
                for credential in credentials {
                    guard let user = credential.user, let password = credential.password else { continue }
                    components.append("-u \(user):\(password)")
                }
            } else {
                if let credential = credential, let user = credential.user, let password = credential.password {
                    components.append("-u \(user):\(password)")
                }
            }
        }
        if let configuration = delegate?.sessionConfiguration, configuration.httpShouldSetCookies {
            if
                let cookieStorage = configuration.httpCookieStorage,
                let cookies = cookieStorage.cookies(for: url), !cookies.isEmpty {
                let allCookies = cookies.map { "\($0.name)=\($0.value)" }.joined(separator: ";")
                components.append("-b \"\(allCookies)\"")
            }
        }
        var headers = HTTPHeaders()
        if let sessionHeaders = delegate?.sessionConfiguration.headers {
            for header in sessionHeaders where header.name != "Cookie" {
                headers[header.name] = header.value
            }
        }
        for header in request.headers where header.name != "Cookie" {
            headers[header.name] = header.value
        }
        for header in headers {
            let escapedValue = header.value.replacingOccurrences(of: "\"", with: "\\\"")
            components.append("-H \"\(header.name): \(escapedValue)\"")
        }
        if let httpBodyData = request.httpBody {
            let httpBody = String(decoding: httpBodyData, as: UTF8.self)
            var escapedBody = httpBody.replacingOccurrences(of: "\\\"", with: "\\\\\"")
            escapedBody = escapedBody.replacingOccurrences(of: "\"", with: "\\\"")
            components.append("-d \"\(escapedBody)\"")
        }
        components.append("\"\(url.absoluteString)\"")
        return components.joined(separator: " \\\n\t")
    }
}
/// Protocol abstraction for `Request`'s communication back to the `SessionDelegate`.
public protocol RequestDelegate: AnyObject {
    /// `URLSessionConfiguration` used to create the underlying `URLSessionTask`s.
    var sessionConfiguration: URLSessionConfiguration { get }
    /// Determines whether the `Request` should automatically call `resume()` when adding the first response handler.
    var startImmediately: Bool { get }
    /// Notifies the delegate the `Request` has reached a point where it needs cleanup.
    ///
    /// - Parameter request: The `Request` to cleanup after.
    func cleanup(after request: Request)
    /// Asynchronously ask the delegate whether a `Request` will be retried.
    ///
    /// - Parameters:
    ///   - request:    `Request` which failed.
    ///   - error:      `Error` which produced the failure.
    ///   - completion: Closure taking the `RetryResult` for evaluation.
    func retryResult(for request: Request, dueTo error: AFError, completion: @escaping (RetryResult) -> Void)
    /// Asynchronously retry the `Request`.
    ///
    /// - Parameters:
    ///   - request:   `Request` which will be retried.
    ///   - timeDelay: `TimeInterval` after which the retry will be triggered.
    func retryRequest(_ request: Request, withDelay timeDelay: TimeInterval?)
}
// MARK: - Subclasses
// MARK: - DataRequest
/// `Request` subclass which handles in-memory `Data` download using `URLSessionDataTask`.
public class DataRequest: Request {
    /// `URLRequestConvertible` value used to create `URLRequest`s for this instance.
    public let convertible: URLRequestConvertible
    /// `Data` read from the server so far.
    public var data: Data? { dataMutableState.data }
    private struct DataMutableState {
        var data: Data?
        var httpResponseHandler: (queue: DispatchQueue,
                                  handler: (_ response: HTTPURLResponse,
                                            _ completionHandler: @escaping (ResponseDisposition) -> Void) -> Void)?
    }
    private let dataMutableState = Protected(DataMutableState())
    /// Creates a `DataRequest` using the provided parameters.
    ///
    /// - Parameters:
    ///   - id:                 `UUID` used for the `Hashable` and `Equatable` implementations. `UUID()` by default.
    ///   - convertible:        `URLRequestConvertible` value used to create `URLRequest`s for this instance.
    ///   - underlyingQueue:    `DispatchQueue` on which all internal `Request` work is performed.
    ///   - serializationQueue: `DispatchQueue` on which all serialization work is performed. By default targets
    ///                         `underlyingQueue`, but can be passed another queue from a `Session`.
    ///   - eventMonitor:       `EventMonitor` called for event callbacks from internal `Request` actions.
    ///   - interceptor:        `RequestInterceptor` used throughout the request lifecycle.
    ///   - delegate:           `RequestDelegate` that provides an interface to actions not performed by the `Request`.
    init(id: UUID = UUID(),
         convertible: URLRequestConvertible,
         underlyingQueue: DispatchQueue,
         serializationQueue: DispatchQueue,
         eventMonitor: EventMonitor?,
         interceptor: RequestInterceptor?,
         delegate: RequestDelegate) {
        self.convertible = convertible
        super.init(id: id,
                   underlyingQueue: underlyingQueue,
                   serializationQueue: serializationQueue,
                   eventMonitor: eventMonitor,
                   interceptor: interceptor,
                   delegate: delegate)
    }
    override func reset() {
        super.reset()
        dataMutableState.write { mutableState in
            mutableState.data = nil
        }
    }
    /// Called when `Data` is received by this instance.
    ///
    /// - Note: Also calls `updateDownloadProgress`.
    ///
    /// - Parameter data: The `Data` received.
    func didReceive(data: Data) {
        dataMutableState.write { mutableState in
            if mutableState.data == nil {
                mutableState.data = data
            } else {
                mutableState.data?.append(data)
            }
        }
        updateDownloadProgress()
    }
    func didReceiveResponse(_ response: HTTPURLResponse, completionHandler: @escaping (URLSession.ResponseDisposition) -> Void) {
        dataMutableState.read { dataMutableState in
            guard let httpResponseHandler = dataMutableState.httpResponseHandler else {
                underlyingQueue.async { completionHandler(.allow) }
                return
            }
            httpResponseHandler.queue.async {
                httpResponseHandler.handler(response) { disposition in
                    if disposition == .cancel {
                        self.mutableState.write { mutableState in
                            mutableState.state = .cancelled
                            mutableState.error = mutableState.error ?? AFError.explicitlyCancelled
                        }
                    }
                    self.underlyingQueue.async {
                        completionHandler(disposition.sessionDisposition)
                    }
                }
            }
        }
    }
    override func task(for request: URLRequest, using session: URLSession) -> URLSessionTask {
        let copiedRequest = request
        return session.dataTask(with: copiedRequest)
    }
    /// Called to update the `downloadProgress` of the instance.
    func updateDownloadProgress() {
        let totalBytesReceived = Int64(data?.count ?? 0)
        let totalBytesExpected = task?.response?.expectedContentLength ?? NSURLSessionTransferSizeUnknown
        downloadProgress.totalUnitCount = totalBytesExpected
        downloadProgress.completedUnitCount = totalBytesReceived
        downloadProgressHandler?.queue.async { self.downloadProgressHandler?.handler(self.downloadProgress) }
    }
    /// Validates the request, using the specified closure.
    ///
    /// - Note: If validation fails, subsequent calls to response handlers will have an associated error.
    ///
    /// - Parameter validation: `Validation` closure used to validate the response.
    ///
    /// - Returns:              The instance.
    @discardableResult
    public func validate(_ validation: @escaping Validation) -> Self {
        let validator: () -> Void = { [unowned self] in
            guard error == nil, let response = response else { return }
            let result = validation(request, response, data)
            if case let .failure(error) = result { self.error = error.asAFError(or: .responseValidationFailed(reason: .customValidationFailed(error: error))) }
            eventMonitor?.request(self,
                                  didValidateRequest: request,
                                  response: response,
                                  data: data,
                                  withResult: result)
        }
        validators.write { $0.append(validator) }
        return self
    }
    /// Sets a closure called whenever the `DataRequest` produces an `HTTPURLResponse` and providing a completion
    /// handler to return a `ResponseDisposition` value.
    ///
    /// - Parameters:
    ///   - queue:   `DispatchQueue` on which the closure will be called. `.main` by default.
    ///   - handler: Closure called when the instance produces an `HTTPURLResponse`. The `completionHandler` provided
    ///              MUST be called, otherwise the request will never complete.
    ///
    /// - Returns:   The instance.
    @_disfavoredOverload
    @discardableResult
    public func onHTTPResponse(
        on queue: DispatchQueue = .main,
        perform handler: @escaping (_ response: HTTPURLResponse,
                                    _ completionHandler: @escaping (ResponseDisposition) -> Void) -> Void
    ) -> Self {
        dataMutableState.write { mutableState in
            mutableState.httpResponseHandler = (queue, handler)
        }
        return self
    }
    /// Sets a closure called whenever the `DataRequest` produces an `HTTPURLResponse`.
    ///
    /// - Parameters:
    ///   - queue:   `DispatchQueue` on which the closure will be called. `.main` by default.
    ///   - handler: Closure called when the instance produces an `HTTPURLResponse`.
    ///
    /// - Returns:   The instance.
    @discardableResult
    public func onHTTPResponse(on queue: DispatchQueue = .main,
                               perform handler: @escaping (HTTPURLResponse) -> Void) -> Self {
        onHTTPResponse(on: queue) { response, completionHandler in
            handler(response)
            completionHandler(.allow)
        }
        return self
    }
}
// MARK: - DataStreamRequest
/// `Request` subclass which streams HTTP response `Data` through a `Handler` closure.
public final class DataStreamRequest: Request {
    /// Closure type handling `DataStreamRequest.Stream` values.
    public typealias Handler<Success, Failure: Error> = (Stream<Success, Failure>) throws -> Void
    /// Type encapsulating an `Event` as it flows through the stream, as well as a `CancellationToken` which can be used
    /// to stop the stream at any time.
    public struct Stream<Success, Failure: Error> {
        /// Latest `Event` from the stream.
        public let event: Event<Success, Failure>
        /// Token used to cancel the stream.
        public let token: CancellationToken
        /// Cancel the ongoing stream by canceling the underlying `DataStreamRequest`.
        public func cancel() {
            token.cancel()
        }
    }
    /// Type representing an event flowing through the stream. Contains either the `Result` of processing streamed
    /// `Data` or the completion of the stream.
    public enum Event<Success, Failure: Error> {
        /// Output produced every time the instance receives additional `Data`. The associated value contains the
        /// `Result` of processing the incoming `Data`.
        case stream(Result<Success, Failure>)
        /// Output produced when the instance has completed, whether due to stream end, cancellation, or an error.
        /// Associated `Completion` value contains the final state.
        case complete(Completion)
    }
    /// Value containing the state of a `DataStreamRequest` when the stream was completed.
    public struct Completion {
        /// Last `URLRequest` issued by the instance.
        public let request: URLRequest?
        /// Last `HTTPURLResponse` received by the instance.
        public let response: HTTPURLResponse?
        /// Last `URLSessionTaskMetrics` produced for the instance.
        public let metrics: URLSessionTaskMetrics?
        /// `AFError` produced for the instance, if any.
        public let error: AFError?
    }
    /// Type used to cancel an ongoing stream.
    public struct CancellationToken {
        weak var request: DataStreamRequest?
        init(_ request: DataStreamRequest) {
            self.request = request
        }
        /// Cancel the ongoing stream by canceling the underlying `DataStreamRequest`.
        public func cancel() {
            request?.cancel()
        }
    }
    /// `URLRequestConvertible` value used to create `URLRequest`s for this instance.
    public let convertible: URLRequestConvertible
    /// Whether or not the instance will be cancelled if stream parsing encounters an error.
    public let automaticallyCancelOnStreamError: Bool
    /// Internal mutable state specific to this type.
    struct StreamMutableState {
        /// `OutputStream` bound to the `InputStream` produced by `asInputStream`, if it has been called.
        var outputStream: OutputStream?
        /// Stream closures called as `Data` is received.
        var streams: [(_ data: Data) -> Void] = []
        /// Number of currently executing streams. Used to ensure completions are only fired after all streams are
        /// enqueued.
        var numberOfExecutingStreams = 0
        /// Completion calls enqueued while streams are still executing.
        var enqueuedCompletionEvents: [() -> Void] = []
        /// Handler for any `HTTPURLResponse`s received.
        var httpResponseHandler: (queue: DispatchQueue,
                                  handler: (_ response: HTTPURLResponse,
                                            _ completionHandler: @escaping (ResponseDisposition) -> Void) -> Void)?
    }
    let streamMutableState = Protected(StreamMutableState())
    /// Creates a `DataStreamRequest` using the provided parameters.
    ///
    /// - Parameters:
    ///   - id:                               `UUID` used for the `Hashable` and `Equatable` implementations. `UUID()`
    ///                                        by default.
    ///   - convertible:                      `URLRequestConvertible` value used to create `URLRequest`s for this
    ///                                        instance.
    ///   - automaticallyCancelOnStreamError: `Bool` indicating whether the instance will be cancelled when an `Error`
    ///                                       is thrown while serializing stream `Data`.
    ///   - underlyingQueue:                  `DispatchQueue` on which all internal `Request` work is performed.
    ///   - serializationQueue:               `DispatchQueue` on which all serialization work is performed. By default
    ///                                       targets
    ///                                       `underlyingQueue`, but can be passed another queue from a `Session`.
    ///   - eventMonitor:                     `EventMonitor` called for event callbacks from internal `Request` actions.
    ///   - interceptor:                      `RequestInterceptor` used throughout the request lifecycle.
    ///   - delegate:                         `RequestDelegate` that provides an interface to actions not performed by
    ///                                       the `Request`.
    init(id: UUID = UUID(),
         convertible: URLRequestConvertible,
         automaticallyCancelOnStreamError: Bool,
         underlyingQueue: DispatchQueue,
         serializationQueue: DispatchQueue,
         eventMonitor: EventMonitor?,
         interceptor: RequestInterceptor?,
         delegate: RequestDelegate) {
        self.convertible = convertible
        self.automaticallyCancelOnStreamError = automaticallyCancelOnStreamError
        super.init(id: id,
                   underlyingQueue: underlyingQueue,
                   serializationQueue: serializationQueue,
                   eventMonitor: eventMonitor,
                   interceptor: interceptor,
                   delegate: delegate)
    }
    override func task(for request: URLRequest, using session: URLSession) -> URLSessionTask {
        let copiedRequest = request
        return session.dataTask(with: copiedRequest)
    }
    override func finish(error: AFError? = nil) {
        streamMutableState.write { state in
            state.outputStream?.close()
        }
        super.finish(error: error)
    }
    func didReceive(data: Data) {
        streamMutableState.write { state in
            #if !canImport(FoundationNetworking) // If we not using swift-corelibs-foundation.
            if let stream = state.outputStream {
                underlyingQueue.async {
                    var bytes = Array(data)
                    stream.write(&bytes, maxLength: bytes.count)
                }
            }
            #endif
            state.numberOfExecutingStreams += state.streams.count
            let localState = state
            underlyingQueue.async { localState.streams.forEach { $0(data) } }
        }
    }
    func didReceiveResponse(_ response: HTTPURLResponse, completionHandler: @escaping (URLSession.ResponseDisposition) -> Void) {
        streamMutableState.read { dataMutableState in
            guard let httpResponseHandler = dataMutableState.httpResponseHandler else {
                underlyingQueue.async { completionHandler(.allow) }
                return
            }
            httpResponseHandler.queue.async {
                httpResponseHandler.handler(response) { disposition in
                    if disposition == .cancel {
                        self.mutableState.write { mutableState in
                            mutableState.state = .cancelled
                            mutableState.error = mutableState.error ?? AFError.explicitlyCancelled
                        }
                    }
                    self.underlyingQueue.async {
                        completionHandler(disposition.sessionDisposition)
                    }
                }
            }
        }
    }
    /// Validates the `URLRequest` and `HTTPURLResponse` received for the instance using the provided `Validation` closure.
    ///
    /// - Parameter validation: `Validation` closure used to validate the request and response.
    ///
    /// - Returns:              The `DataStreamRequest`.
    @discardableResult
    public func validate(_ validation: @escaping Validation) -> Self {
        let validator: () -> Void = { [unowned self] in
            guard error == nil, let response = response else { return }
            let result = validation(request, response)
            if case let .failure(error) = result {
                self.error = error.asAFError(or: .responseValidationFailed(reason: .customValidationFailed(error: error)))
            }
            eventMonitor?.request(self,
                                  didValidateRequest: request,
                                  response: response,
                                  withResult: result)
        }
        validators.write { $0.append(validator) }
        return self
    }
    #if !canImport(FoundationNetworking) // If we not using swift-corelibs-foundation.
    /// Produces an `InputStream` that receives the `Data` received by the instance.
    ///
    /// - Note: The `InputStream` produced by this method must have `open()` called before being able to read `Data`.
    ///         Additionally, this method will automatically call `resume()` on the instance, regardless of whether or
    ///         not the creating session has `startRequestsImmediately` set to `true`.
    ///
    /// - Parameter bufferSize: Size, in bytes, of the buffer between the `OutputStream` and `InputStream`.
    ///
    /// - Returns:              The `InputStream` bound to the internal `OutboundStream`.
    public func asInputStream(bufferSize: Int = 1024) -> InputStream? {
        defer { resume() }
        var inputStream: InputStream?
        streamMutableState.write { state in
            Foundation.Stream.getBoundStreams(withBufferSize: bufferSize,
                                              inputStream: &inputStream,
                                              outputStream: &state.outputStream)
            state.outputStream?.open()
        }
        return inputStream
    }
    #endif
    /// Sets a closure called whenever the `DataRequest` produces an `HTTPURLResponse` and providing a completion
    /// handler to return a `ResponseDisposition` value.
    ///
    /// - Parameters:
    ///   - queue:   `DispatchQueue` on which the closure will be called. `.main` by default.
    ///   - handler: Closure called when the instance produces an `HTTPURLResponse`. The `completionHandler` provided
    ///              MUST be called, otherwise the request will never complete.
    ///
    /// - Returns:   The instance.
    @_disfavoredOverload
    @discardableResult
    public func onHTTPResponse(
        on queue: DispatchQueue = .main,
        perform handler: @escaping (_ response: HTTPURLResponse,
                                    _ completionHandler: @escaping (ResponseDisposition) -> Void) -> Void
    ) -> Self {
        streamMutableState.write { mutableState in
            mutableState.httpResponseHandler = (queue, handler)
        }
        return self
    }
    /// Sets a closure called whenever the `DataRequest` produces an `HTTPURLResponse`.
    ///
    /// - Parameters:
    ///   - queue:   `DispatchQueue` on which the closure will be called. `.main` by default.
    ///   - handler: Closure called when the instance produces an `HTTPURLResponse`.
    ///
    /// - Returns:   The instance.
    @discardableResult
    public func onHTTPResponse(on queue: DispatchQueue = .main,
                               perform handler: @escaping (HTTPURLResponse) -> Void) -> Self {
        onHTTPResponse(on: queue) { response, completionHandler in
            handler(response)
            completionHandler(.allow)
        }
        return self
    }
    func capturingError(from closure: () throws -> Void) {
        do {
            try closure()
        } catch {
            self.error = error.asAFError(or: .responseSerializationFailed(reason: .customSerializationFailed(error: error)))
            cancel()
        }
    }
    func appendStreamCompletion<Success, Failure>(on queue: DispatchQueue,
                                                  stream: @escaping Handler<Success, Failure>) {
        appendResponseSerializer {
            self.underlyingQueue.async {
                self.responseSerializerDidComplete {
                    self.streamMutableState.write { state in
                        guard state.numberOfExecutingStreams == 0 else {
                            state.enqueuedCompletionEvents.append {
                                self.enqueueCompletion(on: queue, stream: stream)
                            }
                            return
                        }
                        self.enqueueCompletion(on: queue, stream: stream)
                    }
                }
            }
        }
    }
    func enqueueCompletion<Success, Failure>(on queue: DispatchQueue,
                                             stream: @escaping Handler<Success, Failure>) {
        queue.async {
            do {
                let completion = Completion(request: self.request,
                                            response: self.response,
                                            metrics: self.metrics,
                                            error: self.error)
                try stream(.init(event: .complete(completion), token: .init(self)))
            } catch {
                // Ignore error, as errors on Completion can't be handled anyway.
            }
        }
    }
}
extension DataStreamRequest.Stream {
    /// Incoming `Result` values from `Event.stream`.
    public var result: Result<Success, Failure>? {
        guard case let .stream(result) = event else { return nil }
        return result
    }
    /// `Success` value of the instance, if any.
    public var value: Success? {
        guard case let .success(value) = result else { return nil }
        return value
    }
    /// `Failure` value of the instance, if any.
    public var error: Failure? {
        guard case let .failure(error) = result else { return nil }
        return error
    }
    /// `Completion` value of the instance, if any.
    public var completion: DataStreamRequest.Completion? {
        guard case let .complete(completion) = event else { return nil }
        return completion
    }
}
// MARK: - DownloadRequest
/// `Request` subclass which downloads `Data` to a file on disk using `URLSessionDownloadTask`.
public class DownloadRequest: Request {
    /// A set of options to be executed prior to moving a downloaded file from the temporary `URL` to the destination
    /// `URL`.
    public struct Options: OptionSet {
        /// Specifies that intermediate directories for the destination URL should be created.
        public static let createIntermediateDirectories = Options(rawValue: 1 << 0)
        /// Specifies that any previous file at the destination `URL` should be removed.
        public static let removePreviousFile = Options(rawValue: 1 << 1)
        public let rawValue: Int
        public init(rawValue: Int) {
            self.rawValue = rawValue
        }
    }
    // MARK: Destination
    /// A closure executed once a `DownloadRequest` has successfully completed in order to determine where to move the
    /// temporary file written to during the download process. The closure takes two arguments: the temporary file URL
    /// and the `HTTPURLResponse`, and returns two values: the file URL where the temporary file should be moved and
    /// the options defining how the file should be moved.
    ///
    /// - Note: Downloads from a local `file://` `URL`s do not use the `Destination` closure, as those downloads do not
    ///         return an `HTTPURLResponse`. Instead the file is merely moved within the temporary directory.
    public typealias Destination = (_ temporaryURL: URL,
                                    _ response: HTTPURLResponse) -> (destinationURL: URL, options: Options)
    /// Creates a download file destination closure which uses the default file manager to move the temporary file to a
    /// file URL in the first available directory with the specified search path directory and search path domain mask.
    ///
    /// - Parameters:
    ///   - directory: The search path directory. `.documentDirectory` by default.
    ///   - domain:    The search path domain mask. `.userDomainMask` by default.
    ///   - options:   `DownloadRequest.Options` used when moving the downloaded file to its destination. None by
    ///                default.
    /// - Returns: The `Destination` closure.
    public class func suggestedDownloadDestination(for directory: FileManager.SearchPathDirectory = .documentDirectory,
                                                   in domain: FileManager.SearchPathDomainMask = .userDomainMask,
                                                   options: Options = []) -> Destination {
        { temporaryURL, response in
            let directoryURLs = FileManager.default.urls(for: directory, in: domain)
            let url = directoryURLs.first?.appendingPathComponent(response.suggestedFilename!) ?? temporaryURL
            return (url, options)
        }
    }
    /// Default `Destination` used by Alamofire to ensure all downloads persist. This `Destination` prepends
    /// `Alamofire_` to the automatically generated download name and moves it within the temporary directory. Files
    /// with this destination must be additionally moved if they should survive the system reclamation of temporary
    /// space.
    static let defaultDestination: Destination = { url, _ in
        (defaultDestinationURL(url), [])
    }
    /// Default `URL` creation closure. Creates a `URL` in the temporary directory with `Alamofire_` prepended to the
    /// provided file name.
    static let defaultDestinationURL: (URL) -> URL = { url in
        let filename = "Alamofire_\(url.lastPathComponent)"
        let destination = url.deletingLastPathComponent().appendingPathComponent(filename)
        return destination
    }
    // MARK: Downloadable
    /// Type describing the source used to create the underlying `URLSessionDownloadTask`.
    public enum Downloadable {
        /// Download should be started from the `URLRequest` produced by the associated `URLRequestConvertible` value.
        case request(URLRequestConvertible)
        /// Download should be started from the associated resume `Data` value.
        case resumeData(Data)
    }
    // MARK: Mutable State
    /// Type containing all mutable state for `DownloadRequest` instances.
    private struct DownloadRequestMutableState {
        /// Possible resume `Data` produced when cancelling the instance.
        var resumeData: Data?
        /// `URL` to which `Data` is being downloaded.
        var fileURL: URL?
    }
    /// Protected mutable state specific to `DownloadRequest`.
    private let mutableDownloadState = Protected(DownloadRequestMutableState())
    /// If the download is resumable and is eventually cancelled or fails, this value may be used to resume the download
    /// using the `download(resumingWith data:)` API.
    ///
    /// - Note: For more information about `resumeData`, see [Apple's documentation](https://developer.apple.com/documentation/foundation/urlsessiondownloadtask/1411634-cancel).
    public var resumeData: Data? {
        #if !canImport(FoundationNetworking) // If we not using swift-corelibs-foundation.
        return mutableDownloadState.resumeData ?? error?.downloadResumeData
        #else
        return mutableDownloadState.resumeData
        #endif
    }
    /// If the download is successful, the `URL` where the file was downloaded.
    public var fileURL: URL? { mutableDownloadState.fileURL }
    // MARK: Initial State
    /// `Downloadable` value used for this instance.
    public let downloadable: Downloadable
    /// The `Destination` to which the downloaded file is moved.
    let destination: Destination
    /// Creates a `DownloadRequest` using the provided parameters.
    ///
    /// - Parameters:
    ///   - id:                 `UUID` used for the `Hashable` and `Equatable` implementations. `UUID()` by default.
    ///   - downloadable:       `Downloadable` value used to create `URLSessionDownloadTasks` for the instance.
    ///   - underlyingQueue:    `DispatchQueue` on which all internal `Request` work is performed.
    ///   - serializationQueue: `DispatchQueue` on which all serialization work is performed. By default targets
    ///                         `underlyingQueue`, but can be passed another queue from a `Session`.
    ///   - eventMonitor:       `EventMonitor` called for event callbacks from internal `Request` actions.
    ///   - interceptor:        `RequestInterceptor` used throughout the request lifecycle.
    ///   - delegate:           `RequestDelegate` that provides an interface to actions not performed by the `Request`
    ///   - destination:        `Destination` closure used to move the downloaded file to its final location.
    init(id: UUID = UUID(),
         downloadable: Downloadable,
         underlyingQueue: DispatchQueue,
         serializationQueue: DispatchQueue,
         eventMonitor: EventMonitor?,
         interceptor: RequestInterceptor?,
         delegate: RequestDelegate,
         destination: @escaping Destination) {
        self.downloadable = downloadable
        self.destination = destination
        super.init(id: id,
                   underlyingQueue: underlyingQueue,
                   serializationQueue: serializationQueue,
                   eventMonitor: eventMonitor,
                   interceptor: interceptor,
                   delegate: delegate)
    }
    override func reset() {
        super.reset()
        mutableDownloadState.write {
            $0.resumeData = nil
            $0.fileURL = nil
        }
    }
    /// Called when a download has finished.
    ///
    /// - Parameters:
    ///   - task:   `URLSessionTask` that finished the download.
    ///   - result: `Result` of the automatic move to `destination`.
    func didFinishDownloading(using task: URLSessionTask, with result: Result<URL, AFError>) {
        eventMonitor?.request(self, didFinishDownloadingUsing: task, with: result)
        switch result {
        case let .success(url): mutableDownloadState.fileURL = url
        case let .failure(error): self.error = error
        }
    }
    /// Updates the `downloadProgress` using the provided values.
    ///
    /// - Parameters:
    ///   - bytesWritten:              Total bytes written so far.
    ///   - totalBytesExpectedToWrite: Total bytes expected to write.
    func updateDownloadProgress(bytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
        downloadProgress.totalUnitCount = totalBytesExpectedToWrite
        downloadProgress.completedUnitCount += bytesWritten
        downloadProgressHandler?.queue.async { self.downloadProgressHandler?.handler(self.downloadProgress) }
    }
    override func task(for request: URLRequest, using session: URLSession) -> URLSessionTask {
        session.downloadTask(with: request)
    }
    /// Creates a `URLSessionTask` from the provided resume data.
    ///
    /// - Parameters:
    ///   - data:    `Data` used to resume the download.
    ///   - session: `URLSession` used to create the `URLSessionTask`.
    ///
    /// - Returns:   The `URLSessionTask` created.
    public func task(forResumeData data: Data, using session: URLSession) -> URLSessionTask {
        session.downloadTask(withResumeData: data)
    }
    /// Cancels the instance. Once cancelled, a `DownloadRequest` can no longer be resumed or suspended.
    ///
    /// - Note: This method will NOT produce resume data. If you wish to cancel and produce resume data, use
    ///         `cancel(producingResumeData:)` or `cancel(byProducingResumeData:)`.
    ///
    /// - Returns: The instance.
    @discardableResult
    override public func cancel() -> Self {
        cancel(producingResumeData: false)
    }
    /// Cancels the instance, optionally producing resume data. Once cancelled, a `DownloadRequest` can no longer be
    /// resumed or suspended.
    ///
    /// - Note: If `producingResumeData` is `true`, the `resumeData` property will be populated with any resume data, if
    ///         available.
    ///
    /// - Returns: The instance.
    @discardableResult
    public func cancel(producingResumeData shouldProduceResumeData: Bool) -> Self {
        cancel(optionallyProducingResumeData: shouldProduceResumeData ? { _ in } : nil)
    }
    /// Cancels the instance while producing resume data. Once cancelled, a `DownloadRequest` can no longer be resumed
    /// or suspended.
    ///
    /// - Note: The resume data passed to the completion handler will also be available on the instance's `resumeData`
    ///         property.
    ///
    /// - Parameter completionHandler: The completion handler that is called when the download has been successfully
    ///                                cancelled. It is not guaranteed to be called on a particular queue, so you may
    ///                                want use an appropriate queue to perform your work.
    ///
    /// - Returns:                     The instance.
    @discardableResult
    public func cancel(byProducingResumeData completionHandler: @escaping (_ data: Data?) -> Void) -> Self {
        cancel(optionallyProducingResumeData: completionHandler)
    }
    /// Internal implementation of cancellation that optionally takes a resume data handler. If no handler is passed,
    /// cancellation is performed without producing resume data.
    ///
    /// - Parameter completionHandler: Optional resume data handler.
    ///
    /// - Returns:                     The instance.
    private func cancel(optionallyProducingResumeData completionHandler: ((_ resumeData: Data?) -> Void)?) -> Self {
        mutableState.write { mutableState in
            guard mutableState.state.canTransitionTo(.cancelled) else { return }
            mutableState.state = .cancelled
            underlyingQueue.async { self.didCancel() }
            guard let task = mutableState.tasks.last as? URLSessionDownloadTask, task.state != .completed else {
                underlyingQueue.async { self.finish() }
                return
            }
            if let completionHandler = completionHandler {
                // Resume to ensure metrics are gathered.
                task.resume()
                task.cancel { resumeData in
                    self.mutableDownloadState.resumeData = resumeData
                    self.underlyingQueue.async { self.didCancelTask(task) }
                    completionHandler(resumeData)
                }
            } else {
                // Resume to ensure metrics are gathered.
                task.resume()
                task.cancel()
                self.underlyingQueue.async { self.didCancelTask(task) }
            }
        }
        return self
    }
    /// Validates the request, using the specified closure.
    ///
    /// - Note: If validation fails, subsequent calls to response handlers will have an associated error.
    ///
    /// - Parameter validation: `Validation` closure to validate the response.
    ///
    /// - Returns:              The instance.
    @discardableResult
    public func validate(_ validation: @escaping Validation) -> Self {
        let validator: () -> Void = { [unowned self] in
            guard error == nil, let response = response else { return }
            let result = validation(request, response, fileURL)
            if case let .failure(error) = result {
                self.error = error.asAFError(or: .responseValidationFailed(reason: .customValidationFailed(error: error)))
            }
            eventMonitor?.request(self,
                                  didValidateRequest: request,
                                  response: response,
                                  fileURL: fileURL,
                                  withResult: result)
        }
        validators.write { $0.append(validator) }
        return self
    }
}
// MARK: - UploadRequest
/// `DataRequest` subclass which handles `Data` upload from memory, file, or stream using `URLSessionUploadTask`.
public class UploadRequest: DataRequest {
    /// Type describing the origin of the upload, whether `Data`, file, or stream.
    public enum Uploadable {
        /// Upload from the provided `Data` value.
        case data(Data)
        /// Upload from the provided file `URL`, as well as a `Bool` determining whether the source file should be
        /// automatically removed once uploaded.
        case file(URL, shouldRemove: Bool)
        /// Upload from the provided `InputStream`.
        case stream(InputStream)
    }
    // MARK: Initial State
    /// The `UploadableConvertible` value used to produce the `Uploadable` value for this instance.
    public let upload: UploadableConvertible
    /// `FileManager` used to perform cleanup tasks, including the removal of multipart form encoded payloads written
    /// to disk.
    public let fileManager: FileManager
    // MARK: Mutable State
    /// `Uploadable` value used by the instance.
    public var uploadable: Uploadable?
    /// Creates an `UploadRequest` using the provided parameters.
    ///
    /// - Parameters:
    ///   - id:                 `UUID` used for the `Hashable` and `Equatable` implementations. `UUID()` by default.
    ///   - convertible:        `UploadConvertible` value used to determine the type of upload to be performed.
    ///   - underlyingQueue:    `DispatchQueue` on which all internal `Request` work is performed.
    ///   - serializationQueue: `DispatchQueue` on which all serialization work is performed. By default targets
    ///                         `underlyingQueue`, but can be passed another queue from a `Session`.
    ///   - eventMonitor:       `EventMonitor` called for event callbacks from internal `Request` actions.
    ///   - interceptor:        `RequestInterceptor` used throughout the request lifecycle.
    ///   - fileManager:        `FileManager` used to perform cleanup tasks, including the removal of multipart form
    ///                         encoded payloads written to disk.
    ///   - delegate:           `RequestDelegate` that provides an interface to actions not performed by the `Request`.
    init(id: UUID = UUID(),
         convertible: UploadConvertible,
         underlyingQueue: DispatchQueue,
         serializationQueue: DispatchQueue,
         eventMonitor: EventMonitor?,
         interceptor: RequestInterceptor?,
         fileManager: FileManager,
         delegate: RequestDelegate) {
        upload = convertible
        self.fileManager = fileManager
        super.init(id: id,
                   convertible: convertible,
                   underlyingQueue: underlyingQueue,
                   serializationQueue: serializationQueue,
                   eventMonitor: eventMonitor,
                   interceptor: interceptor,
                   delegate: delegate)
    }
    /// Called when the `Uploadable` value has been created from the `UploadConvertible`.
    ///
    /// - Parameter uploadable: The `Uploadable` that was created.
    func didCreateUploadable(_ uploadable: Uploadable) {
        self.uploadable = uploadable
        eventMonitor?.request(self, didCreateUploadable: uploadable)
    }
    /// Called when the `Uploadable` value could not be created.
    ///
    /// - Parameter error: `AFError` produced by the failure.
    func didFailToCreateUploadable(with error: AFError) {
        self.error = error
        eventMonitor?.request(self, didFailToCreateUploadableWithError: error)
        retryOrFinish(error: error)
    }
    override func task(for request: URLRequest, using session: URLSession) -> URLSessionTask {
        guard let uploadable = uploadable else {
            fatalError("Attempting to create a URLSessionUploadTask when Uploadable value doesn't exist.")
        }
        switch uploadable {
        case let .data(data): return session.uploadTask(with: request, from: data)
        case let .file(url, _): return session.uploadTask(with: request, fromFile: url)
        case .stream: return session.uploadTask(withStreamedRequest: request)
        }
    }
    override func reset() {
        // Uploadable must be recreated on every retry.
        uploadable = nil
        super.reset()
    }
    /// Produces the `InputStream` from `uploadable`, if it can.
    ///
    /// - Note: Calling this method with a non-`.stream` `Uploadable` is a logic error and will crash.
    ///
    /// - Returns: The `InputStream`.
    func inputStream() -> InputStream {
        guard let uploadable = uploadable else {
            fatalError("Attempting to access the input stream but the uploadable doesn't exist.")
        }
        guard case let .stream(stream) = uploadable else {
            fatalError("Attempted to access the stream of an UploadRequest that wasn't created with one.")
        }
        eventMonitor?.request(self, didProvideInputStream: stream)
        return stream
    }
    override public func cleanup() {
        defer { super.cleanup() }
        guard
            let uploadable = uploadable,
            case let .file(url, shouldRemove) = uploadable,
            shouldRemove
        else { return }
        try? fileManager.removeItem(at: url)
    }
}
/// A type that can produce an `UploadRequest.Uploadable` value.
public protocol UploadableConvertible {
    /// Produces an `UploadRequest.Uploadable` value from the instance.
    ///
    /// - Returns: The `UploadRequest.Uploadable`.
    /// - Throws:  Any `Error` produced during creation.
    func createUploadable() throws -> UploadRequest.Uploadable
}
extension UploadRequest.Uploadable: UploadableConvertible {
    public func createUploadable() throws -> UploadRequest.Uploadable {
        self
    }
}
/// A type that can be converted to an upload, whether from an `UploadRequest.Uploadable` or `URLRequestConvertible`.
public protocol UploadConvertible: UploadableConvertible & URLRequestConvertible {}
Diff truncated after the above file
Pods/Alamofire/Source/RequestCompression.swift Pods/Alamofire/Source/RequestInterceptor.swift Pods/Alamofire/Source/RequestTaskMap.swift Pods/Alamofire/Source/Response.swift Pods/Alamofire/Source/ResponseSerialization.swift Pods/Alamofire/Source/Result+Alamofire.swift Pods/Alamofire/Source/RetryPolicy.swift Pods/Alamofire/Source/ServerTrustEvaluation.swift Pods/Alamofire/Source/Session.swift Pods/Alamofire/Source/SessionDelegate.swift Pods/Alamofire/Source/StringEncoding+Alamofire.swift Pods/Alamofire/Source/URLConvertible+URLRequestConvertible.swift Pods/Alamofire/Source/URLEncodedFormEncoder.swift Pods/Alamofire/Source/URLRequest+Alamofire.swift Pods/Alamofire/Source/URLSessionConfiguration+Alamofire.swift Pods/Alamofire/Source/Validation.swift Pods/CryptoSwift/LICENSE Pods/CryptoSwift/README.md Pods/CryptoSwift/Sources/CryptoSwift/AEAD/AEAD.swift Pods/CryptoSwift/Sources/CryptoSwift/AEAD/AEADChaCha20Poly1305.swift Pods/CryptoSwift/Sources/CryptoSwift/AEAD/AEADXChaCha20Poly1305.swift Pods/CryptoSwift/Sources/CryptoSwift/AES.Cryptors.swift Pods/CryptoSwift/Sources/CryptoSwift/AES.swift Pods/CryptoSwift/Sources/CryptoSwift/ASN1/ASN1.swift Pods/CryptoSwift/Sources/CryptoSwift/ASN1/ASN1Decoder.swift Pods/CryptoSwift/Sources/CryptoSwift/ASN1/ASN1Encoder.swift Pods/CryptoSwift/Sources/CryptoSwift/ASN1/ASN1Scanner.swift Pods/CryptoSwift/Sources/CryptoSwift/Array+Extension.swift Pods/CryptoSwift/Sources/CryptoSwift/Authenticator.swift Pods/CryptoSwift/Sources/CryptoSwift/BatchedCollection.swift Pods/CryptoSwift/Sources/CryptoSwift/Bit.swift Pods/CryptoSwift/Sources/CryptoSwift/BlockCipher.swift Pods/CryptoSwift/Sources/CryptoSwift/BlockDecryptor.swift Pods/CryptoSwift/Sources/CryptoSwift/BlockEncryptor.swift Pods/CryptoSwift/Sources/CryptoSwift/BlockMode/BlockMode.swift Pods/CryptoSwift/Sources/CryptoSwift/BlockMode/BlockModeOptions.swift Pods/CryptoSwift/Sources/CryptoSwift/BlockMode/CBC.swift Pods/CryptoSwift/Sources/CryptoSwift/BlockMode/CCM.swift Pods/CryptoSwift/Sources/CryptoSwift/BlockMode/CFB.swift Pods/CryptoSwift/Sources/CryptoSwift/BlockMode/CTR.swift Pods/CryptoSwift/Sources/CryptoSwift/BlockMode/CipherModeWorker.swift Pods/CryptoSwift/Sources/CryptoSwift/BlockMode/ECB.swift Pods/CryptoSwift/Sources/CryptoSwift/BlockMode/GCM.swift Pods/CryptoSwift/Sources/CryptoSwift/BlockMode/OCB.swift Pods/CryptoSwift/Sources/CryptoSwift/BlockMode/OFB.swift Pods/CryptoSwift/Sources/CryptoSwift/BlockMode/PCBC.swift Pods/CryptoSwift/Sources/CryptoSwift/Blowfish.swift Pods/CryptoSwift/Sources/CryptoSwift/CBCMAC.swift Pods/CryptoSwift/Sources/CryptoSwift/CMAC.swift Pods/CryptoSwift/Sources/CryptoSwift/CS_BigInt/Addition.swift Pods/CryptoSwift/Sources/CryptoSwift/CS_BigInt/BigInt.swift Pods/CryptoSwift/Sources/CryptoSwift/CS_BigInt/BigUInt.swift Pods/CryptoSwift/Sources/CryptoSwift/CS_BigInt/Bitwise Ops.swift Pods/CryptoSwift/Sources/CryptoSwift/CS_BigInt/CS.swift Pods/CryptoSwift/Sources/CryptoSwift/CS_BigInt/Codable.swift Pods/CryptoSwift/Sources/CryptoSwift/CS_BigInt/Comparable.swift Pods/CryptoSwift/Sources/CryptoSwift/CS_BigInt/Data Conversion.swift Pods/CryptoSwift/Sources/CryptoSwift/CS_BigInt/Division.swift Pods/CryptoSwift/Sources/CryptoSwift/CS_BigInt/Exponentiation.swift Pods/CryptoSwift/Sources/CryptoSwift/CS_BigInt/Floating Point Conversion.swift Pods/CryptoSwift/Sources/CryptoSwift/CS_BigInt/GCD.swift Pods/CryptoSwift/Sources/CryptoSwift/CS_BigInt/Hashable.swift Pods/CryptoSwift/Sources/CryptoSwift/CS_BigInt/Integer Conversion.swift Pods/CryptoSwift/Sources/CryptoSwift/CS_BigInt/Multiplication.swift Pods/CryptoSwift/Sources/CryptoSwift/CS_BigInt/Prime Test.swift Pods/CryptoSwift/Sources/CryptoSwift/CS_BigInt/Random.swift Pods/CryptoSwift/Sources/CryptoSwift/CS_BigInt/Shifts.swift Pods/CryptoSwift/Sources/CryptoSwift/CS_BigInt/Square Root.swift Pods/CryptoSwift/Sources/CryptoSwift/CS_BigInt/Strideable.swift Pods/CryptoSwift/Sources/CryptoSwift/CS_BigInt/String Conversion.swift Pods/CryptoSwift/Sources/CryptoSwift/CS_BigInt/Subtraction.swift Pods/CryptoSwift/Sources/CryptoSwift/CS_BigInt/Words and Bits.swift Pods/CryptoSwift/Sources/CryptoSwift/ChaCha20.swift Pods/CryptoSwift/Sources/CryptoSwift/Checksum.swift Pods/CryptoSwift/Sources/CryptoSwift/Cipher.swift Pods/CryptoSwift/Sources/CryptoSwift/Collection+Extension.swift Pods/CryptoSwift/Sources/CryptoSwift/CompactMap.swift Pods/CryptoSwift/Sources/CryptoSwift/Cryptor.swift Pods/CryptoSwift/Sources/CryptoSwift/Cryptors.swift Pods/CryptoSwift/Sources/CryptoSwift/Digest.swift Pods/CryptoSwift/Sources/CryptoSwift/DigestType.swift Pods/CryptoSwift/Sources/CryptoSwift/Foundation/AES+Foundation.swift Pods/CryptoSwift/Sources/CryptoSwift/Foundation/Array+Foundation.swift Pods/CryptoSwift/Sources/CryptoSwift/Foundation/Blowfish+Foundation.swift Pods/CryptoSwift/Sources/CryptoSwift/Foundation/ChaCha20+Foundation.swift Pods/CryptoSwift/Sources/CryptoSwift/Foundation/Data+Extension.swift Pods/CryptoSwift/Sources/CryptoSwift/Foundation/HMAC+Foundation.swift Pods/CryptoSwift/Sources/CryptoSwift/Foundation/Rabbit+Foundation.swift Pods/CryptoSwift/Sources/CryptoSwift/Foundation/String+FoundationExtension.swift Pods/CryptoSwift/Sources/CryptoSwift/Foundation/Utils+Foundation.swift Pods/CryptoSwift/Sources/CryptoSwift/Foundation/XChaCha20+Foundation.swift Pods/CryptoSwift/Sources/CryptoSwift/Generics.swift Pods/CryptoSwift/Sources/CryptoSwift/HKDF.swift Pods/CryptoSwift/Sources/CryptoSwift/HMAC.swift Pods/CryptoSwift/Sources/CryptoSwift/ISO10126Padding.swift Pods/CryptoSwift/Sources/CryptoSwift/ISO78164Padding.swift Pods/CryptoSwift/Sources/CryptoSwift/Int+Extension.swift Pods/CryptoSwift/Sources/CryptoSwift/MD5.swift Pods/CryptoSwift/Sources/CryptoSwift/NoPadding.swift Pods/CryptoSwift/Sources/CryptoSwift/Operators.swift Pods/CryptoSwift/Sources/CryptoSwift/PEM/DER.swift Pods/CryptoSwift/Sources/CryptoSwift/PKCS/PBKDF1.swift Pods/CryptoSwift/Sources/CryptoSwift/PKCS/PBKDF2.swift Pods/CryptoSwift/Sources/CryptoSwift/PKCS/PKCS1v15.swift Pods/CryptoSwift/Sources/CryptoSwift/PKCS/PKCS5.swift Pods/CryptoSwift/Sources/CryptoSwift/PKCS/PKCS7.swift Pods/CryptoSwift/Sources/CryptoSwift/PKCS/PKCS7Padding.swift Pods/CryptoSwift/Sources/CryptoSwift/Padding.swift Pods/CryptoSwift/Sources/CryptoSwift/Poly1305.swift Pods/CryptoSwift/Sources/CryptoSwift/RSA/RSA+Cipher.swift Pods/CryptoSwift/Sources/CryptoSwift/RSA/RSA+Signature.swift Pods/CryptoSwift/Sources/CryptoSwift/RSA/RSA.swift Pods/CryptoSwift/Sources/CryptoSwift/Rabbit.swift Pods/CryptoSwift/Sources/CryptoSwift/SHA1.swift Pods/CryptoSwift/Sources/CryptoSwift/SHA2.swift Pods/CryptoSwift/Sources/CryptoSwift/SHA3.swift Pods/CryptoSwift/Sources/CryptoSwift/Scrypt.swift Pods/CryptoSwift/Sources/CryptoSwift/SecureBytes.swift Pods/CryptoSwift/Sources/CryptoSwift/Signature.swift Pods/CryptoSwift/Sources/CryptoSwift/StreamDecryptor.swift Pods/CryptoSwift/Sources/CryptoSwift/StreamEncryptor.swift Pods/CryptoSwift/Sources/CryptoSwift/String+Extension.swift Pods/CryptoSwift/Sources/CryptoSwift/UInt128.swift Pods/CryptoSwift/Sources/CryptoSwift/UInt16+Extension.swift Pods/CryptoSwift/Sources/CryptoSwift/UInt32+Extension.swift Pods/CryptoSwift/Sources/CryptoSwift/UInt64+Extension.swift Pods/CryptoSwift/Sources/CryptoSwift/UInt8+Extension.swift Pods/CryptoSwift/Sources/CryptoSwift/Updatable.swift Pods/CryptoSwift/Sources/CryptoSwift/Utils.swift Pods/CryptoSwift/Sources/CryptoSwift/XChaCha20.swift Pods/CryptoSwift/Sources/CryptoSwift/ZeroPadding.swift Pods/Differentiator/LICENSE.md Pods/Differentiator/README.md Pods/Differentiator/Sources/Differentiator/AnimatableSectionModel.swift Pods/Differentiator/Sources/Differentiator/AnimatableSectionModelType+ItemPath.swift Pods/Differentiator/Sources/Differentiator/AnimatableSectionModelType.swift Pods/Differentiator/Sources/Differentiator/Changeset.swift Pods/Differentiator/Sources/Differentiator/Diff.swift Pods/Differentiator/Sources/Differentiator/IdentifiableType.swift Pods/Differentiator/Sources/Differentiator/IdentifiableValue.swift Pods/Differentiator/Sources/Differentiator/ItemPath.swift Pods/Differentiator/Sources/Differentiator/Optional+Extensions.swift Pods/Differentiator/Sources/Differentiator/SectionModel.swift Pods/Differentiator/Sources/Differentiator/SectionModelType.swift Pods/Differentiator/Sources/Differentiator/Utilities.swift Pods/EmptyDataSet-Swift/EmptyDataSet-Swift/Sources/EmptyDataSet.swift Pods/EmptyDataSet-Swift/EmptyDataSet-Swift/Sources/EmptyDataSetDelegate.swift Pods/EmptyDataSet-Swift/EmptyDataSet-Swift/Sources/EmptyDataSetSource.swift Pods/EmptyDataSet-Swift/EmptyDataSet-Swift/Sources/EmptyDataSetView+Extension.swift Pods/EmptyDataSet-Swift/EmptyDataSet-Swift/Sources/EmptyDataSetView.swift Pods/EmptyDataSet-Swift/LICENSE Pods/EmptyDataSet-Swift/README.md Pods/FFPage/FFPage/Controller/FFAdapterViewController.h Pods/FFPage/FFPage/Controller/FFAdapterViewController.m Pods/FFPage/FFPage/Controller/FFPageViewController.h Pods/FFPage/FFPage/Controller/FFPageViewController.m Pods/FFPage/FFPage/FFPage.h Pods/FFPage/FFPage/Protocol/FFPageProtocol.h Pods/FFPage/FFPage/Refresh/FFRereshView.h Pods/FFPage/FFPage/Refresh/FFRereshView.m Pods/FFPage/FFPage/Utils/FFDynamicItem.h Pods/FFPage/FFPage/Utils/FFDynamicItem.m Pods/FFPage/FFPage/Utils/UIScrollView+FFPage.h Pods/FFPage/FFPage/Utils/UIScrollView+FFPage.m Pods/FFPage/LICENSE Pods/FFPage/README.md Pods/HandyJSON/LICENSE Pods/HandyJSON/README.md Pods/HandyJSON/Source/AnyExtensions.swift Pods/HandyJSON/Source/BuiltInBasicType.swift Pods/HandyJSON/Source/BuiltInBridgeType.swift Pods/HandyJSON/Source/CBridge.swift Pods/HandyJSON/Source/Configuration.swift Pods/HandyJSON/Source/ContextDescriptorType.swift Pods/HandyJSON/Source/CustomDateFormatTransform.swift Pods/HandyJSON/Source/DataTransform.swift Pods/HandyJSON/Source/DateFormatterTransform.swift Pods/HandyJSON/Source/DateTransform.swift Pods/HandyJSON/Source/Deserializer.swift Pods/HandyJSON/Source/EnumTransform.swift Pods/HandyJSON/Source/EnumType.swift Pods/HandyJSON/Source/Export.swift Pods/HandyJSON/Source/ExtendCustomBasicType.swift Pods/HandyJSON/Source/ExtendCustomModelType.swift Pods/HandyJSON/Source/FieldDescriptor.swift Pods/HandyJSON/Source/HandyJSON.h Pods/HandyJSON/Source/HelpingMapper.swift Pods/HandyJSON/Source/HexColorTransform.swift Pods/HandyJSON/Source/ISO8601DateTransform.swift Pods/HandyJSON/Source/Logger.swift Pods/HandyJSON/Source/MangledName.swift Pods/HandyJSON/Source/Measuable.swift Pods/HandyJSON/Source/Metadata.swift Pods/HandyJSON/Source/NSDecimalNumberTransform.swift Pods/HandyJSON/Source/OtherExtension.swift Pods/HandyJSON/Source/PointerType.swift Pods/HandyJSON/Source/Properties.swift Pods/HandyJSON/Source/PropertyInfo.swift Pods/HandyJSON/Source/ReflectionHelper.swift Pods/HandyJSON/Source/Serializer.swift Pods/HandyJSON/Source/TransformOf.swift Pods/HandyJSON/Source/TransformType.swift Pods/HandyJSON/Source/Transformable.swift Pods/HandyJSON/Source/URLTransform.swift Pods/IQKeyboardManager/IQKeyboardManager/Categories/IQNSArray+Sort.h Pods/IQKeyboardManager/IQKeyboardManager/Categories/IQNSArray+Sort.m Pods/IQKeyboardManager/IQKeyboardManager/Categories/IQUIScrollView+Additions.h Pods/IQKeyboardManager/IQKeyboardManager/Categories/IQUIScrollView+Additions.m Pods/IQKeyboardManager/IQKeyboardManager/Categories/IQUITextFieldView+Additions.h Pods/IQKeyboardManager/IQKeyboardManager/Categories/IQUITextFieldView+Additions.m Pods/IQKeyboardManager/IQKeyboardManager/Categories/IQUIView+Hierarchy.h Pods/IQKeyboardManager/IQKeyboardManager/Categories/IQUIView+Hierarchy.m Pods/IQKeyboardManager/IQKeyboardManager/Categories/IQUIViewController+Additions.h Pods/IQKeyboardManager/IQKeyboardManager/Categories/IQUIViewController+Additions.m Pods/IQKeyboardManager/IQKeyboardManager/Constants/IQKeyboardManagerConstants.h Pods/IQKeyboardManager/IQKeyboardManager/Constants/IQKeyboardManagerConstantsInternal.h Pods/IQKeyboardManager/IQKeyboardManager/IQKeyboardManager.h Pods/IQKeyboardManager/IQKeyboardManager/IQKeyboardManager.m Pods/IQKeyboardManager/IQKeyboardManager/IQKeyboardReturnKeyHandler.h Pods/IQKeyboardManager/IQKeyboardManager/IQKeyboardReturnKeyHandler.m Pods/IQKeyboardManager/IQKeyboardManager/IQTextView/IQTextView.h Pods/IQKeyboardManager/IQKeyboardManager/IQTextView/IQTextView.m Pods/IQKeyboardManager/IQKeyboardManager/IQToolbar/IQBarButtonItem.h Pods/IQKeyboardManager/IQKeyboardManager/IQToolbar/IQBarButtonItem.m Pods/IQKeyboardManager/IQKeyboardManager/IQToolbar/IQPreviousNextView.h Pods/IQKeyboardManager/IQKeyboardManager/IQToolbar/IQPreviousNextView.m Pods/IQKeyboardManager/IQKeyboardManager/IQToolbar/IQTitleBarButtonItem.h Pods/IQKeyboardManager/IQKeyboardManager/IQToolbar/IQTitleBarButtonItem.m Pods/IQKeyboardManager/IQKeyboardManager/IQToolbar/IQToolbar.h Pods/IQKeyboardManager/IQKeyboardManager/IQToolbar/IQToolbar.m Pods/IQKeyboardManager/IQKeyboardManager/IQToolbar/IQUIView+IQKeyboardToolbar.h Pods/IQKeyboardManager/IQKeyboardManager/IQToolbar/IQUIView+IQKeyboardToolbar.m Pods/IQKeyboardManager/IQKeyboardManager/PrivacyInfo.xcprivacy Pods/IQKeyboardManager/LICENSE.md Pods/IQKeyboardManager/README.md Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/Categories/IQNSArray+Sort.swift Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/Categories/IQUIScrollView+Additions.swift Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/Categories/IQUITextFieldView+Additions.swift Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/Categories/IQUIView+Hierarchy.swift Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/Categories/IQUIViewController+Additions.swift Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/Constants/IQKeyboardManagerConstants.swift Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/Constants/IQKeyboardManagerConstantsInternal.swift Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQKeyboardManager+Debug.swift Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQKeyboardManager+Internal.swift Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQKeyboardManager+OrientationNotification.swift Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQKeyboardManager+Position.swift Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQKeyboardManager+Toolbar.swift Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQKeyboardManager+UIKeyboardNotification.swift Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQKeyboardManager+UITextFieldViewNotification.swift Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQKeyboardManager.swift Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQKeyboardReturnKeyHandler.swift Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQTextView/IQPlaceholderable.swift Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQTextView/IQTextView.swift Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQToolbar/IQBarButtonItem.swift Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQToolbar/IQInvocation.swift Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQToolbar/IQPreviousNextView.swift Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQToolbar/IQTitleBarButtonItem.swift Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQToolbar/IQToolbar.swift Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQToolbar/IQUIView+IQKeyboardToolbar.swift Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/PrivacyInfo.xcprivacy Pods/IQKeyboardManagerSwift/LICENSE.md Pods/IQKeyboardManagerSwift/README.md Pods/Lantern/LICENSE Pods/Lantern/README.md Pods/Lantern/Sources/Lantern/Lantern.swift Pods/Lantern/Sources/Lantern/LanternAnimatedTransitioning.swift Pods/Lantern/Sources/Lantern/LanternCell.swift Pods/Lantern/Sources/Lantern/LanternDefaultPageIndicator.swift Pods/Lantern/Sources/Lantern/LanternFadeAnimator.swift Pods/Lantern/Sources/Lantern/LanternImageCell.swift Pods/Lantern/Sources/Lantern/LanternLog.swift Pods/Lantern/Sources/Lantern/LanternNoneAnimator.swift Pods/Lantern/Sources/Lantern/LanternNumberPageIndicator.swift Pods/Lantern/Sources/Lantern/LanternPageIndicator.swift Pods/Lantern/Sources/Lantern/LanternPhotoVideoCell.swift Pods/Lantern/Sources/Lantern/LanternSmoothZoomAnimator.swift Pods/Lantern/Sources/Lantern/LanternVideoPlayer.swift Pods/Lantern/Sources/Lantern/LanternView.swift Pods/Lantern/Sources/Lantern/LanternZoomAnimator.swift Pods/Lantern/Sources/Lantern/LanternZoomSupportedCell.swift Pods/Local Podspecs/JQTools.podspec.json Pods/MJRefresh/LICENSE Pods/MJRefresh/MJRefresh/Base/MJRefreshAutoFooter.h Pods/MJRefresh/MJRefresh/Base/MJRefreshAutoFooter.m Pods/MJRefresh/MJRefresh/Base/MJRefreshBackFooter.h Pods/MJRefresh/MJRefresh/Base/MJRefreshBackFooter.m Pods/MJRefresh/MJRefresh/Base/MJRefreshComponent.h Pods/MJRefresh/MJRefresh/Base/MJRefreshComponent.m Pods/MJRefresh/MJRefresh/Base/MJRefreshFooter.h Pods/MJRefresh/MJRefresh/Base/MJRefreshFooter.m Pods/MJRefresh/MJRefresh/Base/MJRefreshHeader.h Pods/MJRefresh/MJRefresh/Base/MJRefreshHeader.m Pods/MJRefresh/MJRefresh/Base/MJRefreshTrailer.h Pods/MJRefresh/MJRefresh/Base/MJRefreshTrailer.m Pods/MJRefresh/MJRefresh/Custom/Footer/Auto/MJRefreshAutoGifFooter.h Pods/MJRefresh/MJRefresh/Custom/Footer/Auto/MJRefreshAutoGifFooter.m Pods/MJRefresh/MJRefresh/Custom/Footer/Auto/MJRefreshAutoNormalFooter.h Pods/MJRefresh/MJRefresh/Custom/Footer/Auto/MJRefreshAutoNormalFooter.m Pods/MJRefresh/MJRefresh/Custom/Footer/Auto/MJRefreshAutoStateFooter.h Pods/MJRefresh/MJRefresh/Custom/Footer/Auto/MJRefreshAutoStateFooter.m Pods/MJRefresh/MJRefresh/Custom/Footer/Back/MJRefreshBackGifFooter.h Pods/MJRefresh/MJRefresh/Custom/Footer/Back/MJRefreshBackGifFooter.m Pods/MJRefresh/MJRefresh/Custom/Footer/Back/MJRefreshBackNormalFooter.h Pods/MJRefresh/MJRefresh/Custom/Footer/Back/MJRefreshBackNormalFooter.m Pods/MJRefresh/MJRefresh/Custom/Footer/Back/MJRefreshBackStateFooter.h Pods/MJRefresh/MJRefresh/Custom/Footer/Back/MJRefreshBackStateFooter.m Pods/MJRefresh/MJRefresh/Custom/Header/MJRefreshGifHeader.h Pods/MJRefresh/MJRefresh/Custom/Header/MJRefreshGifHeader.m Pods/MJRefresh/MJRefresh/Custom/Header/MJRefreshNormalHeader.h Pods/MJRefresh/MJRefresh/Custom/Header/MJRefreshNormalHeader.m Pods/MJRefresh/MJRefresh/Custom/Header/MJRefreshStateHeader.h Pods/MJRefresh/MJRefresh/Custom/Header/MJRefreshStateHeader.m Pods/MJRefresh/MJRefresh/Custom/Trailer/MJRefreshNormalTrailer.h Pods/MJRefresh/MJRefresh/Custom/Trailer/MJRefreshNormalTrailer.m Pods/MJRefresh/MJRefresh/Custom/Trailer/MJRefreshStateTrailer.h Pods/MJRefresh/MJRefresh/Custom/Trailer/MJRefreshStateTrailer.m Pods/MJRefresh/MJRefresh/MJRefresh.bundle/arrow@2x.png Pods/MJRefresh/MJRefresh/MJRefresh.bundle/en.lproj/Localizable.strings Pods/MJRefresh/MJRefresh/MJRefresh.bundle/ko.lproj/Localizable.strings Pods/MJRefresh/MJRefresh/MJRefresh.bundle/ru.lproj/Localizable.strings Pods/MJRefresh/MJRefresh/MJRefresh.bundle/trail_arrow@2x.png Pods/MJRefresh/MJRefresh/MJRefresh.bundle/uk.lproj/Localizable.strings Pods/MJRefresh/MJRefresh/MJRefresh.bundle/zh-Hans.lproj/Localizable.strings Pods/MJRefresh/MJRefresh/MJRefresh.bundle/zh-Hant.lproj/Localizable.strings Pods/MJRefresh/MJRefresh/MJRefresh.h Pods/MJRefresh/MJRefresh/MJRefreshConfig.h Pods/MJRefresh/MJRefresh/MJRefreshConfig.m Pods/MJRefresh/MJRefresh/MJRefreshConst.h Pods/MJRefresh/MJRefresh/MJRefreshConst.m Pods/MJRefresh/MJRefresh/NSBundle+MJRefresh.h Pods/MJRefresh/MJRefresh/NSBundle+MJRefresh.m Pods/MJRefresh/MJRefresh/UICollectionViewLayout+MJRefresh.h Pods/MJRefresh/MJRefresh/UICollectionViewLayout+MJRefresh.m Pods/MJRefresh/MJRefresh/UIScrollView+MJExtension.h Pods/MJRefresh/MJRefresh/UIScrollView+MJExtension.m Pods/MJRefresh/MJRefresh/UIScrollView+MJRefresh.h Pods/MJRefresh/MJRefresh/UIScrollView+MJRefresh.m Pods/MJRefresh/MJRefresh/UIView+MJExtension.h Pods/MJRefresh/MJRefresh/UIView+MJExtension.m Pods/MJRefresh/README.md Pods/Manifest.lock Pods/ObjcExceptionBridging/LICENSE.txt Pods/ObjcExceptionBridging/README.md Pods/ObjcExceptionBridging/Sources/ObjcExceptionBridging/ObjectiveCMarker.m Pods/ObjcExceptionBridging/Sources/ObjcExceptionBridging/include/ObjcExceptionBridging.h Pods/ObjectMapper/LICENSE Pods/ObjectMapper/README-CN.md Pods/ObjectMapper/Sources/CodableTransform.swift Pods/ObjectMapper/Sources/CustomDateFormatTransform.swift Pods/ObjectMapper/Sources/DataTransform.swift Pods/ObjectMapper/Sources/DateFormatterTransform.swift Pods/ObjectMapper/Sources/DateTransform.swift Pods/ObjectMapper/Sources/DictionaryTransform.swift Pods/ObjectMapper/Sources/EnumOperators.swift Pods/ObjectMapper/Sources/EnumTransform.swift Pods/ObjectMapper/Sources/FromJSON.swift Pods/ObjectMapper/Sources/HexColorTransform.swift Pods/ObjectMapper/Sources/ISO8601DateTransform.swift Pods/ObjectMapper/Sources/ImmutableMappable.swift Pods/ObjectMapper/Sources/IntegerOperators.swift Pods/ObjectMapper/Sources/Map.swift Pods/ObjectMapper/Sources/MapError.swift Pods/ObjectMapper/Sources/Mappable.swift Pods/ObjectMapper/Sources/Mapper.swift Pods/ObjectMapper/Sources/NSDecimalNumberTransform.swift Pods/ObjectMapper/Sources/Operators.swift Pods/ObjectMapper/Sources/ToJSON.swift Pods/ObjectMapper/Sources/TransformOf.swift Pods/ObjectMapper/Sources/TransformOperators.swift Pods/ObjectMapper/Sources/TransformType.swift Pods/ObjectMapper/Sources/URLTransform.swift Pods/Pods.xcodeproj/project.pbxproj Pods/Pods.xcodeproj/xcuserdata/yvkd.xcuserdatad/xcschemes/Alamofire.xcscheme Pods/Pods.xcodeproj/xcuserdata/yvkd.xcuserdatad/xcschemes/CryptoSwift.xcscheme Pods/Pods.xcodeproj/xcuserdata/yvkd.xcuserdatad/xcschemes/Differentiator.xcscheme Pods/Pods.xcodeproj/xcuserdata/yvkd.xcuserdatad/xcschemes/EmptyDataSet-Swift.xcscheme Pods/Pods.xcodeproj/xcuserdata/yvkd.xcuserdatad/xcschemes/FFPage.xcscheme Pods/Pods.xcodeproj/xcuserdata/yvkd.xcuserdatad/xcschemes/HandyJSON.xcscheme Pods/Pods.xcodeproj/xcuserdata/yvkd.xcuserdatad/xcschemes/IQKeyboardManager.xcscheme Pods/Pods.xcodeproj/xcuserdata/yvkd.xcuserdatad/xcschemes/IQKeyboardManagerSwift.xcscheme Pods/Pods.xcodeproj/xcuserdata/yvkd.xcuserdatad/xcschemes/JQTools-JQToolsRes.xcscheme Pods/Pods.xcodeproj/xcuserdata/yvkd.xcuserdatad/xcschemes/JQTools.xcscheme Pods/Pods.xcodeproj/xcuserdata/yvkd.xcuserdatad/xcschemes/Lantern.xcscheme Pods/Pods.xcodeproj/xcuserdata/yvkd.xcuserdatad/xcschemes/MJRefresh.xcscheme Pods/Pods.xcodeproj/xcuserdata/yvkd.xcuserdatad/xcschemes/ObjcExceptionBridging.xcscheme Pods/Pods.xcodeproj/xcuserdata/yvkd.xcuserdatad/xcschemes/ObjectMapper.xcscheme Pods/Pods.xcodeproj/xcuserdata/yvkd.xcuserdatad/xcschemes/Pods-DolphinEnglishLearnStudent.xcscheme Pods/Pods.xcodeproj/xcuserdata/yvkd.xcuserdatad/xcschemes/QMUIKit-QMUIResources.xcscheme Pods/Pods.xcodeproj/xcuserdata/yvkd.xcuserdatad/xcschemes/QMUIKit.xcscheme Pods/Pods.xcodeproj/xcuserdata/yvkd.xcuserdatad/xcschemes/RxCocoa.xcscheme Pods/Pods.xcodeproj/xcuserdata/yvkd.xcuserdatad/xcschemes/RxDataSources.xcscheme Pods/Pods.xcodeproj/xcuserdata/yvkd.xcuserdatad/xcschemes/RxRelay.xcscheme Pods/Pods.xcodeproj/xcuserdata/yvkd.xcuserdatad/xcschemes/RxSwift.xcscheme Pods/Pods.xcodeproj/xcuserdata/yvkd.xcuserdatad/xcschemes/SDWebImage.xcscheme Pods/Pods.xcodeproj/xcuserdata/yvkd.xcuserdatad/xcschemes/SPPageMenu.xcscheme Pods/Pods.xcodeproj/xcuserdata/yvkd.xcuserdatad/xcschemes/SVProgressHUD.xcscheme Pods/Pods.xcodeproj/xcuserdata/yvkd.xcuserdatad/xcschemes/SnapKit.xcscheme Pods/Pods.xcodeproj/xcuserdata/yvkd.xcuserdatad/xcschemes/SwifterSwift.xcscheme Pods/Pods.xcodeproj/xcuserdata/yvkd.xcuserdatad/xcschemes/SwiftyStoreKit.xcscheme Pods/Pods.xcodeproj/xcuserdata/yvkd.xcuserdatad/xcschemes/TZImagePickerController.xcscheme Pods/Pods.xcodeproj/xcuserdata/yvkd.xcuserdatad/xcschemes/UserDefaultsStore.xcscheme Pods/Pods.xcodeproj/xcuserdata/yvkd.xcuserdatad/xcschemes/VTMagic.xcscheme Pods/Pods.xcodeproj/xcuserdata/yvkd.xcuserdatad/xcschemes/WechatOpenSDK-XCFramework.xcscheme Pods/Pods.xcodeproj/xcuserdata/yvkd.xcuserdatad/xcschemes/XCGLogger.xcscheme Pods/Pods.xcodeproj/xcuserdata/yvkd.xcuserdatad/xcschemes/xcschememanagement.plist Pods/QMUIKit/LICENSE.TXT Pods/QMUIKit/QMUIConfigurationTemplate/QMUIConfigurationTemplate.h Pods/QMUIKit/QMUIConfigurationTemplate/QMUIConfigurationTemplate.m Pods/QMUIKit/QMUIKit/QMUIComponents/AssetLibrary/QMUIAsset.h Pods/QMUIKit/QMUIKit/QMUIComponents/AssetLibrary/QMUIAsset.m Pods/QMUIKit/QMUIKit/QMUIComponents/AssetLibrary/QMUIAssetsGroup.h Pods/QMUIKit/QMUIKit/QMUIComponents/AssetLibrary/QMUIAssetsGroup.m Pods/QMUIKit/QMUIKit/QMUIComponents/AssetLibrary/QMUIAssetsManager.h Pods/QMUIKit/QMUIKit/QMUIComponents/AssetLibrary/QMUIAssetsManager.m Pods/QMUIKit/QMUIKit/QMUIComponents/CAAnimation+QMUI.h Pods/QMUIKit/QMUIKit/QMUIComponents/CAAnimation+QMUI.m Pods/QMUIKit/QMUIKit/QMUIComponents/CALayer+QMUIViewAnimation.h Pods/QMUIKit/QMUIKit/QMUIComponents/CALayer+QMUIViewAnimation.m Pods/QMUIKit/QMUIKit/QMUIComponents/ImagePickerLibrary/QMUIAlbumViewController.h Pods/QMUIKit/QMUIKit/QMUIComponents/ImagePickerLibrary/QMUIAlbumViewController.m Pods/QMUIKit/QMUIKit/QMUIComponents/ImagePickerLibrary/QMUIImagePickerCollectionViewCell.h Pods/QMUIKit/QMUIKit/QMUIComponents/ImagePickerLibrary/QMUIImagePickerCollectionViewCell.m Pods/QMUIKit/QMUIKit/QMUIComponents/ImagePickerLibrary/QMUIImagePickerHelper.h Pods/QMUIKit/QMUIKit/QMUIComponents/ImagePickerLibrary/QMUIImagePickerHelper.m Pods/QMUIKit/QMUIKit/QMUIComponents/ImagePickerLibrary/QMUIImagePickerPreviewViewController.h Pods/QMUIKit/QMUIKit/QMUIComponents/ImagePickerLibrary/QMUIImagePickerPreviewViewController.m Pods/QMUIKit/QMUIKit/QMUIComponents/ImagePickerLibrary/QMUIImagePickerViewController.h Pods/QMUIKit/QMUIKit/QMUIComponents/ImagePickerLibrary/QMUIImagePickerViewController.m Pods/QMUIKit/QMUIKit/QMUIComponents/NavigationBarTransition/UINavigationBar+Transition.h Pods/QMUIKit/QMUIKit/QMUIComponents/NavigationBarTransition/UINavigationBar+Transition.m Pods/QMUIKit/QMUIKit/QMUIComponents/NavigationBarTransition/UINavigationController+NavigationBarTransition.h Pods/QMUIKit/QMUIKit/QMUIComponents/NavigationBarTransition/UINavigationController+NavigationBarTransition.m Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIAlertController.h Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIAlertController.m Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIAnimation/QMUIAnimationHelper.h Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIAnimation/QMUIAnimationHelper.m Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIAnimation/QMUIDisplayLinkAnimation.h Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIAnimation/QMUIDisplayLinkAnimation.m Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIAnimation/QMUIEasings.h Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIAppearance.h Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIAppearance.m Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIBadge/QMUIBadgeProtocol.h Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIBadge/UIBarItem+QMUIBadge.h Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIBadge/UIBarItem+QMUIBadge.m Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIBadge/UIView+QMUIBadge.h Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIBadge/UIView+QMUIBadge.m Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIButton/QMUIButton.h Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIButton/QMUIButton.m Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIButton/QMUINavigationButton.h Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIButton/QMUINavigationButton.m Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIButton/QMUIToolbarButton.h Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIButton/QMUIToolbarButton.m Pods/QMUIKit/QMUIKit/QMUIComponents/QMUICellHeightCache.h Pods/QMUIKit/QMUIKit/QMUIComponents/QMUICellHeightCache.m Pods/QMUIKit/QMUIKit/QMUIComponents/QMUICellHeightKeyCache/QMUICellHeightKeyCache.h Pods/QMUIKit/QMUIKit/QMUIComponents/QMUICellHeightKeyCache/QMUICellHeightKeyCache.m Pods/QMUIKit/QMUIKit/QMUIComponents/QMUICellHeightKeyCache/UITableView+QMUICellHeightKeyCache.h Pods/QMUIKit/QMUIKit/QMUIComponents/QMUICellHeightKeyCache/UITableView+QMUICellHeightKeyCache.m Pods/QMUIKit/QMUIKit/QMUIComponents/QMUICellSizeKeyCache/QMUICellSizeKeyCache.h Pods/QMUIKit/QMUIKit/QMUIComponents/QMUICellSizeKeyCache/QMUICellSizeKeyCache.m Pods/QMUIKit/QMUIKit/QMUIComponents/QMUICellSizeKeyCache/UICollectionView+QMUICellSizeKeyCache.h Pods/QMUIKit/QMUIKit/QMUIComponents/QMUICellSizeKeyCache/UICollectionView+QMUICellSizeKeyCache.m Pods/QMUIKit/QMUIKit/QMUIComponents/QMUICollectionViewPagingLayout.h Pods/QMUIKit/QMUIKit/QMUIComponents/QMUICollectionViewPagingLayout.m Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIConsole/QMUIConsole.h Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIConsole/QMUIConsole.m Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIConsole/QMUIConsoleToolbar.h Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIConsole/QMUIConsoleToolbar.m Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIConsole/QMUIConsoleViewController.h Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIConsole/QMUIConsoleViewController.m Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIConsole/QMUILog+QMUIConsole.h Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIConsole/QMUILog+QMUIConsole.m Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIDialogViewController.h Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIDialogViewController.m Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIEmotionInputManager.h Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIEmotionInputManager.m Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIEmotionView.h Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIEmotionView.m Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIEmptyView.h Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIEmptyView.m Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIFloatLayoutView.h Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIFloatLayoutView.m Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIGridView.h Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIGridView.m Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIImagePreviewView/QMUIImagePreviewView.h Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIImagePreviewView/QMUIImagePreviewView.m Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIImagePreviewView/QMUIImagePreviewViewController.h Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIImagePreviewView/QMUIImagePreviewViewController.m Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIImagePreviewView/QMUIImagePreviewViewTransitionAnimator.h Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIImagePreviewView/QMUIImagePreviewViewTransitionAnimator.m Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIKeyboardManager.h Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIKeyboardManager.m Pods/QMUIKit/QMUIKit/QMUIComponents/QMUILabel.h Pods/QMUIKit/QMUIKit/QMUIComponents/QMUILabel.m Pods/QMUIKit/QMUIKit/QMUIComponents/QMUILog/QMUILog.h Pods/QMUIKit/QMUIKit/QMUIComponents/QMUILog/QMUILogItem.h Pods/QMUIKit/QMUIKit/QMUIComponents/QMUILog/QMUILogItem.m Pods/QMUIKit/QMUIKit/QMUIComponents/QMUILog/QMUILogNameManager.h Pods/QMUIKit/QMUIKit/QMUIComponents/QMUILog/QMUILogNameManager.m Pods/QMUIKit/QMUIKit/QMUIComponents/QMUILog/QMUILogger.h Pods/QMUIKit/QMUIKit/QMUIComponents/QMUILog/QMUILogger.m Pods/QMUIKit/QMUIKit/QMUIComponents/QMUILogManagerViewController.h Pods/QMUIKit/QMUIKit/QMUIComponents/QMUILogManagerViewController.m Pods/QMUIKit/QMUIKit/QMUIComponents/QMUILogger+QMUIConfigurationTemplate.h Pods/QMUIKit/QMUIKit/QMUIComponents/QMUILogger+QMUIConfigurationTemplate.m Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIMarqueeLabel.h Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIMarqueeLabel.m Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIModalPresentationViewController.h Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIModalPresentationViewController.m Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIMoreOperationController.h Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIMoreOperationController.m Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIMultipleDelegates/NSObject+QMUIMultipleDelegates.h Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIMultipleDelegates/NSObject+QMUIMultipleDelegates.m Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIMultipleDelegates/QMUIMultipleDelegates.h Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIMultipleDelegates/QMUIMultipleDelegates.m Pods/QMUIKit/QMUIKit/QMUIComponents/QMUINavigationTitleView.h Pods/QMUIKit/QMUIKit/QMUIComponents/QMUINavigationTitleView.m Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIOrderedDictionary.h Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIOrderedDictionary.m Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIPieProgressView.h Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIPieProgressView.m Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIPopupContainerView.h Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIPopupContainerView.m Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIPopupMenuView/QMUIPopupMenuBaseItem.h Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIPopupMenuView/QMUIPopupMenuBaseItem.m Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIPopupMenuView/QMUIPopupMenuButtonItem.h Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIPopupMenuView/QMUIPopupMenuButtonItem.m Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIPopupMenuView/QMUIPopupMenuItemProtocol.h Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIPopupMenuView/QMUIPopupMenuView.h Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIPopupMenuView/QMUIPopupMenuView.m Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIScrollAnimator/QMUINavigationBarScrollingAnimator.h Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIScrollAnimator/QMUINavigationBarScrollingAnimator.m Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIScrollAnimator/QMUINavigationBarScrollingSnapAnimator.h Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIScrollAnimator/QMUINavigationBarScrollingSnapAnimator.m Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIScrollAnimator/QMUIScrollAnimator.h Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIScrollAnimator/QMUIScrollAnimator.m Pods/QMUIKit/QMUIKit/QMUIComponents/QMUISearchBar.h Pods/QMUIKit/QMUIKit/QMUIComponents/QMUISearchBar.m Pods/QMUIKit/QMUIKit/QMUIComponents/QMUISearchController.h Pods/QMUIKit/QMUIKit/QMUIComponents/QMUISearchController.m Pods/QMUIKit/QMUIKit/QMUIComponents/QMUISegmentedControl.h Pods/QMUIKit/QMUIKit/QMUIComponents/QMUISegmentedControl.m Pods/QMUIKit/QMUIKit/QMUIComponents/QMUITableView.h Pods/QMUIKit/QMUIKit/QMUIComponents/QMUITableView.m Pods/QMUIKit/QMUIKit/QMUIComponents/QMUITableViewCell.h Pods/QMUIKit/QMUIKit/QMUIComponents/QMUITableViewCell.m Pods/QMUIKit/QMUIKit/QMUIComponents/QMUITableViewHeaderFooterView.h Pods/QMUIKit/QMUIKit/QMUIComponents/QMUITableViewHeaderFooterView.m Pods/QMUIKit/QMUIKit/QMUIComponents/QMUITableViewProtocols.h Pods/QMUIKit/QMUIKit/QMUIComponents/QMUITestView.h Pods/QMUIKit/QMUIKit/QMUIComponents/QMUITestView.m Pods/QMUIKit/QMUIKit/QMUIComponents/QMUITextField.h Pods/QMUIKit/QMUIKit/QMUIComponents/QMUITextField.m Pods/QMUIKit/QMUIKit/QMUIComponents/QMUITextView.h Pods/QMUIKit/QMUIKit/QMUIComponents/QMUITextView.m Pods/QMUIKit/QMUIKit/QMUIComponents/QMUITheme/QMUITheme.h Pods/QMUIKit/QMUIKit/QMUIComponents/QMUITheme/QMUIThemeManager.h Pods/QMUIKit/QMUIKit/QMUIComponents/QMUITheme/QMUIThemeManager.m Pods/QMUIKit/QMUIKit/QMUIComponents/QMUITheme/QMUIThemeManagerCenter.h Pods/QMUIKit/QMUIKit/QMUIComponents/QMUITheme/QMUIThemeManagerCenter.m Pods/QMUIKit/QMUIKit/QMUIComponents/QMUITheme/QMUIThemePrivate.h Pods/QMUIKit/QMUIKit/QMUIComponents/QMUITheme/QMUIThemePrivate.m Pods/QMUIKit/QMUIKit/QMUIComponents/QMUITheme/UIColor+QMUITheme.h Pods/QMUIKit/QMUIKit/QMUIComponents/QMUITheme/UIColor+QMUITheme.m Pods/QMUIKit/QMUIKit/QMUIComponents/QMUITheme/UIImage+QMUITheme.h Pods/QMUIKit/QMUIKit/QMUIComponents/QMUITheme/UIImage+QMUITheme.m Pods/QMUIKit/QMUIKit/QMUIComponents/QMUITheme/UIView+QMUITheme.h Pods/QMUIKit/QMUIKit/QMUIComponents/QMUITheme/UIView+QMUITheme.m Pods/QMUIKit/QMUIKit/QMUIComponents/QMUITheme/UIViewController+QMUITheme.h Pods/QMUIKit/QMUIKit/QMUIComponents/QMUITheme/UIViewController+QMUITheme.m Pods/QMUIKit/QMUIKit/QMUIComponents/QMUITheme/UIVisualEffect+QMUITheme.h Pods/QMUIKit/QMUIKit/QMUIComponents/QMUITheme/UIVisualEffect+QMUITheme.m Pods/QMUIKit/QMUIKit/QMUIComponents/QMUITips.h Pods/QMUIKit/QMUIKit/QMUIComponents/QMUITips.m Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIWeakObjectContainer.h Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIWeakObjectContainer.m Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIWindowSizeMonitor.h Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIWindowSizeMonitor.m Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIZoomImageView.h Pods/QMUIKit/QMUIKit/QMUIComponents/QMUIZoomImageView.m Pods/QMUIKit/QMUIKit/QMUIComponents/StaticTableView/QMUIStaticTableViewCellData.h Pods/QMUIKit/QMUIKit/QMUIComponents/StaticTableView/QMUIStaticTableViewCellData.m Pods/QMUIKit/QMUIKit/QMUIComponents/StaticTableView/QMUIStaticTableViewCellDataSource.h Pods/QMUIKit/QMUIKit/QMUIComponents/StaticTableView/QMUIStaticTableViewCellDataSource.m Pods/QMUIKit/QMUIKit/QMUIComponents/StaticTableView/UITableView+QMUIStaticCell.h Pods/QMUIKit/QMUIKit/QMUIComponents/StaticTableView/UITableView+QMUIStaticCell.m Pods/QMUIKit/QMUIKit/QMUIComponents/ToastView/QMUIToastAnimator.h Pods/QMUIKit/QMUIKit/QMUIComponents/ToastView/QMUIToastAnimator.m Pods/QMUIKit/QMUIKit/QMUIComponents/ToastView/QMUIToastBackgroundView.h Pods/QMUIKit/QMUIKit/QMUIComponents/ToastView/QMUIToastBackgroundView.m Pods/QMUIKit/QMUIKit/QMUIComponents/ToastView/QMUIToastContentView.h Pods/QMUIKit/QMUIKit/QMUIComponents/ToastView/QMUIToastContentView.m Pods/QMUIKit/QMUIKit/QMUIComponents/ToastView/QMUIToastView.h Pods/QMUIKit/QMUIKit/QMUIComponents/ToastView/QMUIToastView.m Pods/QMUIKit/QMUIKit/QMUICore/QMUICommonDefines.h Pods/QMUIKit/QMUIKit/QMUICore/QMUIConfiguration.h Pods/QMUIKit/QMUIKit/QMUICore/QMUIConfiguration.m Pods/QMUIKit/QMUIKit/QMUICore/QMUIConfigurationMacros.h Pods/QMUIKit/QMUIKit/QMUICore/QMUICore.h Pods/QMUIKit/QMUIKit/QMUICore/QMUIHelper.h Pods/QMUIKit/QMUIKit/QMUICore/QMUIHelper.m Pods/QMUIKit/QMUIKit/QMUICore/QMUILab.h Pods/QMUIKit/QMUIKit/QMUICore/QMUIRuntime.h Pods/QMUIKit/QMUIKit/QMUICore/QMUIRuntime.m Pods/QMUIKit/QMUIKit/QMUIKit.h Pods/QMUIKit/QMUIKit/QMUIMainFrame/QMUICommonTableViewController.h Pods/QMUIKit/QMUIKit/QMUIMainFrame/QMUICommonTableViewController.m Pods/QMUIKit/QMUIKit/QMUIMainFrame/QMUICommonViewController.h Pods/QMUIKit/QMUIKit/QMUIMainFrame/QMUICommonViewController.m Pods/QMUIKit/QMUIKit/QMUIMainFrame/QMUINavigationController.h Pods/QMUIKit/QMUIKit/QMUIMainFrame/QMUINavigationController.m Pods/QMUIKit/QMUIKit/QMUIMainFrame/QMUITabBarViewController.h Pods/QMUIKit/QMUIKit/QMUIMainFrame/QMUITabBarViewController.m Pods/QMUIKit/QMUIKit/QMUIResources/Images.xcassets/Contents.json Pods/QMUIKit/QMUIKit/QMUIResources/Images.xcassets/QMUI_console_clear.imageset/Contents.json Pods/QMUIKit/QMUIKit/QMUIResources/Images.xcassets/QMUI_console_clear.imageset/QMUI_console_clear.pdf Pods/QMUIKit/QMUIKit/QMUIResources/Images.xcassets/QMUI_console_filter.imageset/Contents.json Pods/QMUIKit/QMUIKit/QMUIResources/Images.xcassets/QMUI_console_filter.imageset/QMUI_console_filter.pdf Pods/QMUIKit/QMUIKit/QMUIResources/Images.xcassets/QMUI_console_filter_selected.imageset/Contents.json Pods/QMUIKit/QMUIKit/QMUIResources/Images.xcassets/QMUI_console_filter_selected.imageset/QMUI_console_filter_selected.pdf Pods/QMUIKit/QMUIKit/QMUIResources/Images.xcassets/QMUI_console_logo.imageset/Contents.json Pods/QMUIKit/QMUIKit/QMUIResources/Images.xcassets/QMUI_console_logo.imageset/QMUI_console_logo.pdf Pods/QMUIKit/QMUIKit/QMUIResources/Images.xcassets/QMUI_emotion_delete.imageset/Contents.json Pods/QMUIKit/QMUIKit/QMUIResources/Images.xcassets/QMUI_emotion_delete.imageset/QMUI_emotion_delete.pdf Pods/QMUIKit/QMUIKit/QMUIResources/Images.xcassets/QMUI_hiddenAlbum.imageset/Contents.json Pods/QMUIKit/QMUIKit/QMUIResources/Images.xcassets/QMUI_hiddenAlbum.imageset/QMUI_hiddenAlbum.pdf Pods/QMUIKit/QMUIKit/QMUIResources/Images.xcassets/QMUI_icloud_download_fault.imageset/Contents.json Pods/QMUIKit/QMUIKit/QMUIResources/Images.xcassets/QMUI_icloud_download_fault.imageset/QMUI_icloud_download_fault.pdf Pods/QMUIKit/QMUIKit/QMUIResources/Images.xcassets/QMUI_pickerImage_checkbox.imageset/Contents.json Pods/QMUIKit/QMUIKit/QMUIResources/Images.xcassets/QMUI_pickerImage_checkbox.imageset/QMUI_pickerImage_checkbox.pdf Pods/QMUIKit/QMUIKit/QMUIResources/Images.xcassets/QMUI_pickerImage_checkbox_checked.imageset/Contents.json Pods/QMUIKit/QMUIKit/QMUIResources/Images.xcassets/QMUI_pickerImage_checkbox_checked.imageset/QMUI_pickerImage_checkbox_checked.pdf Pods/QMUIKit/QMUIKit/QMUIResources/Images.xcassets/QMUI_pickerImage_favorite.imageset/Contents.json Pods/QMUIKit/QMUIKit/QMUIResources/Images.xcassets/QMUI_pickerImage_favorite.imageset/QMUI_pickerImage_favorite.pdf Pods/QMUIKit/QMUIKit/QMUIResources/Images.xcassets/QMUI_pickerImage_video_mark.imageset/Contents.json Pods/QMUIKit/QMUIKit/QMUIResources/Images.xcassets/QMUI_pickerImage_video_mark.imageset/QMUI_pickerImage_video_mark.pdf Pods/QMUIKit/QMUIKit/QMUIResources/Images.xcassets/QMUI_previewImage_checkbox.imageset/Contents.json Pods/QMUIKit/QMUIKit/QMUIResources/Images.xcassets/QMUI_previewImage_checkbox.imageset/QMUI_previewImage_checkbox.pdf Pods/QMUIKit/QMUIKit/QMUIResources/Images.xcassets/QMUI_previewImage_checkbox_checked.imageset/Contents.json Pods/QMUIKit/QMUIKit/QMUIResources/Images.xcassets/QMUI_previewImage_checkbox_checked.imageset/QMUI_previewImage_checkbox_checked.pdf Pods/QMUIKit/QMUIKit/QMUIResources/Images.xcassets/QMUI_tips_done.imageset/Contents.json Pods/QMUIKit/QMUIKit/QMUIResources/Images.xcassets/QMUI_tips_done.imageset/QMUI_tips_done.pdf Pods/QMUIKit/QMUIKit/QMUIResources/Images.xcassets/QMUI_tips_error.imageset/Contents.json Pods/QMUIKit/QMUIKit/QMUIResources/Images.xcassets/QMUI_tips_error.imageset/QMUI_tips_error.pdf Pods/QMUIKit/QMUIKit/QMUIResources/Images.xcassets/QMUI_tips_info.imageset/Contents.json Pods/QMUIKit/QMUIKit/QMUIResources/Images.xcassets/QMUI_tips_info.imageset/QMUI_tips_info.pdf Pods/QMUIKit/QMUIKit/UIKitExtensions/CALayer+QMUI.h Pods/QMUIKit/QMUIKit/UIKitExtensions/CALayer+QMUI.m Pods/QMUIKit/QMUIKit/UIKitExtensions/NSArray+QMUI.h Pods/QMUIKit/QMUIKit/UIKitExtensions/NSArray+QMUI.m Pods/QMUIKit/QMUIKit/UIKitExtensions/NSAttributedString+QMUI.h Pods/QMUIKit/QMUIKit/UIKitExtensions/NSAttributedString+QMUI.m Pods/QMUIKit/QMUIKit/UIKitExtensions/NSCharacterSet+QMUI.h Pods/QMUIKit/QMUIKit/UIKitExtensions/NSCharacterSet+QMUI.m Pods/QMUIKit/QMUIKit/UIKitExtensions/NSDictionary+QMUI.h Pods/QMUIKit/QMUIKit/UIKitExtensions/NSDictionary+QMUI.m Pods/QMUIKit/QMUIKit/UIKitExtensions/NSMethodSignature+QMUI.h Pods/QMUIKit/QMUIKit/UIKitExtensions/NSMethodSignature+QMUI.m Pods/QMUIKit/QMUIKit/UIKitExtensions/NSNumber+QMUI.h Pods/QMUIKit/QMUIKit/UIKitExtensions/NSNumber+QMUI.m Pods/QMUIKit/QMUIKit/UIKitExtensions/NSObject+QMUI.h Pods/QMUIKit/QMUIKit/UIKitExtensions/NSObject+QMUI.m Pods/QMUIKit/QMUIKit/UIKitExtensions/NSParagraphStyle+QMUI.h Pods/QMUIKit/QMUIKit/UIKitExtensions/NSParagraphStyle+QMUI.m Pods/QMUIKit/QMUIKit/UIKitExtensions/NSPointerArray+QMUI.h Pods/QMUIKit/QMUIKit/UIKitExtensions/NSPointerArray+QMUI.m Pods/QMUIKit/QMUIKit/UIKitExtensions/NSShadow+QMUI.h Pods/QMUIKit/QMUIKit/UIKitExtensions/NSShadow+QMUI.m Pods/QMUIKit/QMUIKit/UIKitExtensions/NSString+QMUI.h Pods/QMUIKit/QMUIKit/UIKitExtensions/NSString+QMUI.m Pods/QMUIKit/QMUIKit/UIKitExtensions/NSURL+QMUI.h Pods/QMUIKit/QMUIKit/UIKitExtensions/NSURL+QMUI.m Pods/QMUIKit/QMUIKit/UIKitExtensions/QMUIBarProtocol/QMUIBarProtocol.h Pods/QMUIKit/QMUIKit/UIKitExtensions/QMUIBarProtocol/QMUIBarProtocolPrivate.h Pods/QMUIKit/QMUIKit/UIKitExtensions/QMUIBarProtocol/QMUIBarProtocolPrivate.m Pods/QMUIKit/QMUIKit/UIKitExtensions/QMUIBarProtocol/UINavigationBar+QMUIBarProtocol.h Pods/QMUIKit/QMUIKit/UIKitExtensions/QMUIBarProtocol/UINavigationBar+QMUIBarProtocol.m Pods/QMUIKit/QMUIKit/UIKitExtensions/QMUIBarProtocol/UITabBar+QMUIBarProtocol.h Pods/QMUIKit/QMUIKit/UIKitExtensions/QMUIBarProtocol/UITabBar+QMUIBarProtocol.m Pods/QMUIKit/QMUIKit/UIKitExtensions/QMUIStringPrivate.h Pods/QMUIKit/QMUIKit/UIKitExtensions/QMUIStringPrivate.m Pods/QMUIKit/QMUIKit/UIKitExtensions/UIActivityIndicatorView+QMUI.h Pods/QMUIKit/QMUIKit/UIKitExtensions/UIActivityIndicatorView+QMUI.m Pods/QMUIKit/QMUIKit/UIKitExtensions/UIApplication+QMUI.h Pods/QMUIKit/QMUIKit/UIKitExtensions/UIApplication+QMUI.m Pods/QMUIKit/QMUIKit/UIKitExtensions/UIBarItem+QMUI.h Pods/QMUIKit/QMUIKit/UIKitExtensions/UIBarItem+QMUI.m Pods/QMUIKit/QMUIKit/UIKitExtensions/UIBezierPath+QMUI.h Pods/QMUIKit/QMUIKit/UIKitExtensions/UIBezierPath+QMUI.m Pods/QMUIKit/QMUIKit/UIKitExtensions/UIBlurEffect+QMUI.h Pods/QMUIKit/QMUIKit/UIKitExtensions/UIBlurEffect+QMUI.m Pods/QMUIKit/QMUIKit/UIKitExtensions/UIButton+QMUI.h Pods/QMUIKit/QMUIKit/UIKitExtensions/UIButton+QMUI.m Pods/QMUIKit/QMUIKit/UIKitExtensions/UICollectionView+QMUI.h Pods/QMUIKit/QMUIKit/UIKitExtensions/UICollectionView+QMUI.m Pods/QMUIKit/QMUIKit/UIKitExtensions/UICollectionViewCell+QMUI.h Pods/QMUIKit/QMUIKit/UIKitExtensions/UICollectionViewCell+QMUI.m Pods/QMUIKit/QMUIKit/UIKitExtensions/UIColor+QMUI.h Pods/QMUIKit/QMUIKit/UIKitExtensions/UIColor+QMUI.m Pods/QMUIKit/QMUIKit/UIKitExtensions/UIControl+QMUI.h Pods/QMUIKit/QMUIKit/UIKitExtensions/UIControl+QMUI.m Pods/QMUIKit/QMUIKit/UIKitExtensions/UIFont+QMUI.h Pods/QMUIKit/QMUIKit/UIKitExtensions/UIFont+QMUI.m Pods/QMUIKit/QMUIKit/UIKitExtensions/UIGestureRecognizer+QMUI.h Pods/QMUIKit/QMUIKit/UIKitExtensions/UIGestureRecognizer+QMUI.m Pods/QMUIKit/QMUIKit/UIKitExtensions/UIImage+QMUI.h Pods/QMUIKit/QMUIKit/UIKitExtensions/UIImage+QMUI.m Pods/QMUIKit/QMUIKit/UIKitExtensions/UIImageView+QMUI.h Pods/QMUIKit/QMUIKit/UIKitExtensions/UIImageView+QMUI.m Pods/QMUIKit/QMUIKit/UIKitExtensions/UIInterface+QMUI.h Pods/QMUIKit/QMUIKit/UIKitExtensions/UIInterface+QMUI.m Pods/QMUIKit/QMUIKit/UIKitExtensions/UILabel+QMUI.h Pods/QMUIKit/QMUIKit/UIKitExtensions/UILabel+QMUI.m Pods/QMUIKit/QMUIKit/UIKitExtensions/UIMenuController+QMUI.h Pods/QMUIKit/QMUIKit/UIKitExtensions/UIMenuController+QMUI.m Pods/QMUIKit/QMUIKit/UIKitExtensions/UINavigationBar+QMUI.h Pods/QMUIKit/QMUIKit/UIKitExtensions/UINavigationBar+QMUI.m Pods/QMUIKit/QMUIKit/UIKitExtensions/UINavigationController+QMUI.h Pods/QMUIKit/QMUIKit/UIKitExtensions/UINavigationController+QMUI.m Pods/QMUIKit/QMUIKit/UIKitExtensions/UINavigationItem+QMUI.h Pods/QMUIKit/QMUIKit/UIKitExtensions/UINavigationItem+QMUI.m Pods/QMUIKit/QMUIKit/UIKitExtensions/UIScrollView+QMUI.h Pods/QMUIKit/QMUIKit/UIKitExtensions/UIScrollView+QMUI.m Pods/QMUIKit/QMUIKit/UIKitExtensions/UISearchBar+QMUI.h Pods/QMUIKit/QMUIKit/UIKitExtensions/UISearchBar+QMUI.m Pods/QMUIKit/QMUIKit/UIKitExtensions/UISearchController+QMUI.h Pods/QMUIKit/QMUIKit/UIKitExtensions/UISearchController+QMUI.m Pods/QMUIKit/QMUIKit/UIKitExtensions/UISlider+QMUI.h Pods/QMUIKit/QMUIKit/UIKitExtensions/UISlider+QMUI.m Pods/QMUIKit/QMUIKit/UIKitExtensions/UISwitch+QMUI.h Pods/QMUIKit/QMUIKit/UIKitExtensions/UISwitch+QMUI.m Pods/QMUIKit/QMUIKit/UIKitExtensions/UITabBar+QMUI.h Pods/QMUIKit/QMUIKit/UIKitExtensions/UITabBar+QMUI.m Pods/QMUIKit/QMUIKit/UIKitExtensions/UITabBarItem+QMUI.h Pods/QMUIKit/QMUIKit/UIKitExtensions/UITabBarItem+QMUI.m Pods/QMUIKit/QMUIKit/UIKitExtensions/UITableView+QMUI.h Pods/QMUIKit/QMUIKit/UIKitExtensions/UITableView+QMUI.m Pods/QMUIKit/QMUIKit/UIKitExtensions/UITableViewCell+QMUI.h Pods/QMUIKit/QMUIKit/UIKitExtensions/UITableViewCell+QMUI.m Pods/QMUIKit/QMUIKit/UIKitExtensions/UITableViewHeaderFooterView+QMUI.h Pods/QMUIKit/QMUIKit/UIKitExtensions/UITableViewHeaderFooterView+QMUI.m Pods/QMUIKit/QMUIKit/UIKitExtensions/UITextField+QMUI.h Pods/QMUIKit/QMUIKit/UIKitExtensions/UITextField+QMUI.m Pods/QMUIKit/QMUIKit/UIKitExtensions/UITextInputTraits+QMUI.h Pods/QMUIKit/QMUIKit/UIKitExtensions/UITextInputTraits+QMUI.m Pods/QMUIKit/QMUIKit/UIKitExtensions/UITextView+QMUI.h Pods/QMUIKit/QMUIKit/UIKitExtensions/UITextView+QMUI.m Pods/QMUIKit/QMUIKit/UIKitExtensions/UIToolbar+QMUI.h Pods/QMUIKit/QMUIKit/UIKitExtensions/UIToolbar+QMUI.m Pods/QMUIKit/QMUIKit/UIKitExtensions/UITraitCollection+QMUI.h Pods/QMUIKit/QMUIKit/UIKitExtensions/UITraitCollection+QMUI.m Pods/QMUIKit/QMUIKit/UIKitExtensions/UIView+QMUI.h Pods/QMUIKit/QMUIKit/UIKitExtensions/UIView+QMUI.m Pods/QMUIKit/QMUIKit/UIKitExtensions/UIView+QMUIBorder.h Pods/QMUIKit/QMUIKit/UIKitExtensions/UIView+QMUIBorder.m Pods/QMUIKit/QMUIKit/UIKitExtensions/UIViewController+QMUI.h Pods/QMUIKit/QMUIKit/UIKitExtensions/UIViewController+QMUI.m Pods/QMUIKit/QMUIKit/UIKitExtensions/UIVisualEffectView+QMUI.h Pods/QMUIKit/QMUIKit/UIKitExtensions/UIVisualEffectView+QMUI.m Pods/QMUIKit/QMUIKit/UIKitExtensions/UIWindow+QMUI.h Pods/QMUIKit/QMUIKit/UIKitExtensions/UIWindow+QMUI.m Pods/QMUIKit/README.md Pods/RxCocoa/LICENSE.md Pods/RxCocoa/Platform/DataStructures/Bag.swift Pods/RxCocoa/Platform/DataStructures/InfiniteSequence.swift Pods/RxCocoa/Platform/DataStructures/PriorityQueue.swift Pods/RxCocoa/Platform/DataStructures/Queue.swift Pods/RxCocoa/Platform/DispatchQueue+Extensions.swift Pods/RxCocoa/Platform/Platform.Darwin.swift Pods/RxCocoa/Platform/Platform.Linux.swift Pods/RxCocoa/Platform/RecursiveLock.swift Pods/RxCocoa/README.md Pods/RxCocoa/RxCocoa/Common/ControlTarget.swift Pods/RxCocoa/RxCocoa/Common/DelegateProxy.swift Pods/RxCocoa/RxCocoa/Common/DelegateProxyType.swift Pods/RxCocoa/RxCocoa/Common/Infallible+Bind.swift Pods/RxCocoa/RxCocoa/Common/Observable+Bind.swift Pods/RxCocoa/RxCocoa/Common/RxCocoaObjCRuntimeError+Extensions.swift Pods/RxCocoa/RxCocoa/Common/RxTarget.swift Pods/RxCocoa/RxCocoa/Common/SectionedViewDataSourceType.swift Pods/RxCocoa/RxCocoa/Common/TextInput.swift Pods/RxCocoa/RxCocoa/Foundation/KVORepresentable+CoreGraphics.swift Pods/RxCocoa/RxCocoa/Foundation/KVORepresentable+Swift.swift Pods/RxCocoa/RxCocoa/Foundation/KVORepresentable.swift Pods/RxCocoa/RxCocoa/Foundation/NSObject+Rx+KVORepresentable.swift Pods/RxCocoa/RxCocoa/Foundation/NSObject+Rx+RawRepresentable.swift Pods/RxCocoa/RxCocoa/Foundation/NSObject+Rx.swift Pods/RxCocoa/RxCocoa/Foundation/NotificationCenter+Rx.swift Pods/RxCocoa/RxCocoa/Foundation/URLSession+Rx.swift Pods/RxCocoa/RxCocoa/Runtime/_RX.m Pods/RxCocoa/RxCocoa/Runtime/_RXDelegateProxy.m Pods/RxCocoa/RxCocoa/Runtime/_RXKVOObserver.m Pods/RxCocoa/RxCocoa/Runtime/_RXObjCRuntime.m Pods/RxCocoa/RxCocoa/Runtime/include/RxCocoaRuntime.h Pods/RxCocoa/RxCocoa/Runtime/include/_RX.h Pods/RxCocoa/RxCocoa/Runtime/include/_RXDelegateProxy.h Pods/RxCocoa/RxCocoa/Runtime/include/_RXKVOObserver.h Pods/RxCocoa/RxCocoa/Runtime/include/_RXObjCRuntime.h Pods/RxCocoa/RxCocoa/RxCocoa.h Pods/RxCocoa/RxCocoa/RxCocoa.swift Pods/RxCocoa/RxCocoa/Traits/ControlEvent.swift Pods/RxCocoa/RxCocoa/Traits/ControlProperty.swift Pods/RxCocoa/RxCocoa/Traits/Driver/BehaviorRelay+Driver.swift Pods/RxCocoa/RxCocoa/Traits/Driver/ControlEvent+Driver.swift Pods/RxCocoa/RxCocoa/Traits/Driver/ControlProperty+Driver.swift Pods/RxCocoa/RxCocoa/Traits/Driver/Driver+Subscription.swift Pods/RxCocoa/RxCocoa/Traits/Driver/Driver.swift Pods/RxCocoa/RxCocoa/Traits/Driver/Infallible+Driver.swift Pods/RxCocoa/RxCocoa/Traits/Driver/ObservableConvertibleType+Driver.swift Pods/RxCocoa/RxCocoa/Traits/SharedSequence/ObservableConvertibleType+SharedSequence.swift Pods/RxCocoa/RxCocoa/Traits/SharedSequence/SchedulerType+SharedSequence.swift Pods/RxCocoa/RxCocoa/Traits/SharedSequence/SharedSequence+Concurrency.swift Pods/RxCocoa/RxCocoa/Traits/SharedSequence/SharedSequence+Operators+arity.swift Pods/RxCocoa/RxCocoa/Traits/SharedSequence/SharedSequence+Operators.swift Pods/RxCocoa/RxCocoa/Traits/SharedSequence/SharedSequence.swift Pods/RxCocoa/RxCocoa/Traits/Signal/ControlEvent+Signal.swift Pods/RxCocoa/RxCocoa/Traits/Signal/ObservableConvertibleType+Signal.swift Pods/RxCocoa/RxCocoa/Traits/Signal/PublishRelay+Signal.swift Pods/RxCocoa/RxCocoa/Traits/Signal/Signal+Subscription.swift Pods/RxCocoa/RxCocoa/Traits/Signal/Signal.swift Pods/RxCocoa/RxCocoa/iOS/DataSources/RxCollectionViewReactiveArrayDataSource.swift Pods/RxCocoa/RxCocoa/iOS/DataSources/RxPickerViewAdapter.swift Pods/RxCocoa/RxCocoa/iOS/DataSources/RxTableViewReactiveArrayDataSource.swift Pods/RxCocoa/RxCocoa/iOS/Events/ItemEvents.swift Pods/RxCocoa/RxCocoa/iOS/NSTextStorage+Rx.swift Pods/RxCocoa/RxCocoa/iOS/Protocols/RxCollectionViewDataSourceType.swift Pods/RxCocoa/RxCocoa/iOS/Protocols/RxPickerViewDataSourceType.swift Pods/RxCocoa/RxCocoa/iOS/Protocols/RxTableViewDataSourceType.swift Pods/RxCocoa/RxCocoa/iOS/Proxies/RxCollectionViewDataSourcePrefetchingProxy.swift Pods/RxCocoa/RxCocoa/iOS/Proxies/RxCollectionViewDataSourceProxy.swift Pods/RxCocoa/RxCocoa/iOS/Proxies/RxCollectionViewDelegateProxy.swift Pods/RxCocoa/RxCocoa/iOS/Proxies/RxDelegateProxyCrashFix.swift Pods/RxCocoa/RxCocoa/iOS/Proxies/RxNavigationControllerDelegateProxy.swift Pods/RxCocoa/RxCocoa/iOS/Proxies/RxPickerViewDataSourceProxy.swift Pods/RxCocoa/RxCocoa/iOS/Proxies/RxPickerViewDelegateProxy.swift Pods/RxCocoa/RxCocoa/iOS/Proxies/RxScrollViewDelegateProxy.swift Pods/RxCocoa/RxCocoa/iOS/Proxies/RxSearchBarDelegateProxy.swift Pods/RxCocoa/RxCocoa/iOS/Proxies/RxSearchControllerDelegateProxy.swift Pods/RxCocoa/RxCocoa/iOS/Proxies/RxTabBarControllerDelegateProxy.swift Pods/RxCocoa/RxCocoa/iOS/Proxies/RxTabBarDelegateProxy.swift Pods/RxCocoa/RxCocoa/iOS/Proxies/RxTableViewDataSourcePrefetchingProxy.swift Pods/RxCocoa/RxCocoa/iOS/Proxies/RxTableViewDataSourceProxy.swift Pods/RxCocoa/RxCocoa/iOS/Proxies/RxTableViewDelegateProxy.swift Pods/RxCocoa/RxCocoa/iOS/Proxies/RxTextStorageDelegateProxy.swift Pods/RxCocoa/RxCocoa/iOS/Proxies/RxTextViewDelegateProxy.swift Pods/RxCocoa/RxCocoa/iOS/Proxies/RxWKNavigationDelegateProxy.swift Pods/RxCocoa/RxCocoa/iOS/UIActivityIndicatorView+Rx.swift Pods/RxCocoa/RxCocoa/iOS/UIApplication+Rx.swift Pods/RxCocoa/RxCocoa/iOS/UIBarButtonItem+Rx.swift Pods/RxCocoa/RxCocoa/iOS/UIButton+Rx.swift Pods/RxCocoa/RxCocoa/iOS/UICollectionView+Rx.swift Pods/RxCocoa/RxCocoa/iOS/UIControl+Rx.swift Pods/RxCocoa/RxCocoa/iOS/UIDatePicker+Rx.swift Pods/RxCocoa/RxCocoa/iOS/UIGestureRecognizer+Rx.swift Pods/RxCocoa/RxCocoa/iOS/UINavigationController+Rx.swift Pods/RxCocoa/RxCocoa/iOS/UIPickerView+Rx.swift Pods/RxCocoa/RxCocoa/iOS/UIRefreshControl+Rx.swift Pods/RxCocoa/RxCocoa/iOS/UIScrollView+Rx.swift Pods/RxCocoa/RxCocoa/iOS/UISearchBar+Rx.swift Pods/RxCocoa/RxCocoa/iOS/UISearchController+Rx.swift Pods/RxCocoa/RxCocoa/iOS/UISegmentedControl+Rx.swift Pods/RxCocoa/RxCocoa/iOS/UISlider+Rx.swift Pods/RxCocoa/RxCocoa/iOS/UIStepper+Rx.swift Pods/RxCocoa/RxCocoa/iOS/UISwitch+Rx.swift Pods/RxCocoa/RxCocoa/iOS/UITabBar+Rx.swift Pods/RxCocoa/RxCocoa/iOS/UITabBarController+Rx.swift Pods/RxCocoa/RxCocoa/iOS/UITableView+Rx.swift Pods/RxCocoa/RxCocoa/iOS/UITextField+Rx.swift Pods/RxCocoa/RxCocoa/iOS/UITextView+Rx.swift Pods/RxCocoa/RxCocoa/iOS/WKWebView+Rx.swift Pods/RxCocoa/RxCocoa/macOS/NSButton+Rx.swift Pods/RxCocoa/RxCocoa/macOS/NSControl+Rx.swift Pods/RxCocoa/RxCocoa/macOS/NSSlider+Rx.swift Pods/RxCocoa/RxCocoa/macOS/NSTextField+Rx.swift Pods/RxCocoa/RxCocoa/macOS/NSTextView+Rx.swift Pods/RxCocoa/RxCocoa/macOS/NSView+Rx.swift Pods/RxDataSources/LICENSE.md Pods/RxDataSources/README.md Pods/RxDataSources/Sources/RxDataSources/AnimationConfiguration.swift Pods/RxDataSources/Sources/RxDataSources/Array+Extensions.swift Pods/RxDataSources/Sources/RxDataSources/CollectionViewSectionedDataSource.swift Pods/RxDataSources/Sources/RxDataSources/DataSources.swift Pods/RxDataSources/Sources/RxDataSources/Deprecated.swift Pods/RxDataSources/Sources/RxDataSources/FloatingPointType+IdentifiableType.swift Pods/RxDataSources/Sources/RxDataSources/IntegerType+IdentifiableType.swift Pods/RxDataSources/Sources/RxDataSources/RxCollectionViewSectionedAnimatedDataSource.swift Pods/RxDataSources/Sources/RxDataSources/RxCollectionViewSectionedReloadDataSource.swift Pods/RxDataSources/Sources/RxDataSources/RxPickerViewAdapter.swift Pods/RxDataSources/Sources/RxDataSources/RxTableViewSectionedAnimatedDataSource.swift Pods/RxDataSources/Sources/RxDataSources/RxTableViewSectionedReloadDataSource.swift Pods/RxDataSources/Sources/RxDataSources/String+IdentifiableType.swift Pods/RxDataSources/Sources/RxDataSources/TableViewSectionedDataSource.swift Pods/RxDataSources/Sources/RxDataSources/UI+SectionedViewType.swift Pods/RxDataSources/Sources/RxDataSources/ViewTransition.swift Pods/RxRelay/LICENSE.md Pods/RxRelay/README.md Pods/RxRelay/RxRelay/BehaviorRelay.swift Pods/RxRelay/RxRelay/Observable+Bind.swift Pods/RxRelay/RxRelay/PublishRelay.swift Pods/RxRelay/RxRelay/ReplayRelay.swift Pods/RxRelay/RxRelay/Utils.swift Pods/RxSwift/LICENSE.md Pods/RxSwift/Platform/AtomicInt.swift Pods/RxSwift/Platform/DataStructures/Bag.swift Pods/RxSwift/Platform/DataStructures/InfiniteSequence.swift Pods/RxSwift/Platform/DataStructures/PriorityQueue.swift Pods/RxSwift/Platform/DataStructures/Queue.swift Pods/RxSwift/Platform/DispatchQueue+Extensions.swift Pods/RxSwift/Platform/Platform.Darwin.swift Pods/RxSwift/Platform/Platform.Linux.swift Pods/RxSwift/Platform/RecursiveLock.swift Pods/RxSwift/README.md Pods/RxSwift/RxSwift/AnyObserver.swift Pods/RxSwift/RxSwift/Binder.swift Pods/RxSwift/RxSwift/Cancelable.swift Pods/RxSwift/RxSwift/Concurrency/AsyncLock.swift Pods/RxSwift/RxSwift/Concurrency/Lock.swift Pods/RxSwift/RxSwift/Concurrency/LockOwnerType.swift Pods/RxSwift/RxSwift/Concurrency/SynchronizedDisposeType.swift Pods/RxSwift/RxSwift/Concurrency/SynchronizedOnType.swift Pods/RxSwift/RxSwift/Concurrency/SynchronizedUnsubscribeType.swift Pods/RxSwift/RxSwift/ConnectableObservableType.swift Pods/RxSwift/RxSwift/Date+Dispatch.swift Pods/RxSwift/RxSwift/Disposable.swift Pods/RxSwift/RxSwift/Disposables/AnonymousDisposable.swift Pods/RxSwift/RxSwift/Disposables/BinaryDisposable.swift Pods/RxSwift/RxSwift/Disposables/BooleanDisposable.swift Pods/RxSwift/RxSwift/Disposables/CompositeDisposable.swift Pods/RxSwift/RxSwift/Disposables/Disposables.swift Pods/RxSwift/RxSwift/Disposables/DisposeBag.swift Pods/RxSwift/RxSwift/Disposables/DisposeBase.swift Pods/RxSwift/RxSwift/Disposables/NopDisposable.swift Pods/RxSwift/RxSwift/Disposables/RefCountDisposable.swift Pods/RxSwift/RxSwift/Disposables/ScheduledDisposable.swift Pods/RxSwift/RxSwift/Disposables/SerialDisposable.swift Pods/RxSwift/RxSwift/Disposables/SingleAssignmentDisposable.swift Pods/RxSwift/RxSwift/Disposables/SubscriptionDisposable.swift Pods/RxSwift/RxSwift/Errors.swift Pods/RxSwift/RxSwift/Event.swift Pods/RxSwift/RxSwift/Extensions/Bag+Rx.swift Pods/RxSwift/RxSwift/GroupedObservable.swift Pods/RxSwift/RxSwift/ImmediateSchedulerType.swift Pods/RxSwift/RxSwift/Observable+Concurrency.swift Pods/RxSwift/RxSwift/Observable.swift Pods/RxSwift/RxSwift/ObservableConvertibleType.swift Pods/RxSwift/RxSwift/ObservableType+Extensions.swift Pods/RxSwift/RxSwift/ObservableType.swift Pods/RxSwift/RxSwift/Observables/AddRef.swift Pods/RxSwift/RxSwift/Observables/Amb.swift Pods/RxSwift/RxSwift/Observables/AsMaybe.swift Pods/RxSwift/RxSwift/Observables/AsSingle.swift Pods/RxSwift/RxSwift/Observables/Buffer.swift Pods/RxSwift/RxSwift/Observables/Catch.swift Pods/RxSwift/RxSwift/Observables/CombineLatest+Collection.swift Pods/RxSwift/RxSwift/Observables/CombineLatest+arity.swift Pods/RxSwift/RxSwift/Observables/CombineLatest.swift Pods/RxSwift/RxSwift/Observables/CompactMap.swift Pods/RxSwift/RxSwift/Observables/Concat.swift Pods/RxSwift/RxSwift/Observables/Create.swift Pods/RxSwift/RxSwift/Observables/Debounce.swift Pods/RxSwift/RxSwift/Observables/Debug.swift Pods/RxSwift/RxSwift/Observables/Decode.swift Pods/RxSwift/RxSwift/Observables/DefaultIfEmpty.swift Pods/RxSwift/RxSwift/Observables/Deferred.swift Pods/RxSwift/RxSwift/Observables/Delay.swift Pods/RxSwift/RxSwift/Observables/DelaySubscription.swift Pods/RxSwift/RxSwift/Observables/Dematerialize.swift Pods/RxSwift/RxSwift/Observables/DistinctUntilChanged.swift Pods/RxSwift/RxSwift/Observables/Do.swift Pods/RxSwift/RxSwift/Observables/ElementAt.swift Pods/RxSwift/RxSwift/Observables/Empty.swift Pods/RxSwift/RxSwift/Observables/Enumerated.swift Pods/RxSwift/RxSwift/Observables/Error.swift Pods/RxSwift/RxSwift/Observables/Filter.swift Pods/RxSwift/RxSwift/Observables/First.swift Pods/RxSwift/RxSwift/Observables/Generate.swift Pods/RxSwift/RxSwift/Observables/GroupBy.swift Pods/RxSwift/RxSwift/Observables/Just.swift Pods/RxSwift/RxSwift/Observables/Map.swift Pods/RxSwift/RxSwift/Observables/Materialize.swift Pods/RxSwift/RxSwift/Observables/Merge.swift Pods/RxSwift/RxSwift/Observables/Multicast.swift Pods/RxSwift/RxSwift/Observables/Never.swift Pods/RxSwift/RxSwift/Observables/ObserveOn.swift Pods/RxSwift/RxSwift/Observables/Optional.swift Pods/RxSwift/RxSwift/Observables/Producer.swift Pods/RxSwift/RxSwift/Observables/Range.swift Pods/RxSwift/RxSwift/Observables/Reduce.swift Pods/RxSwift/RxSwift/Observables/Repeat.swift Pods/RxSwift/RxSwift/Observables/RetryWhen.swift Pods/RxSwift/RxSwift/Observables/Sample.swift Pods/RxSwift/RxSwift/Observables/Scan.swift Pods/RxSwift/RxSwift/Observables/Sequence.swift Pods/RxSwift/RxSwift/Observables/ShareReplayScope.swift Pods/RxSwift/RxSwift/Observables/SingleAsync.swift Pods/RxSwift/RxSwift/Observables/Sink.swift Pods/RxSwift/RxSwift/Observables/Skip.swift Pods/RxSwift/RxSwift/Observables/SkipUntil.swift Pods/RxSwift/RxSwift/Observables/SkipWhile.swift Pods/RxSwift/RxSwift/Observables/StartWith.swift Pods/RxSwift/RxSwift/Observables/SubscribeOn.swift Pods/RxSwift/RxSwift/Observables/Switch.swift Pods/RxSwift/RxSwift/Observables/SwitchIfEmpty.swift Pods/RxSwift/RxSwift/Observables/Take.swift Pods/RxSwift/RxSwift/Observables/TakeLast.swift Pods/RxSwift/RxSwift/Observables/TakeWithPredicate.swift Pods/RxSwift/RxSwift/Observables/Throttle.swift Pods/RxSwift/RxSwift/Observables/Timeout.swift Pods/RxSwift/RxSwift/Observables/Timer.swift Pods/RxSwift/RxSwift/Observables/ToArray.swift Pods/RxSwift/RxSwift/Observables/Using.swift Pods/RxSwift/RxSwift/Observables/Window.swift Pods/RxSwift/RxSwift/Observables/WithLatestFrom.swift Pods/RxSwift/RxSwift/Observables/WithUnretained.swift Pods/RxSwift/RxSwift/Observables/Zip+Collection.swift Pods/RxSwift/RxSwift/Observables/Zip+arity.swift Pods/RxSwift/RxSwift/Observables/Zip.swift Pods/RxSwift/RxSwift/ObserverType.swift Pods/RxSwift/RxSwift/Observers/AnonymousObserver.swift Pods/RxSwift/RxSwift/Observers/ObserverBase.swift Pods/RxSwift/RxSwift/Observers/TailRecursiveSink.swift Pods/RxSwift/RxSwift/Reactive.swift Pods/RxSwift/RxSwift/Rx.swift Pods/RxSwift/RxSwift/RxMutableBox.swift Pods/RxSwift/RxSwift/SchedulerType.swift Pods/RxSwift/RxSwift/Schedulers/ConcurrentDispatchQueueScheduler.swift Pods/RxSwift/RxSwift/Schedulers/ConcurrentMainScheduler.swift Pods/RxSwift/RxSwift/Schedulers/CurrentThreadScheduler.swift Pods/RxSwift/RxSwift/Schedulers/HistoricalScheduler.swift Pods/RxSwift/RxSwift/Schedulers/HistoricalSchedulerTimeConverter.swift Pods/RxSwift/RxSwift/Schedulers/Internal/DispatchQueueConfiguration.swift Pods/RxSwift/RxSwift/Schedulers/Internal/InvocableScheduledItem.swift Pods/RxSwift/RxSwift/Schedulers/Internal/InvocableType.swift Pods/RxSwift/RxSwift/Schedulers/Internal/ScheduledItem.swift Pods/RxSwift/RxSwift/Schedulers/Internal/ScheduledItemType.swift Pods/RxSwift/RxSwift/Schedulers/MainScheduler.swift Pods/RxSwift/RxSwift/Schedulers/OperationQueueScheduler.swift Pods/RxSwift/RxSwift/Schedulers/RecursiveScheduler.swift Pods/RxSwift/RxSwift/Schedulers/SchedulerServices+Emulation.swift Pods/RxSwift/RxSwift/Schedulers/SerialDispatchQueueScheduler.swift Pods/RxSwift/RxSwift/Schedulers/VirtualTimeConverterType.swift Pods/RxSwift/RxSwift/Schedulers/VirtualTimeScheduler.swift Pods/RxSwift/RxSwift/Subjects/AsyncSubject.swift Pods/RxSwift/RxSwift/Subjects/BehaviorSubject.swift Pods/RxSwift/RxSwift/Subjects/PublishSubject.swift Pods/RxSwift/RxSwift/Subjects/ReplaySubject.swift Pods/RxSwift/RxSwift/Subjects/SubjectType.swift Pods/RxSwift/RxSwift/SwiftSupport/SwiftSupport.swift Pods/RxSwift/RxSwift/Traits/Infallible/Infallible+CombineLatest+Collection.swift Pods/RxSwift/RxSwift/Traits/Infallible/Infallible+CombineLatest+arity.swift Pods/RxSwift/RxSwift/Traits/Infallible/Infallible+Concurrency.swift Pods/RxSwift/RxSwift/Traits/Infallible/Infallible+Create.swift Pods/RxSwift/RxSwift/Traits/Infallible/Infallible+Debug.swift Pods/RxSwift/RxSwift/Traits/Infallible/Infallible+Operators.swift Pods/RxSwift/RxSwift/Traits/Infallible/Infallible+Zip+arity.swift Pods/RxSwift/RxSwift/Traits/Infallible/Infallible.swift Pods/RxSwift/RxSwift/Traits/Infallible/ObservableConvertibleType+Infallible.swift Pods/RxSwift/RxSwift/Traits/PrimitiveSequence/Completable+AndThen.swift Pods/RxSwift/RxSwift/Traits/PrimitiveSequence/Completable.swift Pods/RxSwift/RxSwift/Traits/PrimitiveSequence/Maybe.swift Pods/RxSwift/RxSwift/Traits/PrimitiveSequence/ObservableType+PrimitiveSequence.swift Pods/RxSwift/RxSwift/Traits/PrimitiveSequence/PrimitiveSequence+Concurrency.swift Pods/RxSwift/RxSwift/Traits/PrimitiveSequence/PrimitiveSequence+Zip+arity.swift Pods/RxSwift/RxSwift/Traits/PrimitiveSequence/PrimitiveSequence.swift Pods/RxSwift/RxSwift/Traits/PrimitiveSequence/Single.swift Pods/SDWebImage/LICENSE Pods/SDWebImage/README.md Pods/SDWebImage/SDWebImage/Core/NSButton+WebCache.h Pods/SDWebImage/SDWebImage/Core/NSButton+WebCache.m Pods/SDWebImage/SDWebImage/Core/NSData+ImageContentType.h Pods/SDWebImage/SDWebImage/Core/NSData+ImageContentType.m Pods/SDWebImage/SDWebImage/Core/NSImage+Compatibility.h Pods/SDWebImage/SDWebImage/Core/NSImage+Compatibility.m Pods/SDWebImage/SDWebImage/Core/SDAnimatedImage.h Pods/SDWebImage/SDWebImage/Core/SDAnimatedImage.m Pods/SDWebImage/SDWebImage/Core/SDAnimatedImagePlayer.h Pods/SDWebImage/SDWebImage/Core/SDAnimatedImagePlayer.m Pods/SDWebImage/SDWebImage/Core/SDAnimatedImageRep.h Pods/SDWebImage/SDWebImage/Core/SDAnimatedImageRep.m Pods/SDWebImage/SDWebImage/Core/SDAnimatedImageView+WebCache.h Pods/SDWebImage/SDWebImage/Core/SDAnimatedImageView+WebCache.m Pods/SDWebImage/SDWebImage/Core/SDAnimatedImageView.h Pods/SDWebImage/SDWebImage/Core/SDAnimatedImageView.m Pods/SDWebImage/SDWebImage/Core/SDCallbackQueue.h Pods/SDWebImage/SDWebImage/Core/SDCallbackQueue.m Pods/SDWebImage/SDWebImage/Core/SDDiskCache.h Pods/SDWebImage/SDWebImage/Core/SDDiskCache.m Pods/SDWebImage/SDWebImage/Core/SDGraphicsImageRenderer.h Pods/SDWebImage/SDWebImage/Core/SDGraphicsImageRenderer.m Pods/SDWebImage/SDWebImage/Core/SDImageAPNGCoder.h Pods/SDWebImage/SDWebImage/Core/SDImageAPNGCoder.m Pods/SDWebImage/SDWebImage/Core/SDImageAWebPCoder.h Pods/SDWebImage/SDWebImage/Core/SDImageAWebPCoder.m Pods/SDWebImage/SDWebImage/Core/SDImageCache.h Pods/SDWebImage/SDWebImage/Core/SDImageCache.m Pods/SDWebImage/SDWebImage/Core/SDImageCacheConfig.h Pods/SDWebImage/SDWebImage/Core/SDImageCacheConfig.m Pods/SDWebImage/SDWebImage/Core/SDImageCacheDefine.h Pods/SDWebImage/SDWebImage/Core/SDImageCacheDefine.m Pods/SDWebImage/SDWebImage/Core/SDImageCachesManager.h Pods/SDWebImage/SDWebImage/Core/SDImageCachesManager.m Pods/SDWebImage/SDWebImage/Core/SDImageCoder.h Pods/SDWebImage/SDWebImage/Core/SDImageCoder.m Pods/SDWebImage/SDWebImage/Core/SDImageCoderHelper.h Pods/SDWebImage/SDWebImage/Core/SDImageCoderHelper.m Pods/SDWebImage/SDWebImage/Core/SDImageCodersManager.h Pods/SDWebImage/SDWebImage/Core/SDImageCodersManager.m Pods/SDWebImage/SDWebImage/Core/SDImageFrame.h Pods/SDWebImage/SDWebImage/Core/SDImageFrame.m Pods/SDWebImage/SDWebImage/Core/SDImageGIFCoder.h Pods/SDWebImage/SDWebImage/Core/SDImageGIFCoder.m Pods/SDWebImage/SDWebImage/Core/SDImageGraphics.h Pods/SDWebImage/SDWebImage/Core/SDImageGraphics.m Pods/SDWebImage/SDWebImage/Core/SDImageHEICCoder.h Pods/SDWebImage/SDWebImage/Core/SDImageHEICCoder.m Pods/SDWebImage/SDWebImage/Core/SDImageIOAnimatedCoder.h Pods/SDWebImage/SDWebImage/Core/SDImageIOAnimatedCoder.m Pods/SDWebImage/SDWebImage/Core/SDImageIOCoder.h Pods/SDWebImage/SDWebImage/Core/SDImageIOCoder.m Pods/SDWebImage/SDWebImage/Core/SDImageLoader.h Pods/SDWebImage/SDWebImage/Core/SDImageLoader.m Pods/SDWebImage/SDWebImage/Core/SDImageLoadersManager.h Pods/SDWebImage/SDWebImage/Core/SDImageLoadersManager.m Pods/SDWebImage/SDWebImage/Core/SDImageTransformer.h Pods/SDWebImage/SDWebImage/Core/SDImageTransformer.m Pods/SDWebImage/SDWebImage/Core/SDMemoryCache.h Pods/SDWebImage/SDWebImage/Core/SDMemoryCache.m Pods/SDWebImage/SDWebImage/Core/SDWebImageCacheKeyFilter.h Pods/SDWebImage/SDWebImage/Core/SDWebImageCacheKeyFilter.m Pods/SDWebImage/SDWebImage/Core/SDWebImageCacheSerializer.h Pods/SDWebImage/SDWebImage/Core/SDWebImageCacheSerializer.m Pods/SDWebImage/SDWebImage/Core/SDWebImageCompat.h Pods/SDWebImage/SDWebImage/Core/SDWebImageCompat.m Pods/SDWebImage/SDWebImage/Core/SDWebImageDefine.h Pods/SDWebImage/SDWebImage/Core/SDWebImageDefine.m Pods/SDWebImage/SDWebImage/Core/SDWebImageDownloader.h Pods/SDWebImage/SDWebImage/Core/SDWebImageDownloader.m Pods/SDWebImage/SDWebImage/Core/SDWebImageDownloaderConfig.h Pods/SDWebImage/SDWebImage/Core/SDWebImageDownloaderConfig.m Pods/SDWebImage/SDWebImage/Core/SDWebImageDownloaderDecryptor.h Pods/SDWebImage/SDWebImage/Core/SDWebImageDownloaderDecryptor.m Pods/SDWebImage/SDWebImage/Core/SDWebImageDownloaderOperation.h Pods/SDWebImage/SDWebImage/Core/SDWebImageDownloaderOperation.m Pods/SDWebImage/SDWebImage/Core/SDWebImageDownloaderRequestModifier.h Pods/SDWebImage/SDWebImage/Core/SDWebImageDownloaderRequestModifier.m Pods/SDWebImage/SDWebImage/Core/SDWebImageDownloaderResponseModifier.h Pods/SDWebImage/SDWebImage/Core/SDWebImageDownloaderResponseModifier.m Pods/SDWebImage/SDWebImage/Core/SDWebImageError.h Pods/SDWebImage/SDWebImage/Core/SDWebImageError.m Pods/SDWebImage/SDWebImage/Core/SDWebImageIndicator.h Pods/SDWebImage/SDWebImage/Core/SDWebImageIndicator.m Pods/SDWebImage/SDWebImage/Core/SDWebImageManager.h Pods/SDWebImage/SDWebImage/Core/SDWebImageManager.m Pods/SDWebImage/SDWebImage/Core/SDWebImageOperation.h Pods/SDWebImage/SDWebImage/Core/SDWebImageOperation.m Pods/SDWebImage/SDWebImage/Core/SDWebImageOptionsProcessor.h Pods/SDWebImage/SDWebImage/Core/SDWebImageOptionsProcessor.m Pods/SDWebImage/SDWebImage/Core/SDWebImagePrefetcher.h Pods/SDWebImage/SDWebImage/Core/SDWebImagePrefetcher.m Pods/SDWebImage/SDWebImage/Core/SDWebImageTransition.h Pods/SDWebImage/SDWebImage/Core/SDWebImageTransition.m Pods/SDWebImage/SDWebImage/Core/UIButton+WebCache.h Pods/SDWebImage/SDWebImage/Core/UIButton+WebCache.m Pods/SDWebImage/SDWebImage/Core/UIImage+ExtendedCacheData.h Pods/SDWebImage/SDWebImage/Core/UIImage+ExtendedCacheData.m Pods/SDWebImage/SDWebImage/Core/UIImage+ForceDecode.h Pods/SDWebImage/SDWebImage/Core/UIImage+ForceDecode.m Pods/SDWebImage/SDWebImage/Core/UIImage+GIF.h Pods/SDWebImage/SDWebImage/Core/UIImage+GIF.m Pods/SDWebImage/SDWebImage/Core/UIImage+MemoryCacheCost.h Pods/SDWebImage/SDWebImage/Core/UIImage+MemoryCacheCost.m Pods/SDWebImage/SDWebImage/Core/UIImage+Metadata.h Pods/SDWebImage/SDWebImage/Core/UIImage+Metadata.m Pods/SDWebImage/SDWebImage/Core/UIImage+MultiFormat.h Pods/SDWebImage/SDWebImage/Core/UIImage+MultiFormat.m Pods/SDWebImage/SDWebImage/Core/UIImage+Transform.h Pods/SDWebImage/SDWebImage/Core/UIImage+Transform.m Pods/SDWebImage/SDWebImage/Core/UIImageView+HighlightedWebCache.h Pods/SDWebImage/SDWebImage/Core/UIImageView+HighlightedWebCache.m Pods/SDWebImage/SDWebImage/Core/UIImageView+WebCache.h Pods/SDWebImage/SDWebImage/Core/UIImageView+WebCache.m Pods/SDWebImage/SDWebImage/Core/UIView+WebCache.h Pods/SDWebImage/SDWebImage/Core/UIView+WebCache.m Pods/SDWebImage/SDWebImage/Core/UIView+WebCacheOperation.h Pods/SDWebImage/SDWebImage/Core/UIView+WebCacheOperation.m Pods/SDWebImage/SDWebImage/Core/UIView+WebCacheState.h Pods/SDWebImage/SDWebImage/Core/UIView+WebCacheState.m Pods/SDWebImage/SDWebImage/Private/NSBezierPath+SDRoundedCorners.h Pods/SDWebImage/SDWebImage/Private/NSBezierPath+SDRoundedCorners.m Pods/SDWebImage/SDWebImage/Private/SDAssociatedObject.h Pods/SDWebImage/SDWebImage/Private/SDAssociatedObject.m Pods/SDWebImage/SDWebImage/Private/SDAsyncBlockOperation.h Pods/SDWebImage/SDWebImage/Private/SDAsyncBlockOperation.m Pods/SDWebImage/SDWebImage/Private/SDDeviceHelper.h Pods/SDWebImage/SDWebImage/Private/SDDeviceHelper.m Pods/SDWebImage/SDWebImage/Private/SDDisplayLink.h Pods/SDWebImage/SDWebImage/Private/SDDisplayLink.m Pods/SDWebImage/SDWebImage/Private/SDFileAttributeHelper.h Pods/SDWebImage/SDWebImage/Private/SDFileAttributeHelper.m Pods/SDWebImage/SDWebImage/Private/SDImageAssetManager.h Pods/SDWebImage/SDWebImage/Private/SDImageAssetManager.m Pods/SDWebImage/SDWebImage/Private/SDImageCachesManagerOperation.h Pods/SDWebImage/SDWebImage/Private/SDImageCachesManagerOperation.m Pods/SDWebImage/SDWebImage/Private/SDImageFramePool.h Pods/SDWebImage/SDWebImage/Private/SDImageFramePool.m Pods/SDWebImage/SDWebImage/Private/SDImageIOAnimatedCoderInternal.h Pods/SDWebImage/SDWebImage/Private/SDInternalMacros.h Pods/SDWebImage/SDWebImage/Private/SDInternalMacros.m Pods/SDWebImage/SDWebImage/Private/SDWeakProxy.h Pods/SDWebImage/SDWebImage/Private/SDWeakProxy.m Pods/SDWebImage/SDWebImage/Private/SDWebImageTransitionInternal.h Pods/SDWebImage/SDWebImage/Private/SDmetamacros.h Pods/SDWebImage/SDWebImage/Private/UIColor+SDHexString.h Pods/SDWebImage/SDWebImage/Private/UIColor+SDHexString.m Pods/SDWebImage/WebImage/SDWebImage.h Pods/SPPageMenu/README.md Pods/SPPageMenu/SPPageMenu/SPPageMenu.h Pods/SPPageMenu/SPPageMenu/SPPageMenu.m Pods/SVProgressHUD/LICENSE Pods/SVProgressHUD/README.md Pods/SVProgressHUD/SVProgressHUD/PrivacyInfo.xcprivacy Pods/SVProgressHUD/SVProgressHUD/SVIndefiniteAnimatedView.h Pods/SVProgressHUD/SVProgressHUD/SVIndefiniteAnimatedView.m Pods/SVProgressHUD/SVProgressHUD/SVProgressAnimatedView.h Pods/SVProgressHUD/SVProgressHUD/SVProgressAnimatedView.m Pods/SVProgressHUD/SVProgressHUD/SVProgressHUD.bundle/angle-mask.png Pods/SVProgressHUD/SVProgressHUD/SVProgressHUD.bundle/angle-mask@2x.png Pods/SVProgressHUD/SVProgressHUD/SVProgressHUD.bundle/angle-mask@3x.png Pods/SVProgressHUD/SVProgressHUD/SVProgressHUD.bundle/error.png Pods/SVProgressHUD/SVProgressHUD/SVProgressHUD.bundle/error@2x.png Pods/SVProgressHUD/SVProgressHUD/SVProgressHUD.bundle/error@3x.png Pods/SVProgressHUD/SVProgressHUD/SVProgressHUD.bundle/info.png Pods/SVProgressHUD/SVProgressHUD/SVProgressHUD.bundle/info@2x.png Pods/SVProgressHUD/SVProgressHUD/SVProgressHUD.bundle/info@3x.png Pods/SVProgressHUD/SVProgressHUD/SVProgressHUD.bundle/success.png Pods/SVProgressHUD/SVProgressHUD/SVProgressHUD.bundle/success@2x.png Pods/SVProgressHUD/SVProgressHUD/SVProgressHUD.bundle/success@3x.png Pods/SVProgressHUD/SVProgressHUD/SVProgressHUD.h Pods/SVProgressHUD/SVProgressHUD/SVProgressHUD.m Pods/SVProgressHUD/SVProgressHUD/SVRadialGradientLayer.h Pods/SVProgressHUD/SVProgressHUD/SVRadialGradientLayer.m Pods/SnapKit/LICENSE Pods/SnapKit/README.md Pods/SnapKit/Sources/Constraint.swift Pods/SnapKit/Sources/ConstraintAttributes.swift Pods/SnapKit/Sources/ConstraintConfig.swift Pods/SnapKit/Sources/ConstraintConstantTarget.swift Pods/SnapKit/Sources/ConstraintDSL.swift Pods/SnapKit/Sources/ConstraintDescription.swift Pods/SnapKit/Sources/ConstraintDirectionalInsetTarget.swift Pods/SnapKit/Sources/ConstraintDirectionalInsets.swift Pods/SnapKit/Sources/ConstraintInsetTarget.swift Pods/SnapKit/Sources/ConstraintInsets.swift Pods/SnapKit/Sources/ConstraintItem.swift Pods/SnapKit/Sources/ConstraintLayoutGuide+Extensions.swift Pods/SnapKit/Sources/ConstraintLayoutGuide.swift Pods/SnapKit/Sources/ConstraintLayoutGuideDSL.swift Pods/SnapKit/Sources/ConstraintLayoutSupport.swift Pods/SnapKit/Sources/ConstraintLayoutSupportDSL.swift Pods/SnapKit/Sources/ConstraintMaker.swift Pods/SnapKit/Sources/ConstraintMakerEditable.swift Pods/SnapKit/Sources/ConstraintMakerExtendable.swift Pods/SnapKit/Sources/ConstraintMakerFinalizable.swift Pods/SnapKit/Sources/ConstraintMakerPrioritizable.swift Pods/SnapKit/Sources/ConstraintMakerRelatable+Extensions.swift Pods/SnapKit/Sources/ConstraintMakerRelatable.swift Pods/SnapKit/Sources/ConstraintMultiplierTarget.swift Pods/SnapKit/Sources/ConstraintOffsetTarget.swift Pods/SnapKit/Sources/ConstraintPriority.swift Pods/SnapKit/Sources/ConstraintPriorityTarget.swift Pods/SnapKit/Sources/ConstraintRelatableTarget.swift Pods/SnapKit/Sources/ConstraintRelation.swift Pods/SnapKit/Sources/ConstraintView+Extensions.swift Pods/SnapKit/Sources/ConstraintView.swift Pods/SnapKit/Sources/ConstraintViewDSL.swift Pods/SnapKit/Sources/Debugging.swift Pods/SnapKit/Sources/LayoutConstraint.swift Pods/SnapKit/Sources/LayoutConstraintItem.swift Pods/SnapKit/Sources/Typealiases.swift Pods/SnapKit/Sources/UILayoutSupport+Extensions.swift Pods/SwifterSwift/LICENSE Pods/SwifterSwift/README.md Pods/SwifterSwift/Sources/SwifterSwift/AppKit/NSColorExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/AppKit/NSImageExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/AppKit/NSViewExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/Combine/FutureExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/CoreAnimation/CAGradientLayerExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/CoreAnimation/CATransform3DExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/CoreGraphics/CGAffineTransformExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/CoreGraphics/CGColorExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/CoreGraphics/CGFloatExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/CoreGraphics/CGPointExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/CoreGraphics/CGRectExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/CoreGraphics/CGSizeExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/CoreGraphics/CGVectorExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/CoreLocation/CLLocationArrayExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/CoreLocation/CLLocationExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/CoreLocation/CLVisitExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/CryptoKit/DigestExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/Dispatch/DispatchQueueExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/Foundation/CalendarExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/Foundation/DataExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/Foundation/DateExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/Foundation/FileManagerExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/Foundation/LocaleExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/Foundation/MeasurementExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/Foundation/NSAttributedStringExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/Foundation/NSPredicateExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/Foundation/NSRegularExpressionExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/Foundation/NotificationCenterExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/Foundation/URLExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/Foundation/URLRequestExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/Foundation/URLSessionExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/Foundation/UserDefaultsExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/HealthKit/HKActivitySummaryExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/MapKit/MKMapViewExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/MapKit/MKMultiPointExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/MapKit/MKPolylineExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/SceneKit/SCNBoxExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/SceneKit/SCNCapsuleExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/SceneKit/SCNConeExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/SceneKit/SCNCylinderExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/SceneKit/SCNGeometryExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/SceneKit/SCNMaterialExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/SceneKit/SCNPlaneExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/SceneKit/SCNShapeExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/SceneKit/SCNSphereExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/SceneKit/SCNVector3Extensions.swift Pods/SwifterSwift/Sources/SwifterSwift/Shared/ColorExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/Shared/EdgeInsetsExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/Shared/FontExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/SpriteKit/SKNodeExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/SpriteKit/SKSpriteNodeExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/StoreKit/SKProductExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/SwiftStdlib/ArrayExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/SwiftStdlib/BidirectionalCollectionExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/SwiftStdlib/BinaryFloatingPointExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/SwiftStdlib/BinaryIntegerExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/SwiftStdlib/BoolExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/SwiftStdlib/CharacterExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/SwiftStdlib/CollectionExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/SwiftStdlib/ComparableExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/SwiftStdlib/DecodableExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/SwiftStdlib/DefaultStringInterpolationExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/SwiftStdlib/DictionaryExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/SwiftStdlib/DoubleExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/SwiftStdlib/FloatExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/SwiftStdlib/FloatingPointExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/SwiftStdlib/IntExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/SwiftStdlib/KeyedDecodingContainerExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/SwiftStdlib/MutableCollectionExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/SwiftStdlib/OptionalExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/SwiftStdlib/RangeReplaceableCollectionExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/SwiftStdlib/SequenceExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/SwiftStdlib/SignedIntegerExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/SwiftStdlib/SignedNumericExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/SwiftStdlib/StringExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/SwiftStdlib/StringProtocolExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/UIKit/UIActivityExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/UIKit/UIAlertControllerExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/UIKit/UIApplicationExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/UIKit/UIBarButtonItemExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/UIKit/UIBezierPathExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/UIKit/UIButtonExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/UIKit/UICollectionViewExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/UIKit/UIColorExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/UIKit/UIFontExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/UIKit/UIGestureRecognizerExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/UIKit/UIImageExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/UIKit/UIImageViewExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/UIKit/UILabelExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/UIKit/UILayoutPriorityExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/UIKit/UINavigationBarExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/UIKit/UINavigationControllerExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/UIKit/UINavigationItemExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/UIKit/UIRefreshControlExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/UIKit/UIScrollViewExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/UIKit/UISearchBarExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/UIKit/UISegmentedControlExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/UIKit/UISliderExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/UIKit/UIStackViewExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/UIKit/UIStoryboardExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/UIKit/UISwitchExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/UIKit/UITabBarExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/UIKit/UITableViewExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/UIKit/UITextFieldExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/UIKit/UITextViewExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/UIKit/UIViewControllerExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/UIKit/UIViewExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/UIKit/UIWindowExtensions.swift Pods/SwifterSwift/Sources/SwifterSwift/WebKit/WKWebViewExtensions.swift Pods/SwiftyStoreKit/LICENSE.md Pods/SwiftyStoreKit/README.md Pods/SwiftyStoreKit/Sources/SwiftyStoreKit/AppleReceiptValidator.swift Pods/SwiftyStoreKit/Sources/SwiftyStoreKit/CompleteTransactionsController.swift Pods/SwiftyStoreKit/Sources/SwiftyStoreKit/InAppProductQueryRequest.swift Pods/SwiftyStoreKit/Sources/SwiftyStoreKit/InAppReceipt.swift Pods/SwiftyStoreKit/Sources/SwiftyStoreKit/InAppReceiptRefreshRequest.swift Pods/SwiftyStoreKit/Sources/SwiftyStoreKit/InAppReceiptVerificator.swift Pods/SwiftyStoreKit/Sources/SwiftyStoreKit/OS.swift Pods/SwiftyStoreKit/Sources/SwiftyStoreKit/PaymentQueueController.swift Pods/SwiftyStoreKit/Sources/SwiftyStoreKit/PaymentsController.swift Pods/SwiftyStoreKit/Sources/SwiftyStoreKit/ProductsInfoController.swift Pods/SwiftyStoreKit/Sources/SwiftyStoreKit/RestorePurchasesController.swift Pods/SwiftyStoreKit/Sources/SwiftyStoreKit/SKProduct+LocalizedPrice.swift Pods/SwiftyStoreKit/Sources/SwiftyStoreKit/SKProductDiscount+LocalizedPrice.swift Pods/SwiftyStoreKit/Sources/SwiftyStoreKit/SwiftyStoreKit+Types.swift Pods/SwiftyStoreKit/Sources/SwiftyStoreKit/SwiftyStoreKit.swift Pods/TZImagePickerController/LICENSE Pods/TZImagePickerController/README.md Pods/TZImagePickerController/TZImagePickerController/Location/TZLocationManager.h Pods/TZImagePickerController/TZImagePickerController/Location/TZLocationManager.m Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/NSBundle+TZImagePicker.h Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/NSBundle+TZImagePicker.m Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZAssetCell.h Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZAssetCell.m Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZAssetModel.h Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZAssetModel.m Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZAuthLimitedFooterTipView.h Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZAuthLimitedFooterTipView.m Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZGifPhotoPreviewController.h Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZGifPhotoPreviewController.m Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZImageCropManager.h Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZImageCropManager.m Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZImageManager.h Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZImageManager.m Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZImagePickerController.bundle/MMVideoPreviewPlay@2x.png Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZImagePickerController.bundle/MMVideoPreviewPlayHL@2x.png Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZImagePickerController.bundle/VideoSendIcon@2x.png Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZImagePickerController.bundle/addMore@2x.png Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZImagePickerController.bundle/ar.lproj/Localizable.strings Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZImagePickerController.bundle/de.lproj/Localizable.strings Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZImagePickerController.bundle/en.lproj/Localizable.strings Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZImagePickerController.bundle/es.lproj/Localizable.strings Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZImagePickerController.bundle/fr.lproj/Localizable.strings Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZImagePickerController.bundle/iCloudError@2x.png Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZImagePickerController.bundle/ja.lproj/Localizable.strings Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZImagePickerController.bundle/ko-KP.lproj/Localizable.strings Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZImagePickerController.bundle/navi_back@2x.png Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZImagePickerController.bundle/photo_def_photoPickerVc@2x.png Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZImagePickerController.bundle/photo_def_previewVc@2x.png Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZImagePickerController.bundle/photo_number_icon@2x.png Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZImagePickerController.bundle/photo_original_def@2x.png Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZImagePickerController.bundle/photo_original_sel@2x.png Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZImagePickerController.bundle/photo_sel_photoPickerVc@2x.png Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZImagePickerController.bundle/photo_sel_previewVc@2x.png Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZImagePickerController.bundle/preview_number_icon@2x.png Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZImagePickerController.bundle/preview_original_def@2x.png Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZImagePickerController.bundle/pt.lproj/Localizable.strings Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZImagePickerController.bundle/right_arrow@2x.png Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZImagePickerController.bundle/ru.lproj/Localizable.strings Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZImagePickerController.bundle/takePicture80@2x.png Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZImagePickerController.bundle/takePicture@2x.png Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZImagePickerController.bundle/tip@2x.png Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZImagePickerController.bundle/vi.lproj/Localizable.strings Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZImagePickerController.bundle/zh-Hans.lproj/Localizable.strings Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZImagePickerController.bundle/zh-Hant.lproj/Localizable.strings Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZImagePickerController.h Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZImagePickerController.m Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZImageRequestOperation.h Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZImageRequestOperation.m Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZPhotoPickerController.h Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZPhotoPickerController.m Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZPhotoPreviewCell.h Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZPhotoPreviewCell.m Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZPhotoPreviewController.h Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZPhotoPreviewController.m Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZProgressView.h Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZProgressView.m Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZVideoCropController.h Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZVideoCropController.m Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZVideoEditedPreviewController.h Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZVideoEditedPreviewController.m Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZVideoPlayerController.h Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/TZVideoPlayerController.m Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/UIView+TZLayout.h Pods/TZImagePickerController/TZImagePickerController/TZImagePickerController/UIView+TZLayout.m Pods/Target Support Files/Alamofire/Alamofire-Info.plist Pods/Target Support Files/Alamofire/Alamofire-dummy.m Pods/Target Support Files/Alamofire/Alamofire-prefix.pch Pods/Target Support Files/Alamofire/Alamofire-umbrella.h Pods/Target Support Files/Alamofire/Alamofire.debug.xcconfig Pods/Target Support Files/Alamofire/Alamofire.modulemap Pods/Target Support Files/Alamofire/Alamofire.release.xcconfig Pods/Target Support Files/CryptoSwift/CryptoSwift-Info.plist Pods/Target Support Files/CryptoSwift/CryptoSwift-dummy.m Pods/Target Support Files/CryptoSwift/CryptoSwift-prefix.pch Pods/Target Support Files/CryptoSwift/CryptoSwift-umbrella.h Pods/Target Support Files/CryptoSwift/CryptoSwift.debug.xcconfig Pods/Target Support Files/CryptoSwift/CryptoSwift.modulemap Pods/Target Support Files/CryptoSwift/CryptoSwift.release.xcconfig Pods/Target Support Files/Differentiator/Differentiator-Info.plist Pods/Target Support Files/Differentiator/Differentiator-dummy.m Pods/Target Support Files/Differentiator/Differentiator-prefix.pch Pods/Target Support Files/Differentiator/Differentiator-umbrella.h Pods/Target Support Files/Differentiator/Differentiator.debug.xcconfig Pods/Target Support Files/Differentiator/Differentiator.modulemap Pods/Target Support Files/Differentiator/Differentiator.release.xcconfig Pods/Target Support Files/EmptyDataSet-Swift/EmptyDataSet-Swift-Info.plist Pods/Target Support Files/EmptyDataSet-Swift/EmptyDataSet-Swift-dummy.m Pods/Target Support Files/EmptyDataSet-Swift/EmptyDataSet-Swift-prefix.pch Pods/Target Support Files/EmptyDataSet-Swift/EmptyDataSet-Swift-umbrella.h Pods/Target Support Files/EmptyDataSet-Swift/EmptyDataSet-Swift.debug.xcconfig Pods/Target Support Files/EmptyDataSet-Swift/EmptyDataSet-Swift.modulemap Pods/Target Support Files/EmptyDataSet-Swift/EmptyDataSet-Swift.release.xcconfig Pods/Target Support Files/FFPage/FFPage-Info.plist Pods/Target Support Files/FFPage/FFPage-dummy.m Pods/Target Support Files/FFPage/FFPage-prefix.pch Pods/Target Support Files/FFPage/FFPage-umbrella.h Pods/Target Support Files/FFPage/FFPage.debug.xcconfig Pods/Target Support Files/FFPage/FFPage.modulemap Pods/Target Support Files/FFPage/FFPage.release.xcconfig Pods/Target Support Files/HandyJSON/HandyJSON-Info.plist Pods/Target Support Files/HandyJSON/HandyJSON-dummy.m Pods/Target Support Files/HandyJSON/HandyJSON-prefix.pch Pods/Target Support Files/HandyJSON/HandyJSON-umbrella.h Pods/Target Support Files/HandyJSON/HandyJSON.debug.xcconfig Pods/Target Support Files/HandyJSON/HandyJSON.modulemap Pods/Target Support Files/HandyJSON/HandyJSON.release.xcconfig Pods/Target Support Files/IQKeyboardManager/IQKeyboardManager-Info.plist Pods/Target Support Files/IQKeyboardManager/IQKeyboardManager-dummy.m Pods/Target Support Files/IQKeyboardManager/IQKeyboardManager-prefix.pch Pods/Target Support Files/IQKeyboardManager/IQKeyboardManager-umbrella.h Pods/Target Support Files/IQKeyboardManager/IQKeyboardManager.debug.xcconfig Pods/Target Support Files/IQKeyboardManager/IQKeyboardManager.modulemap Pods/Target Support Files/IQKeyboardManager/IQKeyboardManager.release.xcconfig Pods/Target Support Files/IQKeyboardManagerSwift/IQKeyboardManagerSwift-Info.plist Pods/Target Support Files/IQKeyboardManagerSwift/IQKeyboardManagerSwift-dummy.m Pods/Target Support Files/IQKeyboardManagerSwift/IQKeyboardManagerSwift-prefix.pch Pods/Target Support Files/IQKeyboardManagerSwift/IQKeyboardManagerSwift-umbrella.h Pods/Target Support Files/IQKeyboardManagerSwift/IQKeyboardManagerSwift.debug.xcconfig Pods/Target Support Files/IQKeyboardManagerSwift/IQKeyboardManagerSwift.modulemap Pods/Target Support Files/IQKeyboardManagerSwift/IQKeyboardManagerSwift.release.xcconfig Pods/Target Support Files/JQTools/JQTools-Info.plist Pods/Target Support Files/JQTools/JQTools-dummy.m Pods/Target Support Files/JQTools/JQTools-prefix.pch Pods/Target Support Files/JQTools/JQTools-umbrella.h Pods/Target Support Files/JQTools/JQTools.debug.xcconfig Pods/Target Support Files/JQTools/JQTools.modulemap Pods/Target Support Files/JQTools/JQTools.release.xcconfig Pods/Target Support Files/JQTools/ResourceBundle-JQToolsRes-JQTools-Info.plist Pods/Target Support Files/Lantern/Lantern-Info.plist Pods/Target Support Files/Lantern/Lantern-dummy.m Pods/Target Support Files/Lantern/Lantern-prefix.pch Pods/Target Support Files/Lantern/Lantern-umbrella.h Pods/Target Support Files/Lantern/Lantern.debug.xcconfig Pods/Target Support Files/Lantern/Lantern.modulemap Pods/Target Support Files/Lantern/Lantern.release.xcconfig Pods/Target Support Files/MJRefresh/MJRefresh-Info.plist Pods/Target Support Files/MJRefresh/MJRefresh-dummy.m Pods/Target Support Files/MJRefresh/MJRefresh-prefix.pch Pods/Target Support Files/MJRefresh/MJRefresh-umbrella.h Pods/Target Support Files/MJRefresh/MJRefresh.debug.xcconfig Pods/Target Support Files/MJRefresh/MJRefresh.modulemap Pods/Target Support Files/MJRefresh/MJRefresh.release.xcconfig Pods/Target Support Files/ObjcExceptionBridging/ObjcExceptionBridging-Info.plist Pods/Target Support Files/ObjcExceptionBridging/ObjcExceptionBridging-dummy.m Pods/Target Support Files/ObjcExceptionBridging/ObjcExceptionBridging-prefix.pch Pods/Target Support Files/ObjcExceptionBridging/ObjcExceptionBridging-umbrella.h Pods/Target Support Files/ObjcExceptionBridging/ObjcExceptionBridging.debug.xcconfig Pods/Target Support Files/ObjcExceptionBridging/ObjcExceptionBridging.modulemap Pods/Target Support Files/ObjcExceptionBridging/ObjcExceptionBridging.release.xcconfig Pods/Target Support Files/ObjectMapper/ObjectMapper-Info.plist Pods/Target Support Files/ObjectMapper/ObjectMapper-dummy.m Pods/Target Support Files/ObjectMapper/ObjectMapper-prefix.pch Pods/Target Support Files/ObjectMapper/ObjectMapper-umbrella.h Pods/Target Support Files/ObjectMapper/ObjectMapper.debug.xcconfig Pods/Target Support Files/ObjectMapper/ObjectMapper.modulemap Pods/Target Support Files/ObjectMapper/ObjectMapper.release.xcconfig Pods/Target Support Files/Pods-DolphinEnglishLearnStudent/Pods-DolphinEnglishLearnStudent-Info.plist Pods/Target Support Files/Pods-DolphinEnglishLearnStudent/Pods-DolphinEnglishLearnStudent-acknowledgements.markdown Pods/Target Support Files/Pods-DolphinEnglishLearnStudent/Pods-DolphinEnglishLearnStudent-acknowledgements.plist Pods/Target Support Files/Pods-DolphinEnglishLearnStudent/Pods-DolphinEnglishLearnStudent-dummy.m Pods/Target Support Files/Pods-DolphinEnglishLearnStudent/Pods-DolphinEnglishLearnStudent-frameworks-Debug-input-files.xcfilelist Pods/Target Support Files/Pods-DolphinEnglishLearnStudent/Pods-DolphinEnglishLearnStudent-frameworks-Debug-output-files.xcfilelist Pods/Target Support Files/Pods-DolphinEnglishLearnStudent/Pods-DolphinEnglishLearnStudent-frameworks-Release-input-files.xcfilelist Pods/Target Support Files/Pods-DolphinEnglishLearnStudent/Pods-DolphinEnglishLearnStudent-frameworks-Release-output-files.xcfilelist Pods/Target Support Files/Pods-DolphinEnglishLearnStudent/Pods-DolphinEnglishLearnStudent-frameworks.sh Pods/Target Support Files/Pods-DolphinEnglishLearnStudent/Pods-DolphinEnglishLearnStudent-umbrella.h Pods/Target Support Files/Pods-DolphinEnglishLearnStudent/Pods-DolphinEnglishLearnStudent.debug.xcconfig Pods/Target Support Files/Pods-DolphinEnglishLearnStudent/Pods-DolphinEnglishLearnStudent.modulemap Pods/Target Support Files/Pods-DolphinEnglishLearnStudent/Pods-DolphinEnglishLearnStudent.release.xcconfig Pods/Target Support Files/QMUIKit/QMUIKit-Info.plist Pods/Target Support Files/QMUIKit/QMUIKit-dummy.m Pods/Target Support Files/QMUIKit/QMUIKit-prefix.pch Pods/Target Support Files/QMUIKit/QMUIKit-umbrella.h Pods/Target Support Files/QMUIKit/QMUIKit.debug.xcconfig Pods/Target Support Files/QMUIKit/QMUIKit.modulemap Pods/Target Support Files/QMUIKit/QMUIKit.release.xcconfig Pods/Target Support Files/QMUIKit/ResourceBundle-QMUIResources-QMUIKit-Info.plist Pods/Target Support Files/RxCocoa/RxCocoa-Info.plist Pods/Target Support Files/RxCocoa/RxCocoa-dummy.m Pods/Target Support Files/RxCocoa/RxCocoa-prefix.pch Pods/Target Support Files/RxCocoa/RxCocoa-umbrella.h Pods/Target Support Files/RxCocoa/RxCocoa.debug.xcconfig Pods/Target Support Files/RxCocoa/RxCocoa.modulemap Pods/Target Support Files/RxCocoa/RxCocoa.release.xcconfig Pods/Target Support Files/RxDataSources/RxDataSources-Info.plist Pods/Target Support Files/RxDataSources/RxDataSources-dummy.m Pods/Target Support Files/RxDataSources/RxDataSources-prefix.pch Pods/Target Support Files/RxDataSources/RxDataSources-umbrella.h Pods/Target Support Files/RxDataSources/RxDataSources.debug.xcconfig Pods/Target Support Files/RxDataSources/RxDataSources.modulemap Pods/Target Support Files/RxDataSources/RxDataSources.release.xcconfig Pods/Target Support Files/RxRelay/RxRelay-Info.plist Pods/Target Support Files/RxRelay/RxRelay-dummy.m Pods/Target Support Files/RxRelay/RxRelay-prefix.pch Pods/Target Support Files/RxRelay/RxRelay-umbrella.h Pods/Target Support Files/RxRelay/RxRelay.debug.xcconfig Pods/Target Support Files/RxRelay/RxRelay.modulemap Pods/Target Support Files/RxRelay/RxRelay.release.xcconfig Pods/Target Support Files/RxSwift/RxSwift-Info.plist Pods/Target Support Files/RxSwift/RxSwift-dummy.m Pods/Target Support Files/RxSwift/RxSwift-prefix.pch Pods/Target Support Files/RxSwift/RxSwift-umbrella.h Pods/Target Support Files/RxSwift/RxSwift.debug.xcconfig Pods/Target Support Files/RxSwift/RxSwift.modulemap Pods/Target Support Files/RxSwift/RxSwift.release.xcconfig Pods/Target Support Files/SDWebImage/SDWebImage-Info.plist Pods/Target Support Files/SDWebImage/SDWebImage-dummy.m Pods/Target Support Files/SDWebImage/SDWebImage-prefix.pch Pods/Target Support Files/SDWebImage/SDWebImage-umbrella.h Pods/Target Support Files/SDWebImage/SDWebImage.debug.xcconfig Pods/Target Support Files/SDWebImage/SDWebImage.modulemap Pods/Target Support Files/SDWebImage/SDWebImage.release.xcconfig Pods/Target Support Files/SPPageMenu/SPPageMenu-Info.plist Pods/Target Support Files/SPPageMenu/SPPageMenu-dummy.m Pods/Target Support Files/SPPageMenu/SPPageMenu-prefix.pch Pods/Target Support Files/SPPageMenu/SPPageMenu-umbrella.h Pods/Target Support Files/SPPageMenu/SPPageMenu.debug.xcconfig Pods/Target Support Files/SPPageMenu/SPPageMenu.modulemap Pods/Target Support Files/SPPageMenu/SPPageMenu.release.xcconfig Pods/Target Support Files/SVProgressHUD/SVProgressHUD-Info.plist Pods/Target Support Files/SVProgressHUD/SVProgressHUD-dummy.m Pods/Target Support Files/SVProgressHUD/SVProgressHUD-prefix.pch Pods/Target Support Files/SVProgressHUD/SVProgressHUD-umbrella.h Pods/Target Support Files/SVProgressHUD/SVProgressHUD.debug.xcconfig Pods/Target Support Files/SVProgressHUD/SVProgressHUD.modulemap Pods/Target Support Files/SVProgressHUD/SVProgressHUD.release.xcconfig Pods/Target Support Files/SnapKit/SnapKit-Info.plist Pods/Target Support Files/SnapKit/SnapKit-dummy.m Pods/Target Support Files/SnapKit/SnapKit-prefix.pch Pods/Target Support Files/SnapKit/SnapKit-umbrella.h Pods/Target Support Files/SnapKit/SnapKit.debug.xcconfig Pods/Target Support Files/SnapKit/SnapKit.modulemap Pods/Target Support Files/SnapKit/SnapKit.release.xcconfig Pods/Target Support Files/SwifterSwift/SwifterSwift-Info.plist Pods/Target Support Files/SwifterSwift/SwifterSwift-dummy.m Pods/Target Support Files/SwifterSwift/SwifterSwift-prefix.pch Pods/Target Support Files/SwifterSwift/SwifterSwift-umbrella.h Pods/Target Support Files/SwifterSwift/SwifterSwift.debug.xcconfig Pods/Target Support Files/SwifterSwift/SwifterSwift.modulemap Pods/Target Support Files/SwifterSwift/SwifterSwift.release.xcconfig Pods/Target Support Files/SwiftyStoreKit/SwiftyStoreKit-Info.plist Pods/Target Support Files/SwiftyStoreKit/SwiftyStoreKit-dummy.m Pods/Target Support Files/SwiftyStoreKit/SwiftyStoreKit-prefix.pch Pods/Target Support Files/SwiftyStoreKit/SwiftyStoreKit-umbrella.h Pods/Target Support Files/SwiftyStoreKit/SwiftyStoreKit.debug.xcconfig Pods/Target Support Files/SwiftyStoreKit/SwiftyStoreKit.modulemap Pods/Target Support Files/SwiftyStoreKit/SwiftyStoreKit.release.xcconfig Pods/Target Support Files/TZImagePickerController/TZImagePickerController-Info.plist Pods/Target Support Files/TZImagePickerController/TZImagePickerController-dummy.m Pods/Target Support Files/TZImagePickerController/TZImagePickerController-prefix.pch Pods/Target Support Files/TZImagePickerController/TZImagePickerController-umbrella.h Pods/Target Support Files/TZImagePickerController/TZImagePickerController.debug.xcconfig Pods/Target Support Files/TZImagePickerController/TZImagePickerController.modulemap Pods/Target Support Files/TZImagePickerController/TZImagePickerController.release.xcconfig Pods/Target Support Files/UserDefaultsStore/UserDefaultsStore-Info.plist Pods/Target Support Files/UserDefaultsStore/UserDefaultsStore-dummy.m Pods/Target Support Files/UserDefaultsStore/UserDefaultsStore-prefix.pch Pods/Target Support Files/UserDefaultsStore/UserDefaultsStore-umbrella.h Pods/Target Support Files/UserDefaultsStore/UserDefaultsStore.debug.xcconfig Pods/Target Support Files/UserDefaultsStore/UserDefaultsStore.modulemap Pods/Target Support Files/UserDefaultsStore/UserDefaultsStore.release.xcconfig Pods/Target Support Files/VTMagic/VTMagic-Info.plist Pods/Target Support Files/VTMagic/VTMagic-dummy.m Pods/Target Support Files/VTMagic/VTMagic-prefix.pch Pods/Target Support Files/VTMagic/VTMagic-umbrella.h Pods/Target Support Files/VTMagic/VTMagic.debug.xcconfig Pods/Target Support Files/VTMagic/VTMagic.modulemap Pods/Target Support Files/VTMagic/VTMagic.release.xcconfig Pods/Target Support Files/WechatOpenSDK-XCFramework/WechatOpenSDK-XCFramework-xcframeworks-input-files.xcfilelist Pods/Target Support Files/WechatOpenSDK-XCFramework/WechatOpenSDK-XCFramework-xcframeworks-output-files.xcfilelist Pods/Target Support Files/WechatOpenSDK-XCFramework/WechatOpenSDK-XCFramework-xcframeworks.sh Pods/Target Support Files/WechatOpenSDK-XCFramework/WechatOpenSDK-XCFramework.debug.xcconfig Pods/Target Support Files/WechatOpenSDK-XCFramework/WechatOpenSDK-XCFramework.release.xcconfig Pods/Target Support Files/XCGLogger/XCGLogger-Info.plist Pods/Target Support Files/XCGLogger/XCGLogger-dummy.m Pods/Target Support Files/XCGLogger/XCGLogger-prefix.pch Pods/Target Support Files/XCGLogger/XCGLogger-umbrella.h Pods/Target Support Files/XCGLogger/XCGLogger.debug.xcconfig Pods/Target Support Files/XCGLogger/XCGLogger.modulemap Pods/Target Support Files/XCGLogger/XCGLogger.release.xcconfig Pods/UserDefaultsStore/LICENSE Pods/UserDefaultsStore/README.md Pods/UserDefaultsStore/Sources/Identifiable.swift Pods/UserDefaultsStore/Sources/SingleUserDefaultsStore.swift Pods/UserDefaultsStore/Sources/UserDefaultsStore.swift Pods/VTMagic/LICENSE Pods/VTMagic/README.md Pods/VTMagic/VTMagic/UIColor+VTMagic.h Pods/VTMagic/VTMagic/UIColor+VTMagic.m Pods/VTMagic/VTMagic/UIScrollView+VTMagic.h Pods/VTMagic/VTMagic/UIScrollView+VTMagic.m Pods/VTMagic/VTMagic/UIViewController+VTMagic.h Pods/VTMagic/VTMagic/UIViewController+VTMagic.m Pods/VTMagic/VTMagic/VTContentView.h Pods/VTMagic/VTMagic/VTContentView.m Pods/VTMagic/VTMagic/VTEnumType.h Pods/VTMagic/VTMagic/VTMagic.h Pods/VTMagic/VTMagic/VTMagicController.h Pods/VTMagic/VTMagic/VTMagicController.m Pods/VTMagic/VTMagic/VTMagicMacros.h Pods/VTMagic/VTMagic/VTMagicProtocol.h Pods/VTMagic/VTMagic/VTMagicView.h Pods/VTMagic/VTMagic/VTMagicView.m Pods/VTMagic/VTMagic/VTMenuBar.h Pods/VTMagic/VTMagic/VTMenuBar.m Pods/WechatOpenSDK-XCFramework/WechatOpenSDK-XCFramework.xcframework/.DS_Store Pods/WechatOpenSDK-XCFramework/WechatOpenSDK-XCFramework.xcframework/Info.plist Pods/WechatOpenSDK-XCFramework/WechatOpenSDK-XCFramework.xcframework/README.txt Pods/WechatOpenSDK-XCFramework/WechatOpenSDK-XCFramework.xcframework/ios-arm64_armv7/Headers/WXApi.h Pods/WechatOpenSDK-XCFramework/WechatOpenSDK-XCFramework.xcframework/ios-arm64_armv7/Headers/WXApiObject.h Pods/WechatOpenSDK-XCFramework/WechatOpenSDK-XCFramework.xcframework/ios-arm64_armv7/Headers/WechatAuthSDK.h Pods/WechatOpenSDK-XCFramework/WechatOpenSDK-XCFramework.xcframework/ios-arm64_armv7/libWechatOpenSDK.a Pods/WechatOpenSDK-XCFramework/WechatOpenSDK-XCFramework.xcframework/ios-arm64_i386_x86_64-simulator/Headers/WXApi.h Pods/WechatOpenSDK-XCFramework/WechatOpenSDK-XCFramework.xcframework/ios-arm64_i386_x86_64-simulator/Headers/WXApiObject.h Pods/WechatOpenSDK-XCFramework/WechatOpenSDK-XCFramework.xcframework/ios-arm64_i386_x86_64-simulator/Headers/WechatAuthSDK.h Pods/WechatOpenSDK-XCFramework/WechatOpenSDK-XCFramework.xcframework/ios-arm64_i386_x86_64-simulator/libWechatOpenSDK.a Pods/XCGLogger/.swift-version Pods/XCGLogger/LICENSE.txt Pods/XCGLogger/README.md Pods/XCGLogger/Sources/XCGLogger/Destinations/AppleSystemLogDestination.swift Pods/XCGLogger/Sources/XCGLogger/Destinations/AutoRotatingFileDestination.swift Pods/XCGLogger/Sources/XCGLogger/Destinations/BaseDestination.swift Pods/XCGLogger/Sources/XCGLogger/Destinations/BaseQueuedDestination.swift Pods/XCGLogger/Sources/XCGLogger/Destinations/ConsoleDestination.swift Pods/XCGLogger/Sources/XCGLogger/Destinations/DestinationProtocol.swift Pods/XCGLogger/Sources/XCGLogger/Destinations/FileDestination.swift Pods/XCGLogger/Sources/XCGLogger/Destinations/TestDestination.swift Pods/XCGLogger/Sources/XCGLogger/Extensions/DispatchQueue+XCGAdditions.swift Pods/XCGLogger/Sources/XCGLogger/Extensions/URL+XCGAdditions.swift Pods/XCGLogger/Sources/XCGLogger/Filters/DevFilter.swift Pods/XCGLogger/Sources/XCGLogger/Filters/FileNameFilter.swift Pods/XCGLogger/Sources/XCGLogger/Filters/FilterProtocol.swift Pods/XCGLogger/Sources/XCGLogger/Filters/TagFilter.swift Pods/XCGLogger/Sources/XCGLogger/Filters/UserInfoFilter.swift Pods/XCGLogger/Sources/XCGLogger/LogFormatters/ANSIColorLogFormatter.swift Pods/XCGLogger/Sources/XCGLogger/LogFormatters/Base64LogFormatter.swift Pods/XCGLogger/Sources/XCGLogger/LogFormatters/LogFormatterProtocol.swift Pods/XCGLogger/Sources/XCGLogger/LogFormatters/PrePostFixLogFormatter.swift Pods/XCGLogger/Sources/XCGLogger/LogFormatters/XcodeColorsLogFormatter.swift Pods/XCGLogger/Sources/XCGLogger/Misc/HelperFunctions.swift Pods/XCGLogger/Sources/XCGLogger/Misc/LogDetails.swift Pods/XCGLogger/Sources/XCGLogger/XCGLogger.swift