diff --git a/IndieMusic/IndieMusic.xcodeproj/project.pbxproj b/IndieMusic/IndieMusic.xcodeproj/project.pbxproj index 9e2882f..052cb3f 100644 --- a/IndieMusic/IndieMusic.xcodeproj/project.pbxproj +++ b/IndieMusic/IndieMusic.xcodeproj/project.pbxproj @@ -9,6 +9,11 @@ /* Begin PBXBuildFile section */ 3C955224FAC6473657A027C3 /* Pods_IndieMusicTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 42CE866330C1741ADEC950B2 /* Pods_IndieMusicTests.framework */; }; 49330FC1492387B5155757F6 /* Pods_IndieMusic_IndieMusicUITests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FA03C869C12FEE5FB3D835C2 /* Pods_IndieMusic_IndieMusicUITests.framework */; }; + 770228E52B54FA3600E07F7A /* ActivityIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 770228E42B54FA3600E07F7A /* ActivityIndicator.swift */; }; + 770228E72B5514F600E07F7A /* InternationalNumberViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 770228E62B5514F600E07F7A /* InternationalNumberViewController.swift */; }; + 770228E92B55169C00E07F7A /* InternationalNumber.swift in Sources */ = {isa = PBXBuildFile; fileRef = 770228E82B55169C00E07F7A /* InternationalNumber.swift */; }; + 770228EB2B55174D00E07F7A /* InternationalNumberViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 770228EA2B55174D00E07F7A /* InternationalNumberViewModel.swift */; }; + 770228ED2B55284F00E07F7A /* Login.swift in Sources */ = {isa = PBXBuildFile; fileRef = 770228EC2B55284F00E07F7A /* Login.swift */; }; 77165D742B464493002AE0A5 /* BarButtonItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77165D732B464493002AE0A5 /* BarButtonItem.swift */; }; 7736FF442B4CECF2008D5DAD /* CommentViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7736FF432B4CECF2008D5DAD /* CommentViewModel.swift */; }; 7736FF462B4CF0E6008D5DAD /* CommentDetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7736FF452B4CF0E6008D5DAD /* CommentDetailViewController.swift */; }; @@ -204,6 +209,11 @@ 3BA421D41748C113CCE36A32 /* Pods_IndieMusic.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_IndieMusic.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 42CE866330C1741ADEC950B2 /* Pods_IndieMusicTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_IndieMusicTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 69F724137B8F9C1F7E51080D /* Pods-IndieMusicTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-IndieMusicTests.release.xcconfig"; path = "Target Support Files/Pods-IndieMusicTests/Pods-IndieMusicTests.release.xcconfig"; sourceTree = ""; }; + 770228E42B54FA3600E07F7A /* ActivityIndicator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActivityIndicator.swift; sourceTree = ""; }; + 770228E62B5514F600E07F7A /* InternationalNumberViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InternationalNumberViewController.swift; sourceTree = ""; }; + 770228E82B55169C00E07F7A /* InternationalNumber.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InternationalNumber.swift; sourceTree = ""; }; + 770228EA2B55174D00E07F7A /* InternationalNumberViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InternationalNumberViewModel.swift; sourceTree = ""; }; + 770228EC2B55284F00E07F7A /* Login.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Login.swift; sourceTree = ""; }; 77165D732B464493002AE0A5 /* BarButtonItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BarButtonItem.swift; sourceTree = ""; }; 7736FF432B4CECF2008D5DAD /* CommentViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommentViewModel.swift; sourceTree = ""; }; 7736FF452B4CF0E6008D5DAD /* CommentDetailViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommentDetailViewController.swift; sourceTree = ""; }; @@ -412,6 +422,14 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 770228E32B54FA3600E07F7A /* RxActivityIndicator */ = { + isa = PBXGroup; + children = ( + 770228E42B54FA3600E07F7A /* ActivityIndicator.swift */, + ); + path = RxActivityIndicator; + sourceTree = ""; + }; 7743999C2AFA18B0006F8EEA /* Player */ = { isa = PBXGroup; children = ( @@ -613,6 +631,8 @@ 77C9B9DE2B4BCE300006C83F /* Message.swift */, 77C9B9E42B4BDB4C0006C83F /* Like.swift */, 77C9B9EA2B4BE7E50006C83F /* Comment.swift */, + 770228E82B55169C00E07F7A /* InternationalNumber.swift */, + 770228EC2B55284F00E07F7A /* Login.swift */, ); path = Models; sourceTree = ""; @@ -646,6 +666,7 @@ 778B8A5A2AF8EAFD0034AFD4 /* Third Party */ = { isa = PBXGroup; children = ( + 770228E32B54FA3600E07F7A /* RxActivityIndicator */, 778B8A602AF8ECC20034AFD4 /* RxErrorTracker */, 7751D3812B45324300F1F2BD /* IndieMusic-Bridging-Header.h */, ); @@ -695,6 +716,8 @@ 77FA0B472B0DFB0E00404C5E /* EditBindPhoneViewController.swift */, 77FA0B432B0DFABD00404C5E /* LoginView.swift */, 7751D3792B450BB100F1F2BD /* AgreementViewController.swift */, + 770228E62B5514F600E07F7A /* InternationalNumberViewController.swift */, + 770228EA2B55174D00E07F7A /* InternationalNumberViewModel.swift */, ); path = Login; sourceTree = ""; @@ -1086,9 +1109,11 @@ 778B8A5F2AF8ECAB0034AFD4 /* Navigator.swift in Sources */, 77FA0B302B0C4D5A00404C5E /* ShareActionController.swift in Sources */, 778B8AA82AF8ED0E0034AFD4 /* Color.swift in Sources */, + 770228EB2B55174D00E07F7A /* InternationalNumberViewModel.swift in Sources */, 778B8AAE2AF8ED0E0034AFD4 /* TextView.swift in Sources */, 778B8A9B2AF8ECFC0034AFD4 /* AuthManager.swift in Sources */, 778B8AAF2AF8ED0E0034AFD4 /* UIColor+IndieMusic.swift in Sources */, + 770228E52B54FA3600E07F7A /* ActivityIndicator.swift in Sources */, 7751D3722B43B9ED00F1F2BD /* Search.swift in Sources */, 774A18072B06045200F56DF1 /* HomeView.swift in Sources */, 77FAF7642B075FEB00FC2CA1 /* JournalDetailViewModel.swift in Sources */, @@ -1119,6 +1144,7 @@ 7751D35E2B42B61100F1F2BD /* PersonInfo.swift in Sources */, 774399A82AFE28BA006F8EEA /* BlurEffectView.swift in Sources */, 778B8A852AF8ECE50034AFD4 /* SearchViewController.swift in Sources */, + 770228E92B55169C00E07F7A /* InternationalNumber.swift in Sources */, 7751D3802B45271600F1F2BD /* BindPhoneViewModel.swift in Sources */, 774A17F52B045F1C00F56DF1 /* Player.swift in Sources */, 7751D3882B45584000F1F2BD /* LaunchADManager.swift in Sources */, @@ -1133,6 +1159,7 @@ 778B8ABF2AF8ED280034AFD4 /* NavigationController.swift in Sources */, 774399AA2AFE3170006F8EEA /* PaddingLabel.swift in Sources */, 77FB7A702B48074600B64030 /* MusicStyleViewModel.swift in Sources */, + 770228ED2B55284F00E07F7A /* Login.swift in Sources */, 7751D3502B42ABBF00F1F2BD /* SettingViewController.swift in Sources */, 77A659DF2B51023200B408C3 /* TestViewController.swift in Sources */, 77FA0B4E2B0EF8C700404C5E /* PhoneCodeViewModel.swift in Sources */, @@ -1196,6 +1223,7 @@ 77FA0B362B0C50D800404C5E /* Share.swift in Sources */, 778B8A622AF8ECC20034AFD4 /* ErrorTracker.swift in Sources */, 774A18102B070A6900F56DF1 /* SongViewCell.swift in Sources */, + 770228E72B5514F600E07F7A /* InternationalNumberViewController.swift in Sources */, 7751D3762B43E8D200F1F2BD /* MusicStyle.swift in Sources */, 77C9B9D72B4BBD780006C83F /* FollowingViewModel.swift in Sources */, 7736FF442B4CECF2008D5DAD /* CommentViewModel.swift in Sources */, diff --git a/IndieMusic/IndieMusic/Application/Navigator.swift b/IndieMusic/IndieMusic/Application/Navigator.swift index f5903c0..ca6507f 100644 --- a/IndieMusic/IndieMusic/Application/Navigator.swift +++ b/IndieMusic/IndieMusic/Application/Navigator.swift @@ -56,7 +56,7 @@ class Navigator { case phoneCode(viewModel: PhoneCodeViewModel) case login(viewModel: LoginViewModel) case bindPhone(viewModel: BindPhoneViewModel) - + case internationalNumber(viewModel: InternationalNumberViewModel) case player(viewModel: PlayerViewModel) case alert @@ -162,7 +162,8 @@ class Navigator { return LoginViewController(viewModel: viewModel, navigator: self) case .bindPhone(viewModel: let viewModel): return BindPhoneViewController(viewModel: viewModel, navigator: self) - + case .internationalNumber(viewModel: let viewModel): + return InternationalNumberViewController.init(viewModel: viewModel, navigator: self) case .player(viewModel: let viewModel): let player = PlayerViewController(viewModel: viewModel, navigator: self) return player diff --git a/IndieMusic/IndieMusic/Common/AlertViewController.swift b/IndieMusic/IndieMusic/Common/AlertViewController.swift index 2474724..430bfb1 100644 --- a/IndieMusic/IndieMusic/Common/AlertViewController.swift +++ b/IndieMusic/IndieMusic/Common/AlertViewController.swift @@ -71,7 +71,24 @@ class AlertViewController: UIViewController { return confirmButton }() + + var confirmClosures: (()->())? + + + override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { + super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) + + + self.modalPresentationStyle = .custom + self.transitioningDelegate = self + + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + override func viewDidLoad() { super.viewDidLoad() @@ -141,10 +158,13 @@ class AlertViewController: UIViewController { } @objc func cancelButtonClicked() { - + self.dismiss(animated: true) } @objc func confirmButtonClick() { + if let confirmClosures = self.confirmClosures { + confirmClosures() + } } } diff --git a/IndieMusic/IndieMusic/Common/ViewController.swift b/IndieMusic/IndieMusic/Common/ViewController.swift index dc7e99e..edcd60f 100644 --- a/IndieMusic/IndieMusic/Common/ViewController.swift +++ b/IndieMusic/IndieMusic/Common/ViewController.swift @@ -58,7 +58,7 @@ class ViewController: UIViewController, Navigatable { }() lazy var closeBarButton: BarButtonItem = { - let view = BarButtonItem(image: UIImage.init(named: ""), + let view = BarButtonItem(image: UIImage.init(named: "nav_close_btn")?.withRenderingMode(.alwaysOriginal), style: .plain, target: self, action: nil) @@ -160,6 +160,7 @@ class ViewController: UIViewController, Navigatable { } func updateUI() { + view.backgroundColor = .white } diff --git a/IndieMusic/IndieMusic/Common/ViewModel.swift b/IndieMusic/IndieMusic/Common/ViewModel.swift index 0b0a36a..43473de 100644 --- a/IndieMusic/IndieMusic/Common/ViewModel.swift +++ b/IndieMusic/IndieMusic/Common/ViewModel.swift @@ -22,10 +22,10 @@ class ViewModel: NSObject { var page = 1 -// let loading = ActivityIndicator() -// let headerLoading = ActivityIndicator() -// let footerLoading = ActivityIndicator() -// + let loading = ActivityIndicator() + let headerLoading = ActivityIndicator() + let footerLoading = ActivityIndicator() + let error = ErrorTracker() let serverError = PublishSubject() let parsedError = PublishSubject() diff --git a/IndieMusic/IndieMusic/Configs/Configs.swift b/IndieMusic/IndieMusic/Configs/Configs.swift index 89f99a2..38231bc 100644 --- a/IndieMusic/IndieMusic/Configs/Configs.swift +++ b/IndieMusic/IndieMusic/Configs/Configs.swift @@ -19,14 +19,14 @@ extension EnvironmentType { case .production: return "https://" case .development: - return "http://" + return "http://testapi.indie.cn:9012" } } } struct Configs { struct App { - static let environmentType: EnvironmentType = .production + static let environmentType: EnvironmentType = .development static let aggrementUrl = environmentType.baseUrl + "" static let universalLink = "" static let bundleIdentifier = "cn.indie.queyue" diff --git a/IndieMusic/IndieMusic/Extensions/UserDefaultKeys.swift b/IndieMusic/IndieMusic/Extensions/UserDefaultKeys.swift index 776e046..87b3dcb 100644 --- a/IndieMusic/IndieMusic/Extensions/UserDefaultKeys.swift +++ b/IndieMusic/IndieMusic/Extensions/UserDefaultKeys.swift @@ -135,7 +135,7 @@ extension UserDefaults { enum defaultKeys: String { case userToken case userID - + case userName } } diff --git a/IndieMusic/IndieMusic/Managers/LibsManager.swift b/IndieMusic/IndieMusic/Managers/LibsManager.swift index 2ef61e9..75ccbbf 100644 --- a/IndieMusic/IndieMusic/Managers/LibsManager.swift +++ b/IndieMusic/IndieMusic/Managers/LibsManager.swift @@ -20,8 +20,6 @@ class LibsManager: NSObject { private override init() { super.init() - setupSwiftDate() - setupWeChatSDK() } func setupSwiftDate() { @@ -32,7 +30,12 @@ class LibsManager: NSObject { func setupLibs(with window: UIWindow? = nil) { let libsManager = LibsManager.shared - + libsManager.setupSwiftDate() + libsManager.setupSVProgressHUD() + +// libsManager.setupDorameonKit() +// libsManager.setupBugly() + libsManager.setupWeChatSDK() } func setupXHLaunchAd() { @@ -40,6 +43,11 @@ class LibsManager: NSObject { } + func setupSVProgressHUD() { + SVProgressHUD.setDefaultStyle(.dark) + SVProgressHUD.setMaximumDismissTimeInterval(1) + } + func setupWeChatSDK() { WXApi.startLog(by: .detail) { (log) in print("微信log:\(log)") diff --git a/IndieMusic/IndieMusic/Models/InternationalNumber.swift b/IndieMusic/IndieMusic/Models/InternationalNumber.swift new file mode 100644 index 0000000..ca7a457 --- /dev/null +++ b/IndieMusic/IndieMusic/Models/InternationalNumber.swift @@ -0,0 +1,28 @@ +// +// InternationalNumber.swift +// IndieMusic +// +// Created by WenLei on 2024/1/15. +// + +import Foundation +import RxDataSources + +struct InternationalNumber { + let country: String + let number: String +} + +struct InternationalNumberSection { + var items: [InternationalNumber] +} + +extension InternationalNumberSection: SectionModelType { + typealias Item = InternationalNumber + + init(original: InternationalNumberSection, items: [Item]) { + self = original + self.items = items + } +} + diff --git a/IndieMusic/IndieMusic/Models/Login.swift b/IndieMusic/IndieMusic/Models/Login.swift new file mode 100644 index 0000000..e3bffb8 --- /dev/null +++ b/IndieMusic/IndieMusic/Models/Login.swift @@ -0,0 +1,17 @@ +// +// Login.swift +// IndieMusic +// +// Created by WenLei on 2024/1/15. +// + +import Foundation + +struct Login: Codable { + let id: String? + let nickname: String? + let sex: Bool? + let avatar: String? + let personality: String? + let token: String? +} diff --git a/IndieMusic/IndieMusic/Modules/Home/HomeViewController.swift b/IndieMusic/IndieMusic/Modules/Home/HomeViewController.swift index 44b18b9..9153aee 100644 --- a/IndieMusic/IndieMusic/Modules/Home/HomeViewController.swift +++ b/IndieMusic/IndieMusic/Modules/Home/HomeViewController.swift @@ -88,7 +88,7 @@ class HomeViewController: TableViewController { guard let viewModel = viewModel as? HomeViewModel else { return } let input = HomeViewModel.Input.init(viewWillAppear: rx.viewWillAppear, - selection: tableView.rx.itemSelected.asDriver()) + selection: tableView.rx.itemSelected.asDriver(), loginButtonTrigger: self.noLoginBottomView.loginButton.rx.tap.asDriver()) let output = viewModel.transform(input: input) let dataSource = HomeViewController.dataSource() @@ -113,6 +113,14 @@ class HomeViewController: TableViewController { + self.noLoginBottomView.loginButton.rx.tap.subscribe { _ in + + let loginViewModel = LoginViewModel.init(provider: viewModel.provider) + self.navigator.show(segue: .login(viewModel: loginViewModel), sender: self) + + }.disposed(by: rx.disposeBag) + + diff --git a/IndieMusic/IndieMusic/Modules/Home/HomeViewModel.swift b/IndieMusic/IndieMusic/Modules/Home/HomeViewModel.swift index 0d4322d..6a64b36 100644 --- a/IndieMusic/IndieMusic/Modules/Home/HomeViewModel.swift +++ b/IndieMusic/IndieMusic/Modules/Home/HomeViewModel.swift @@ -14,6 +14,7 @@ class HomeViewModel: ViewModel, ViewModelType { struct Input { let viewWillAppear: ControlEvent let selection: Driver + let loginButtonTrigger: Driver } diff --git a/IndieMusic/IndieMusic/Modules/Login/InternationalNumberViewController.swift b/IndieMusic/IndieMusic/Modules/Login/InternationalNumberViewController.swift new file mode 100644 index 0000000..f28cc49 --- /dev/null +++ b/IndieMusic/IndieMusic/Modules/Login/InternationalNumberViewController.swift @@ -0,0 +1,153 @@ +// +// InternationalNumberViewController.swift +// IndieMusic +// +// Created by WenLei on 2024/1/15. +// + +import UIKit +import RxSwift +import RxCocoa +import RxDataSources + +class InternationalNumberViewController: TableViewController { +// lazy var closeBarButton: BarButtonItem = { +// let view = BarButtonItem(image: UIImage.init(named: ""), +// style: .plain, +// target: self, +// action: nil) +// return view +// }() + + + override func viewDidLoad() { + super.viewDidLoad() + + // Do any additional setup after loading the view. + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + + self.navigationController?.setNavigationBarHidden(false, animated: true) + + self.navigationItem.leftBarButtonItem = closeBarButton + } + + override func makeUI() { + super.makeUI() + view.backgroundColor = .init(hex: 0xf5f5f5) + navigationItem.title = "选择国家/地区" + + tableView.mj_header = nil + tableView.mj_footer = nil + + tableView.register(InternationalNumberViewCell.self, forCellReuseIdentifier: "InternationalNumberViewCell") + + } + + + override func bindViewModel() { + super.bindViewModel() + + guard let viewModel = viewModel as? InternationalNumberViewModel else { return } + + let input = InternationalNumberViewModel.Input.init(viewWillAppear: rx.viewWillAppear, + modelSelected: tableView.rx.modelSelected(InternationalNumber.self).asDriver()) + + let output = viewModel.transform(input: input) + + let dataSource = InternationalNumberViewController.dataSource() + + output.items.bind(to: tableView.rx.items(dataSource: dataSource)).disposed(by: rx.disposeBag) + + + output.modelSelected.drive { [weak self] sectionItem in + self?.navigator.dismiss(sender: self) + + }.disposed(by: rx.disposeBag) + + + } + + + override func viewDidLayoutSubviews() { + super.viewDidLayoutSubviews() + + + } + +} + + +extension InternationalNumberViewController { + static func dataSource() -> RxTableViewSectionedReloadDataSource { + return RxTableViewSectionedReloadDataSource( + configureCell: { dataSource, tableView, indexPath, item in + let cell: InternationalNumberViewCell = tableView.dequeueReusableCell(withIdentifier: "InternationalNumberViewCell", for: indexPath) as! InternationalNumberViewCell + + cell.internationalNumber = item + + return cell + } + ) + } +} + +class InternationalNumberViewCell: UITableViewCell { + let titleLabel: UILabel = { + let titleLabel = UILabel.init() + titleLabel.font = UIFont.systemFont(ofSize: 17) + titleLabel.textColor = .secondaryText() + + return titleLabel + }() + + let detailLabel: UILabel = { + let detailLabel = UILabel.init() + detailLabel.font = UIFont.systemFont(ofSize: 17) + detailLabel.textColor = .init(hex: 0x000000) + + return detailLabel + }() + + var internationalNumber: InternationalNumber? { + didSet { + guard let internationalNumber = internationalNumber else { return } + + titleLabel.text = internationalNumber.country + detailLabel.text = internationalNumber.number + } + } + + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + + makeUI() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func makeUI() { + contentView.addSubview(titleLabel) + contentView.addSubview(detailLabel) + + + detailLabel.snp.makeConstraints { make in + make.right.equalTo(contentView).offset(-18) + make.top.equalTo(contentView) + make.bottom.equalTo(contentView) + make.height.equalTo(60) + } + + titleLabel.snp.makeConstraints { make in + make.left.equalTo(contentView).offset(18) + make.top.equalTo(contentView) + make.bottom.equalTo(contentView) + make.right.equalTo(detailLabel.snp.left).offset(-10) + + } + } +} diff --git a/IndieMusic/IndieMusic/Modules/Login/InternationalNumberViewModel.swift b/IndieMusic/IndieMusic/Modules/Login/InternationalNumberViewModel.swift new file mode 100644 index 0000000..fd0e61a --- /dev/null +++ b/IndieMusic/IndieMusic/Modules/Login/InternationalNumberViewModel.swift @@ -0,0 +1,63 @@ +// +// InternationalNumberViewModel.swift +// IndieMusic +// +// Created by WenLei on 2024/1/15. +// + +import Foundation +import RxSwift +import RxCocoa + +class InternationalNumberViewModel: ViewModel, ViewModelType { + + struct Input { + let viewWillAppear: ControlEvent + let modelSelected: Driver + + } + + struct Output { + let items: BehaviorRelay<[InternationalNumberSection]> + let modelSelected: Driver + + } + + let items = BehaviorRelay<[InternationalNumberSection]>.init(value: []) + + var number = BehaviorRelay.init(value: "+86") + + + init(number: BehaviorRelay, provider: IndieMusicAPI) { + self.number = number + + super.init(provider: provider) + } + + func transform(input: Input) -> Output { + + input.viewWillAppear.subscribe { (_) in + + }.disposed(by: rx.disposeBag) + + let internationalNumber0 = InternationalNumber.init(country: "中国", number: "+86") + let internationalNumber1 = InternationalNumber.init(country: "美国", number: "+1") + let internationalNumber2 = InternationalNumber.init(country: "日本", number: "+81") + + let internationalNumberSection = InternationalNumberSection.init(items: [internationalNumber0, internationalNumber1, internationalNumber2]) + + + items.accept([internationalNumberSection]) + + + input.modelSelected.drive { numberCode in + self.number.accept(numberCode.number) + }.disposed(by: rx.disposeBag) + + + + return Output.init(items: items, + modelSelected: input.modelSelected) + } + +} diff --git a/IndieMusic/IndieMusic/Modules/Login/LoginView.swift b/IndieMusic/IndieMusic/Modules/Login/LoginView.swift index 7964064..28bdd4c 100644 --- a/IndieMusic/IndieMusic/Modules/Login/LoginView.swift +++ b/IndieMusic/IndieMusic/Modules/Login/LoginView.swift @@ -26,18 +26,19 @@ class PhoneView: UIView { }() - lazy var phoneTextFiele: UITextField = { - let phoneTextFiele = UITextField.init() - phoneTextFiele.font = UIFont.systemFont(ofSize: 17) - phoneTextFiele.placeholder = "输入手机号" - phoneTextFiele.clearButtonMode = .whileEditing - + lazy var phoneTextField: UITextField = { + let phoneTextField = UITextField.init() + phoneTextField.font = UIFont.systemFont(ofSize: 17) + phoneTextField.placeholder = "输入手机号" + phoneTextField.clearButtonMode = .whileEditing + phoneTextField.keyboardType = .numberPad + - if let clearButton = phoneTextFiele.value(forKeyPath: "_clearButton") as? UIButton { + if let clearButton = phoneTextField.value(forKeyPath: "_clearButton") as? UIButton { clearButton.setImage(UIImage(named:"login_clear_btn"), for: .normal) } - return phoneTextFiele + return phoneTextField }() var bottomLineView: UIView = { @@ -63,9 +64,12 @@ class PhoneView: UIView { func makeUI() { layer.cornerRadius = 25 + phoneTextField.delegate = self + + addSubview(codeButton) addSubview(lineView) - addSubview(phoneTextFiele) + addSubview(phoneTextField) addSubview(bottomLineView) } @@ -87,7 +91,7 @@ class PhoneView: UIView { make.centerY.equalTo(codeButton) } - phoneTextFiele.snp.makeConstraints { make in + phoneTextField.snp.makeConstraints { make in make.left.equalTo(lineView.snp.right).offset(15) make.right.equalTo(self).offset(-31) make.top.equalTo(self) @@ -104,6 +108,26 @@ class PhoneView: UIView { } +extension PhoneView: UITextFieldDelegate { + func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { + let currentText = textField.text ?? "" + let prospectiveText = (currentText as NSString).replacingCharacters(in: range, with: string) + + // 允许更改的最大字符数 + let maxLength = 11 + + // 检查是否只包含数字 + let allowedCharacters = CharacterSet.decimalDigits + let characterSet = CharacterSet(charactersIn: string) + let isNumeric = allowedCharacters.isSuperset(of: characterSet) + + // 检查文本长度是否符合限制并且是否为数字 + return isNumeric && prospectiveText.count <= maxLength + } +} + + + class PhoneCodeView: UIView { let textFieldView: UITextField = { let textFieldView = UITextField.init() @@ -216,7 +240,9 @@ class LoginAgreementView: UIView { lazy var announceCheckBoxView: UIButton = { let announceCheckBoxView = UIButton.init() announceCheckBoxView.setImage(UIImage.init(named: "login_radio_off"), for: .normal) - announceCheckBoxView.setImage(UIImage.init(named: "login_agreement_on")?.withRenderingMode(.alwaysTemplate), for: .selected) + announceCheckBoxView.setImage(UIImage.init(named: "login_radio_on"), for: .selected) + +// announceCheckBoxView.setImage(UIImage.init(named: "login_agreement_on")?.withRenderingMode(.alwaysTemplate), for: .selected) announceCheckBoxView.setContentHuggingPriority(.required, for: .horizontal) announceCheckBoxView.setContentCompressionResistancePriority(.required, for: .horizontal) announceCheckBoxView.addTarget(self, action: #selector(announceCheckBoxViewClick), for: .touchUpInside) @@ -264,7 +290,7 @@ class LoginAgreementView: UIView { super.layoutSubviews() announceCheckBoxView.snp.makeConstraints { make in - make.left.equalTo(self).offset(21) + make.left.equalTo(self) make.top.equalTo(self) } @@ -272,13 +298,13 @@ class LoginAgreementView: UIView { make.left.equalTo(announceCheckBoxView.snp.right).offset(9) make.top.equalTo(self).offset(2) make.bottom.equalTo(self) - make.right.equalTo(-21) + make.right.equalTo(self) } } @objc func announceCheckBoxViewClick() { - self.announceCheckBoxView.isSelected.toggle() +// self.announceCheckBoxView.isSelected.toggle() } } diff --git a/IndieMusic/IndieMusic/Modules/Login/LoginViewController.swift b/IndieMusic/IndieMusic/Modules/Login/LoginViewController.swift index 1054bad..ea87122 100644 --- a/IndieMusic/IndieMusic/Modules/Login/LoginViewController.swift +++ b/IndieMusic/IndieMusic/Modules/Login/LoginViewController.swift @@ -6,12 +6,13 @@ // import UIKit +import SVProgressHUD class LoginViewController: ViewController { var backgroundImageView: UIImageView = { let backgroundImageView = UIImageView.init(image: UIImage.init(named: "launch_bakcground")) -// backgroundImageView.contentMode = .bottom + backgroundImageView.contentMode = .scaleAspectFill return backgroundImageView }() @@ -79,6 +80,7 @@ class LoginViewController: ViewController { override func makeUI() { super.makeUI() + view.backgroundColor = .white view.addSubview(backgroundImageView) @@ -100,31 +102,76 @@ class LoginViewController: ViewController { let agreementBoxSelected = loginAgreementView.announceCheckBoxView.rx.tap.map{!self.loginAgreementView.announceCheckBoxView.isSelected}.asDriver(onErrorJustReturn: false) - let input = LoginViewModel.Input.init(codeButtonTrigger: loginButton.rx.tap.asDriver(), + let input = LoginViewModel.Input.init(numberButtonTrigger: phoneView.codeButton.rx.tap.asDriver(), + codeButtonTrigger: loginButton.rx.tap.asDriver(), wechatButtonTrigger: otherLoginView.weChatButton.rx.tap.asDriver(), appleButtonTrigger: otherLoginView.appleButton.rx.tap.asDriver(), - agreementBoxChecked: agreementBoxSelected, notiWecahtResp: NotificationCenter.default.rx.notification(.notiWecahtResp)) let output = viewModel.transform(input: input) - _ = phoneView.phoneTextFiele.rx.textInput <-> viewModel.phone + _ = phoneView.phoneTextField.rx.textInput <-> viewModel.phone - loginButton.rx.tap.subscribe { [weak self] _ in + viewModel.number.subscribe { [weak self] number in + self?.phoneView.codeButton.setTitle(number, for: .normal) + }.disposed(by: rx.disposeBag) + + + self.phoneView.codeButton.rx.tap.subscribe { _ in + let internationalNumberViewModel = InternationalNumberViewModel.init(number: viewModel.number, provider: viewModel.provider) + self.navigator.show(segue: .internationalNumber(viewModel: internationalNumberViewModel), sender: self, transition: .modal) + + }.disposed(by: rx.disposeBag) + + + + + loginAgreementView.announceCheckBoxView.rx.tap + .map { [weak self] in + !(self?.loginAgreementView.announceCheckBoxView.isSelected ?? false) + } + .bind(to: viewModel.isAgree) + .disposed(by: rx.disposeBag) + + viewModel.isAgree + .bind(to: loginAgreementView.announceCheckBoxView.rx.isSelected) + .disposed(by: rx.disposeBag) + + + viewModel.isAgree.subscribe { isAgree in + print("isAgree : \(isAgree)") + }.disposed(by: rx.disposeBag) + + + + + output.isPhoneValid.drive { isPhoneValid in + print("isPhoneValid : \(isPhoneValid)") -// let phoneCodeViewModel = PhoneCodeViewModel.init(phone: "+86 18551843868", provider: viewModel.provider) -// let code = PhoneCodeController.init(viewModel: phoneCodeViewModel, navigator: self.navigator) -// -// self.navigationController?.pushViewController(code, animated: true) + }.disposed(by: rx.disposeBag) + + + + output.toLogin.drive { [weak self] canLogin in + guard canLogin else { return } + let phoneCodeViewModel = PhoneCodeViewModel.init(phone: viewModel.phone.value, provider: viewModel.provider) + self?.navigator.show(segue: .phoneCode(viewModel: phoneCodeViewModel), sender: self) + }.disposed(by: rx.disposeBag) + + + output.errorMessage.drive { errorMessage in + guard errorMessage.count > 0 else { return } - let bindPhoneViewModel = BindPhoneViewModel.init(provider: viewModel.provider) - self?.navigator.show(segue: .bindPhone(viewModel: bindPhoneViewModel), sender: self) - + SVProgressHUD.showText(withStatus: errorMessage) }.disposed(by: rx.disposeBag) + + + + } @@ -132,11 +179,12 @@ class LoginViewController: ViewController { super.viewDidLayoutSubviews() backgroundImageView.snp.makeConstraints { make in - make.bottom.equalTo(view).offset(-BaseDimensions.bottomHeight) + make.bottom.equalTo(view) +// .offset(-BaseDimensions.bottomHeight) make.left.equalTo(view) make.right.equalTo(view) - make.height.equalTo(463) - make.width.equalTo(463) +// make.height.equalTo(463) +// make.width.equalTo(463) } diff --git a/IndieMusic/IndieMusic/Modules/Login/LoginViewModel.swift b/IndieMusic/IndieMusic/Modules/Login/LoginViewModel.swift index 54bdafa..6116a97 100644 --- a/IndieMusic/IndieMusic/Modules/Login/LoginViewModel.swift +++ b/IndieMusic/IndieMusic/Modules/Login/LoginViewModel.swift @@ -13,26 +13,70 @@ import RxViewController class LoginViewModel: ViewModel, ViewModelType { struct Input { + let numberButtonTrigger: Driver let codeButtonTrigger: Driver let wechatButtonTrigger: Driver let appleButtonTrigger: Driver - let agreementBoxChecked: Driver let notiWecahtResp: Observable + } struct Output { + let number: BehaviorRelay + let isPhoneValid: Driver + let toLogin: Driver + let errorMessage: Driver } + let number = BehaviorRelay(value: "+86") let phone = BehaviorRelay(value: "") + let isAgree = BehaviorRelay(value: false) func transform(input: Input) -> Output { + number.subscribe { number in + print("number1 : \(number)") + }.disposed(by: rx.disposeBag) + + - input.codeButtonTrigger.drive { _ in + let phoneValid = phone + .asObservable() + .map { [weak self] in + self?.isValidPattern($0, pattern: "^(1[0-9])\\d{9}$") ?? false + } + .asDriver(onErrorJustReturn: false) + + + let canLogin = Driver.combineLatest(phoneValid, isAgree.asDriver()) { $0 && $1 } + + + let loginTrigger = input.codeButtonTrigger.withLatestFrom(canLogin) + + + let toLogin = loginTrigger.flatMapLatest { [weak self] canLogin -> Driver in + guard let self = self, canLogin else { return Driver.just(false) } - }.disposed(by: rx.disposeBag) + return self.requestSMSData().asDriver(onErrorJustReturn: false) + } + + let errorMessage = input.codeButtonTrigger + .withLatestFrom(Driver.combineLatest(phoneValid, isAgree.asDriver())) + .flatMapLatest { isPhoneValid, isAgreed -> Driver in + if !isPhoneValid { + return Driver.just("电话号码无效") + } + if !isAgreed { + return Driver.just("请同意用户协议") + } + return Driver.just("") + } + + + + input.wechatButtonTrigger.drive { _ in guard WXApi.isWXAppInstalled() else { @@ -55,21 +99,42 @@ class LoginViewModel: ViewModel, ViewModelType { }.disposed(by: rx.disposeBag) - input.agreementBoxChecked.drive { isSelected in - print("agreementBoxChecked: \(isSelected)") - - }.disposed(by: rx.disposeBag) +// input.agreementBoxChecked.drive { isSelected in +// print("agreementBoxChecked: \(isSelected)") +// +// }.disposed(by: rx.disposeBag) - phone.subscribe { phone in - print("phone: \(phone)") - - }.disposed(by: rx.disposeBag) +// phone.subscribe { phone in +// print("phone: \(phone)") +// +// }.disposed(by: rx.disposeBag) - return Output.init() + return Output.init(number: number, + isPhoneValid: phoneValid, toLogin: toLogin, errorMessage: errorMessage) + } + + + + + func requestSMSData() -> Observable { + let mobile = phone.value + return provider.sendsms(mobile: mobile) + .trackActivity(loading) + .trackError(error) + .map({ emptyModel in + guard emptyModel != nil else { return false} + return true + }) + } + + func isValidPattern(_ string: String, pattern: String) -> Bool { + guard let regex = try? NSRegularExpression(pattern: pattern) else { return false } + let range = NSRange(location: 0, length: string.utf16.count) + return regex.firstMatch(in: string, options: [], range: range) != nil } } diff --git a/IndieMusic/IndieMusic/Modules/Login/PhoneCodeController.swift b/IndieMusic/IndieMusic/Modules/Login/PhoneCodeController.swift index 1e07bf1..358b8e2 100644 --- a/IndieMusic/IndieMusic/Modules/Login/PhoneCodeController.swift +++ b/IndieMusic/IndieMusic/Modules/Login/PhoneCodeController.swift @@ -29,7 +29,8 @@ class PhoneCodeController: ViewController { var boxInputView: CodeInputView = { - let boxInputView = CodeInputView.init() + var boxInputView = CodeInputView.init() + boxInputView.resetCodeLength(6, beginEdit: true) let cellProperty = CRBoxInputCellProperty.init() cellProperty.cellCursorColor = UIColor.black cellProperty.cellCursorWidth = 2 @@ -39,7 +40,7 @@ class PhoneCodeController: ViewController { cellProperty.cellFont = UIFont.boldSystemFont(ofSize: 24) cellProperty.cellTextColor = UIColor.black - boxInputView.boxFlowLayout?.itemSize = CGSize.init(width: 70, height: 70) + boxInputView.boxFlowLayout?.itemSize = CGSize.init(width: 40, height: 40) boxInputView.customCellProperty = cellProperty boxInputView.loadAndPrepare(withBeginEdit: true) @@ -66,21 +67,7 @@ class PhoneCodeController: ViewController { boxInputView.textDidChangeblock = { (text, isFinish) in if isFinish { - let agress = AlertViewController.init() - agress.titleLabel.text = "阅读并同意以下协议" - agress.cancelButton.setTitle("取消", for: .normal) - agress.confirmButton.setTitle("已阅读并同意", for: .normal) - - let paragraphStyle = NSMutableParagraphStyle() - paragraphStyle.alignment = .center - paragraphStyle.lineSpacing = 5 - agress.detailTextView.addAttributed(attributedes: [("已阅读并同意 ", [NSAttributedString.Key.foregroundColor: UIColor.secondaryText(), NSAttributedString.Key.paragraphStyle: paragraphStyle]), - ("《用户协议隐私政策》", [NSAttributedString.Key.foregroundColor: UIColor.primaryText(), NSAttributedString.Key.link: URL.init(string: Configs.App.aggrementUrl) ?? "", NSAttributedString.Key.paragraphStyle: paragraphStyle, NSAttributedString.Key.font: UIFont.systemFont(ofSize: 12, weight: .medium)]), - (" 并授权获取本机号码", [NSAttributedString.Key.foregroundColor: UIColor.secondaryText(), NSAttributedString.Key.paragraphStyle: paragraphStyle]) - ]) - - self.present(agress, animated: true) } @@ -124,6 +111,36 @@ class PhoneCodeController: ViewController { output.tipsText.drive(self.tipsLabel.rx.text).disposed(by: rx.disposeBag) + output.toAlertView.subscribe { _ in + let agree = AlertViewController.init() + agree.titleLabel.text = "阅读并同意以下协议" + agree.cancelButton.setTitle("取消", for: .normal) + agree.confirmButton.setTitle("已阅读并同意", for: .normal) + + let paragraphStyle = NSMutableParagraphStyle() + paragraphStyle.alignment = .center + paragraphStyle.lineSpacing = 5 + agree.detailTextView.addAttributed(attributedes: [("已阅读并同意 ", [NSAttributedString.Key.foregroundColor: UIColor.secondaryText(), NSAttributedString.Key.paragraphStyle: paragraphStyle]), + ("《用户协议隐私政策》", [NSAttributedString.Key.foregroundColor: UIColor.primaryText(), NSAttributedString.Key.link: URL.init(string: Configs.App.aggrementUrl) ?? "", NSAttributedString.Key.paragraphStyle: paragraphStyle, NSAttributedString.Key.font: UIFont.systemFont(ofSize: 12, weight: .medium)]), + (" 并授权获取本机号码", [NSAttributedString.Key.foregroundColor: UIColor.secondaryText(), NSAttributedString.Key.paragraphStyle: paragraphStyle]) + ]) + + + agree.confirmClosures = {[weak self] in + self?.dismiss(animated: true) + self?.navigator.pop(sender: self, toRoot: true) + + } + + + self.present(agree, animated: true) + +// self.navigator.pop(sender: self, toRoot: true) + + + }.disposed(by: rx.disposeBag) + + } diff --git a/IndieMusic/IndieMusic/Modules/Login/PhoneCodeViewModel.swift b/IndieMusic/IndieMusic/Modules/Login/PhoneCodeViewModel.swift index 5916d32..7bdfaf9 100644 --- a/IndieMusic/IndieMusic/Modules/Login/PhoneCodeViewModel.swift +++ b/IndieMusic/IndieMusic/Modules/Login/PhoneCodeViewModel.swift @@ -8,6 +8,7 @@ import Foundation import RxSwift import RxCocoa +import SVProgressHUD class PhoneCodeViewModel: ViewModel, ViewModelType { @@ -20,6 +21,7 @@ class PhoneCodeViewModel: ViewModel, ViewModelType { let countdownText: Observable let isButtonEnabled: Driver let tipsText: Driver + let toAlertView: PublishRelay } private var remainingSeconds = 59 @@ -28,9 +30,15 @@ class PhoneCodeViewModel: ViewModel, ViewModelType { +// let mobileCheckCode = BehaviorRelay.init(value: "") + let toAlertView = PublishRelay.init() + + var phone: String + init(phone: String, provider: IndieMusicAPI) { - super.init(provider: provider) self.tipsText = "验证码已发送至 \(phone)" + self.phone = phone + super.init(provider: provider) } @@ -38,10 +46,37 @@ class PhoneCodeViewModel: ViewModel, ViewModelType { let startImmediately = Observable.just(Void()) - let startCountdown = Observable.merge(startImmediately, input.codeButtonTrigger) - .do(onNext: { [weak self] _ in - self?.remainingSeconds = 59 - }) + let startCountdownAfterRequest = input.codeButtonTrigger + .flatMapLatest { [weak self] _ -> Observable in + guard let self = self else { return Observable.just(false) } + return self.requestSMSData() + } + .filter { $0 } // 确保只有在请求成功时才开始倒计时 + .map { _ in Void() } + + let startCountdown = Observable.merge(startImmediately, startCountdownAfterRequest) + .do(onNext: { [weak self] _ in + self?.remainingSeconds = 59 + }) + +// // 倒计时逻辑 +// let timer = startCountdown +// .flatMapLatest { _ in +// Observable.interval(.seconds(1), scheduler +// + + +// +// let startCountdown = Observable.merge(startImmediately, input.codeButtonTrigger) +// .do(onNext: { [weak self] _ in +// self?.remainingSeconds = 59 +// }).flatMapLatest { [weak self] _ -> Observable in +// guard let self = self else { return Observable.just(false) } +// return self.requestSMSData() +// } +// +// + let timer = startCountdown .flatMapLatest { _ in @@ -71,13 +106,52 @@ class PhoneCodeViewModel: ViewModel, ViewModelType { input.boxInputText.subscribe(onNext: { (text, isFinish) in + + guard let text = text, isFinish == true else { return } print("text: \(text) isFinish: \(isFinish) ") + self.requestLoginData(mobileCheckCode: text).subscribe(onNext: { login in + + UserDefaults.AccountInfo.set(login.token, forKey: .userToken) + UserDefaults.AccountInfo.set(login.id, forKey: .userID) + UserDefaults.AccountInfo.set(login.nickname, forKey: .userName) + + self.toAlertView.accept(()) + + }, onError: { error in + if case HTTPServiceError.errorJudge(let err) = error { + SVProgressHUD.showText(withStatus: err.message) + } + + }).disposed(by: self.rx.disposeBag) + + }).disposed(by: rx.disposeBag) return Output(countdownText: countdownText, isButtonEnabled: isButtonEnabled, - tipsText: tipsText) + tipsText: tipsText, + toAlertView: toAlertView) + } + + + func requestLoginData(mobileCheckCode: String) -> Observable { + let mobileCheckCode = mobileCheckCode + let phone = phone + return provider.loginOrRegister(mobile: phone, mobileCheckCode: mobileCheckCode) + .trackActivity(loading) + .trackError(error) + } + + func requestSMSData() -> Observable { + let mobile = phone + return provider.sendsms(mobile: mobile) + .trackActivity(loading) + .trackError(error) + .map({ emptyModel in + guard emptyModel != nil else { return false} + return true + }) } } diff --git a/IndieMusic/IndieMusic/Networking/Api.swift b/IndieMusic/IndieMusic/Networking/Api.swift index a736a43..22ebcd4 100644 --- a/IndieMusic/IndieMusic/Networking/Api.swift +++ b/IndieMusic/IndieMusic/Networking/Api.swift @@ -13,7 +13,11 @@ protocol IndieMusicAPI { func wechatAccessToken(appid: String, secret: String, code: String, grantType: String) -> Single - func login(dic: [String: Any]) -> Single + /// 发送验证码 + func sendsms(mobile: String) -> Single + /// 登录注册 + func loginOrRegister(mobile: String, mobileCheckCode: String) -> Single + } diff --git a/IndieMusic/IndieMusic/Networking/Rest/APIConfig.swift b/IndieMusic/IndieMusic/Networking/Rest/APIConfig.swift index 0a7b8a5..caed2f9 100644 --- a/IndieMusic/IndieMusic/Networking/Rest/APIConfig.swift +++ b/IndieMusic/IndieMusic/Networking/Rest/APIConfig.swift @@ -17,7 +17,9 @@ protocol ProductAPIType { enum APIConfig { case wechatAccessToken([String: Any]) - case login([String: Any]) + case login(String, String, [String: Any]) + + case sendsms(mobil: String, [String: Any]) } extension APIConfig: TargetType { @@ -26,8 +28,11 @@ extension APIConfig: TargetType { case .wechatAccessToken: return "sns/oauth2/access_token" - case .login: - return "" + case .login(let mobile, let mobileCheckCode, _): + return "user/user/appLogin/\(mobile)/\(mobileCheckCode)" + + case .sendsms(let mobil, _): + return "user/user/sendsms/\(mobil)" } } @@ -35,7 +40,7 @@ extension APIConfig: TargetType { switch self { case .wechatAccessToken: return .get - case .login: + case .sendsms, .login: return .post } } @@ -46,6 +51,9 @@ extension APIConfig: TargetType { return URLEncoding.default case .login: return JSONEncoding.default + + case .sendsms: + return JSONEncoding.default } } @@ -56,7 +64,7 @@ extension APIConfig: TargetType { case .wechatAccessToken: return .requestPlain - case .login(let dic): + case .login(_, _, let dic), .sendsms(_, let dic): parameters = dic return .requestParameters(parameters: parameters, encoding: parameterEncoding) @@ -70,7 +78,7 @@ extension APIConfig: TargetType { switch self { case .wechatAccessToken: return URL(string: Configs.App.wechatURL)! - case .login: + case .login, .sendsms: return URL(string: Configs.App.environmentType.baseUrl)! } @@ -78,6 +86,9 @@ extension APIConfig: TargetType { var headers : [String : String]? { switch self { + case .wechatAccessToken: + return nil + default: return nil } diff --git a/IndieMusic/IndieMusic/Networking/Rest/ErrorResponse.swift b/IndieMusic/IndieMusic/Networking/Rest/ErrorResponse.swift index 45ff6d7..c03290f 100644 --- a/IndieMusic/IndieMusic/Networking/Rest/ErrorResponse.swift +++ b/IndieMusic/IndieMusic/Networking/Rest/ErrorResponse.swift @@ -11,16 +11,16 @@ struct ErrorResponse: Codable { var message: String? var code: Int? - var errors: [ErrorModel] = [] +// var errors: [ErrorModel] = [] var documentationUrl: String? init() {} - func detail() -> String { - return errors.map { $0.message ?? "" } - .joined(separator: "\n") - } +// func detail() -> String { +// return errors.map { $0.message ?? "" } +// .joined(separator: "\n") +// } } struct ErrorModel: Codable { diff --git a/IndieMusic/IndieMusic/Networking/Rest/Networking.swift b/IndieMusic/IndieMusic/Networking/Rest/Networking.swift index 0a52708..bc8a62b 100644 --- a/IndieMusic/IndieMusic/Networking/Rest/Networking.swift +++ b/IndieMusic/IndieMusic/Networking/Rest/Networking.swift @@ -35,27 +35,47 @@ extension ObservableType where Element == Response { } catch { } - - //这里的情况是,只有code为0的时候,数据data才有东西 - if let errodModel = try? JSONDecoder().decode(ErrorResponse.self, from: response.data) { - print("ssss") + + do { + let errodModel = try JSONDecoder().decode(ErrorResponse.self, from: response.data) + print("ssss") if let msg = errodModel.message, msg == "处理失败,未知异常" { - print("接口错误:\(String(describing: response.request?.url))") - } - + print("接口错误:\(String(describing: response.request?.url))") + } + switch errodModel.code { - case 0: + case 200: return Observable.just(response) case 410: return Observable.error(HTTPServiceError.tokenInvalid(err: errodModel)) default: return Observable.error(HTTPServiceError.errorJudge(err: errodModel)) } - } else if response.statusCode == 410 { - - } else if response.statusCode == 200 { - return Observable.just(response) + } catch { + print("解码错误: \(error)") + } + + //这里的情况是,只有code为0的时候,数据data才有东西 +// if let errodModel = try? JSONDecoder().decode(ErrorResponse.self, from: response.data) { +// print("ssss") +// if let msg = errodModel.message, msg == "处理失败,未知异常" { +// print("接口错误:\(String(describing: response.request?.url))") +// } +// +// switch errodModel.code { +// case 0: +// return Observable.just(response) +// case 410: +// return Observable.error(HTTPServiceError.tokenInvalid(err: errodModel)) +// default: +// return Observable.error(HTTPServiceError.errorJudge(err: errodModel)) +// } +// } else if response.statusCode == 410 { +// +// } else if response.statusCode == 200 { +// return Observable.just(response) +// } return Observable.error(MoyaError.jsonMapping(response)) diff --git a/IndieMusic/IndieMusic/Networking/Rest/RestApi.swift b/IndieMusic/IndieMusic/Networking/Rest/RestApi.swift index 8cc9c7b..c4e0771 100644 --- a/IndieMusic/IndieMusic/Networking/Rest/RestApi.swift +++ b/IndieMusic/IndieMusic/Networking/Rest/RestApi.swift @@ -21,11 +21,11 @@ enum ApiError: Error { } } - var description: String { - switch self { - case .serverError(let response): return response.detail() - } - } +// var description: String { +// switch self { +// case .serverError(let response): return response.detail() +// } +// } } class RestApi: IndieMusicAPI { @@ -73,12 +73,18 @@ extension RestApi { } - func login(dic: [String: Any]) -> Single { - return requestObject(.login(dic), with: "data", type: EmptyModel.self) - + + + func loginOrRegister(mobile: String, mobileCheckCode: String) -> Single { + return requestObject(.login(mobile, mobileCheckCode, ["": ""]), with: "data", type: Login.self) } + func sendsms(mobile: String) -> Single { + let dic = ["": ""] + return requestObject(.sendsms(mobil: mobile, dic), with: "data", type: EmptyModel.self) + + } } diff --git a/IndieMusic/IndieMusic/Resources/Assets.xcassets/launch/launch_bakcground.imageset/Contents.json b/IndieMusic/IndieMusic/Resources/Assets.xcassets/launch/launch_bakcground.imageset/Contents.json index 3fb1d3d..aad5115 100644 --- a/IndieMusic/IndieMusic/Resources/Assets.xcassets/launch/launch_bakcground.imageset/Contents.json +++ b/IndieMusic/IndieMusic/Resources/Assets.xcassets/launch/launch_bakcground.imageset/Contents.json @@ -5,7 +5,6 @@ "scale" : "1x" }, { - "filename" : "launch_bakcground@2x.png", "idiom" : "universal", "scale" : "2x" }, diff --git a/IndieMusic/IndieMusic/Resources/Assets.xcassets/launch/launch_bakcground.imageset/launch_bakcground@2x.png b/IndieMusic/IndieMusic/Resources/Assets.xcassets/launch/launch_bakcground.imageset/launch_bakcground@2x.png deleted file mode 100644 index 200b0ec..0000000 Binary files a/IndieMusic/IndieMusic/Resources/Assets.xcassets/launch/launch_bakcground.imageset/launch_bakcground@2x.png and /dev/null differ diff --git a/IndieMusic/IndieMusic/Resources/Assets.xcassets/launch/launch_bakcground.imageset/launch_bakcground@3x.png b/IndieMusic/IndieMusic/Resources/Assets.xcassets/launch/launch_bakcground.imageset/launch_bakcground@3x.png index d90c620..b9b6a70 100644 Binary files a/IndieMusic/IndieMusic/Resources/Assets.xcassets/launch/launch_bakcground.imageset/launch_bakcground@3x.png and b/IndieMusic/IndieMusic/Resources/Assets.xcassets/launch/launch_bakcground.imageset/launch_bakcground@3x.png differ diff --git a/IndieMusic/IndieMusic/Resources/Assets.xcassets/login/login_radio_on.imageset/Contents.json b/IndieMusic/IndieMusic/Resources/Assets.xcassets/login/login_radio_on.imageset/Contents.json new file mode 100644 index 0000000..0d8371b --- /dev/null +++ b/IndieMusic/IndieMusic/Resources/Assets.xcassets/login/login_radio_on.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "login_radio_on.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/IndieMusic/IndieMusic/Resources/Assets.xcassets/login/login_radio_on.imageset/login_radio_on.svg b/IndieMusic/IndieMusic/Resources/Assets.xcassets/login/login_radio_on.imageset/login_radio_on.svg new file mode 100644 index 0000000..033a21e --- /dev/null +++ b/IndieMusic/IndieMusic/Resources/Assets.xcassets/login/login_radio_on.imageset/login_radio_on.svg @@ -0,0 +1,4 @@ + + + + diff --git a/IndieMusic/IndieMusic/Resources/Assets.xcassets/login/login_wechat_btn.imageset/login_wechat_btn.svg b/IndieMusic/IndieMusic/Resources/Assets.xcassets/login/login_wechat_btn.imageset/login_wechat_btn.svg index 4cf73db..13fd202 100644 --- a/IndieMusic/IndieMusic/Resources/Assets.xcassets/login/login_wechat_btn.imageset/login_wechat_btn.svg +++ b/IndieMusic/IndieMusic/Resources/Assets.xcassets/login/login_wechat_btn.imageset/login_wechat_btn.svg @@ -1,11 +1,4 @@ - - - - - - - - - - + + + diff --git a/IndieMusic/IndieMusic/Resources/Assets.xcassets/nav/Contents.json b/IndieMusic/IndieMusic/Resources/Assets.xcassets/nav/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/IndieMusic/IndieMusic/Resources/Assets.xcassets/nav/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/IndieMusic/IndieMusic/Resources/Assets.xcassets/nav_back_btn.imageset/Contents.json b/IndieMusic/IndieMusic/Resources/Assets.xcassets/nav/nav_back_btn.imageset/Contents.json similarity index 100% rename from IndieMusic/IndieMusic/Resources/Assets.xcassets/nav_back_btn.imageset/Contents.json rename to IndieMusic/IndieMusic/Resources/Assets.xcassets/nav/nav_back_btn.imageset/Contents.json diff --git a/IndieMusic/IndieMusic/Resources/Assets.xcassets/nav_back_btn.imageset/nav_back_btn.svg b/IndieMusic/IndieMusic/Resources/Assets.xcassets/nav/nav_back_btn.imageset/nav_back_btn.svg similarity index 100% rename from IndieMusic/IndieMusic/Resources/Assets.xcassets/nav_back_btn.imageset/nav_back_btn.svg rename to IndieMusic/IndieMusic/Resources/Assets.xcassets/nav/nav_back_btn.imageset/nav_back_btn.svg diff --git a/IndieMusic/IndieMusic/Resources/Assets.xcassets/nav/nav_close_btn.imageset/Contents.json b/IndieMusic/IndieMusic/Resources/Assets.xcassets/nav/nav_close_btn.imageset/Contents.json new file mode 100644 index 0000000..ad6fb1a --- /dev/null +++ b/IndieMusic/IndieMusic/Resources/Assets.xcassets/nav/nav_close_btn.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "nav_close_btn.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/IndieMusic/IndieMusic/Resources/Assets.xcassets/nav/nav_close_btn.imageset/nav_close_btn.svg b/IndieMusic/IndieMusic/Resources/Assets.xcassets/nav/nav_close_btn.imageset/nav_close_btn.svg new file mode 100644 index 0000000..ea3969f --- /dev/null +++ b/IndieMusic/IndieMusic/Resources/Assets.xcassets/nav/nav_close_btn.imageset/nav_close_btn.svg @@ -0,0 +1,4 @@ + + + + diff --git a/IndieMusic/IndieMusic/Third Party/RxActivityIndicator/ActivityIndicator.swift b/IndieMusic/IndieMusic/Third Party/RxActivityIndicator/ActivityIndicator.swift new file mode 100644 index 0000000..a9a24c0 --- /dev/null +++ b/IndieMusic/IndieMusic/Third Party/RxActivityIndicator/ActivityIndicator.swift @@ -0,0 +1,81 @@ +// +// ActivityIndicator.swift +// RxExample +// +// Created by Krunoslav Zaher on 10/18/15. +// Copyright © 2015 Krunoslav Zaher. All rights reserved. +// + +#if !RX_NO_MODULE +import RxSwift +import RxCocoa +#endif + +private struct ActivityToken: ObservableConvertibleType, Disposable { + private let _source: Observable + private let _dispose: Cancelable + + init(source: Observable, disposeAction: @escaping () -> Void) { + _source = source + _dispose = Disposables.create(with: disposeAction) + } + + func dispose() { + _dispose.dispose() + } + + func asObservable() -> Observable { + return _source + } +} + +/** + Enables monitoring of sequence computation. + If there is at least one sequence computation in progress, `true` will be sent. + When all activities complete `false` will be sent. + */ +public class ActivityIndicator: SharedSequenceConvertibleType { + public typealias Element = Bool + public typealias SharingStrategy = DriverSharingStrategy + + private let _lock = NSRecursiveLock() + private let _relay = BehaviorRelay(value: 0) + private let _loading: SharedSequence + + public init() { + _loading = _relay.asDriver() + .map { $0 > 0 } + .distinctUntilChanged() + } + + fileprivate func trackActivityOfObservable(_ source: Source) -> Observable { + return Observable.using({ () -> ActivityToken in + self.increment() + return ActivityToken(source: source.asObservable(), disposeAction: self.decrement) + }, observableFactory: { value in + return value.asObservable() + }) + } + + private func increment() { + _lock.lock() + _relay.accept(_relay.value + 1) + _lock.unlock() + } + + private func decrement() { + _lock.lock() + _relay.accept(_relay.value - 1) + _lock.unlock() + } + + public func asSharedSequence() -> SharedSequence { + return _loading + } +} + +extension ObservableConvertibleType { + public func trackActivity(_ activityIndicator: ActivityIndicator) -> Observable { + return activityIndicator.trackActivityOfObservable(self) + } +} diff --git a/IndieMusic/Podfile b/IndieMusic/Podfile index e6ff18f..55365b2 100644 --- a/IndieMusic/Podfile +++ b/IndieMusic/Podfile @@ -20,9 +20,10 @@ target 'IndieMusic' do pod 'SwiftDate' pod 'XHLaunchAd' pod 'DZNEmptyDataSet' - pod 'SVProgressHUD', :git => 'https://github.com/SVProgressHUD/SVProgressHUD.git' + pod 'SVProgressHUD',:git => 'https://github.com/Fidetro/SVProgressHUD.git' pod 'AttributedString' pod 'ESTMusicIndicator' + pod 'IQKeyboardManagerSwift' pod 'NSObject+Rx'