Commit 5c74fd19 ilCode

登录注册UI

1 个父辈 e9c9b92a
......@@ -16,6 +16,8 @@
5E66B2D92C1C295100590452 /* UIButton+Ext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E66B2D82C1C295100590452 /* UIButton+Ext.swift */; };
5E6CF00A2C12FAD600BF3CF5 /* TargetController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E6CF0092C12FAD600BF3CF5 /* TargetController.swift */; };
5E6CF0222C1305DD00BF3CF5 /* AoleiSports.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 5E6CF0202C1305DD00BF3CF5 /* AoleiSports.xcdatamodeld */; };
5E9165D82C1D3F45004A3C5E /* UserProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E9165D72C1D3F45004A3C5E /* UserProvider.swift */; };
5E9165DA2C1D9A09004A3C5E /* ServerApi.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E9165D92C1D9A09004A3C5E /* ServerApi.swift */; };
5E93B4882C1A7DA200CD6536 /* BaseTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E93B4872C1A7DA200CD6536 /* BaseTableViewCell.swift */; };
5E93B48A2C1A935F00CD6536 /* PrivacyController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E93B4892C1A935F00CD6536 /* PrivacyController.swift */; };
5E93B48C2C1A985F00CD6536 /* LoginController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E93B48B2C1A985F00CD6536 /* LoginController.swift */; };
......@@ -76,6 +78,8 @@
5E66B2D82C1C295100590452 /* UIButton+Ext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIButton+Ext.swift"; sourceTree = "<group>"; };
5E6CF0092C12FAD600BF3CF5 /* TargetController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TargetController.swift; sourceTree = "<group>"; };
5E6CF0212C1305DD00BF3CF5 /* AoleiSports.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = AoleiSports.xcdatamodel; sourceTree = "<group>"; };
5E9165D72C1D3F45004A3C5E /* UserProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserProvider.swift; sourceTree = "<group>"; };
5E9165D92C1D9A09004A3C5E /* ServerApi.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerApi.swift; sourceTree = "<group>"; };
5E93B4872C1A7DA200CD6536 /* BaseTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseTableViewCell.swift; sourceTree = "<group>"; };
5E93B4892C1A935F00CD6536 /* PrivacyController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrivacyController.swift; sourceTree = "<group>"; };
5E93B48B2C1A985F00CD6536 /* LoginController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginController.swift; sourceTree = "<group>"; };
......@@ -164,6 +168,7 @@
5E93B48B2C1A985F00CD6536 /* LoginController.swift */,
5E93B48D2C1A98BE00CD6536 /* RegisterController.swift */,
5E93B48F2C1A9BDC00CD6536 /* LoginTools.swift */,
5E9165D72C1D3F45004A3C5E /* UserProvider.swift */,
);
path = User;
sourceTree = "<group>";
......@@ -291,6 +296,7 @@
5E9A1A4D2C0F1A2E00321AC5 /* FontTools.swift */,
5EA670612C104D2800CEEA01 /* LoggerTools.swift */,
5EC03E492C1155530068A5CB /* NetworkTools.swift */,
5E9165D92C1D9A09004A3C5E /* ServerApi.swift */,
5EC03E4D2C1155720068A5CB /* CacheTools.swift */,
5EC03E4F2C118A420068A5CB /* AppInfo.swift */,
5E1E66AF2C194337009339F0 /* LayoutTools.swift */,
......@@ -460,8 +466,10 @@
5E93B4902C1A9BDC00CD6536 /* LoginTools.swift in Sources */,
5EC03E532C118FA00068A5CB /* ScoreProvider.swift in Sources */,
5E9A1A522C0F205A00321AC5 /* LeagueController.swift in Sources */,
5E9165D82C1D3F45004A3C5E /* UserProvider.swift in Sources */,
5E9A1A282C0EF51600321AC5 /* GKNavigationBarConfigure.swift in Sources */,
5EC03E4A2C1155530068A5CB /* NetworkTools.swift in Sources */,
5E9165DA2C1D9A09004A3C5E /* ServerApi.swift in Sources */,
5E9A1A342C0EF51600321AC5 /* UIScrollView+GKExtension.swift in Sources */,
5E9A1A402C0F04CC00321AC5 /* HomeController.swift in Sources */,
5E1E66AE2C192F7F009339F0 /* ProfileView.swift in Sources */,
......
......@@ -36,21 +36,21 @@ class HomeController: BaseController {
btn.setImage(R.image.match_setting(), for: .normal)
btn.contentHorizontalAlignment = .right
// 方法1
// btn.rx.tap.do(onNext: {
// print("点击了设置按钮")
// })
// .map { !self.filterBtn.isHidden }
// .bind(to: filterBtn.rx.isHidden)
// .disposed(by: disposeBag)
// btn.rx.tap.do(onNext: {
// print("点击了设置按钮")
// })
// .map { !self.filterBtn.isHidden }
// .bind(to: filterBtn.rx.isHidden)
// .disposed(by: disposeBag)
// 方法2:
btn.rx.tap.subscribe(onNext: {
self.isHiddenRelay.accept(!self.isHiddenRelay.value)
print("点击了设置按钮")
// ScoreViewModel.getNmMatchList { result in
//
// }
// ScoreViewModel.getNmMatchList { result in
//
// }
self.provider?.getNmMatchList { result in
switch result {
......@@ -68,7 +68,7 @@ class HomeController: BaseController {
override func viewDidLoad() {
super.viewDidLoad()
gk_navTitle = "赛事比分"
gk_navLeftBarButtonItem = UIBarButtonItem(customView: filterBtn)
gk_navRightBarButtonItem = setItem
......
......@@ -228,7 +228,7 @@ class LoginController: BaseController {
view.addSubview(registerButton)
view.addSubview(thirdPartyLoginLabel)
view.addSubview(buttonStackView)
verificationCodeButton.snp.makeConstraints { make in
make.top.equalToSuperview().offset(kNavBarH + 10)
make.left.equalToSuperview().offset(20)
......@@ -356,13 +356,6 @@ class LoginController: BaseController {
}
self.view.layoutIfNeeded()
}
// 更新协议选择框的约束
if isVerificationCodeLogin {
agreementCheckBox.topAnchor.constraint(equalTo: verificationCodeField.bottomAnchor, constant: 20).isActive = true
} else {
agreementCheckBox.topAnchor.constraint(equalTo: passwordField.bottomAnchor, constant: 20).isActive = true
}
}
@objc private func getCodeTapped() {
......@@ -370,6 +363,22 @@ class LoginController: BaseController {
view.makeToast("请输入手机号", position: .center)
return
}
view.makeToastActivity(.center)
UserProvider<LoginOrRegisterTarget>.mobileCodeRequest(mobile: phoneNumber) { logicResult in
self.view.hideToastActivity()
if case .failure(let err) = logicResult {
self.view.makeToast(err.localizedDescription, position: .center)
return
}
self.dealCountDown()
}
}
@objc private func dealCountDown() {
getCodeButton.isEnabled = false
remainingSeconds = 60
......@@ -399,12 +408,11 @@ class LoginController: BaseController {
@objc private func loginTapped() {
dealReadPrivacy()
}
@objc private func registerTapped() {
// 注册逻辑
print("注册")
navigationController?.pushViewController(RegisterController(), animated: true)
}
@objc private func appleLoginTapped() {
......@@ -414,7 +422,7 @@ class LoginController: BaseController {
@objc private func guestLoginTapped() {
dealReadPrivacy()
}
private func dealReadPrivacy() {
......@@ -425,6 +433,7 @@ class LoginController: BaseController {
view.makeToastActivity(.center)
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
self.view.hideToastActivity()
self.view.makeToast("加载成功")
}
}
}
......@@ -8,9 +8,143 @@
import UIKit
class PrivacyController: BaseController {
let userAgreement = """
更新日期:2024年06月01日
欢迎使用傲雷体育应用程序。本隐私政策解释了我们如何收集、使用、共享和保护您的个人信息。
提示:我们的产品/服务中包含的或链接至第三方提供的信息与服务 (包括任何第三方应用、网站、产品、服务等) ,这些信息与服务由第三方负责运营,您在使用该信息或服务时,将适用第三方另行向您说明的个人信息收集、使用规则。
信息收集和使用
账号注册
在您注册账号时,我们将会收集您的账户昵称、密码、手机号码,若您以其他方式关注我们,我们会向关联第三方请求您的个人信息,对于我要求但第三方无法提供的个人信息,我们仍可以要求您提供,如果您拒绝提供这些信息,将影响您使用我们的相关服务或功能。
账号注销
您可通过客户端“系统信息”-“客服微信”联系客服申请账号注销,我们核实后将在15日内予以处理;账号注销后,我们将对已经收集的个人信息进行删除或匿名化处理。
获取设备GPS数据:
经过用户授权后,App将调用您设备的网络服务状态,获取您的移动数据访问APP以更新实时赛事情报。
会员说明:
您在访问傲雷体育APP的免费功能时,无需注册即可使用。但访问付费功能模块时则需要根据个人意愿自愿注册及支付,以方便后续订单的跟踪和售后。本APP付费会员换算详情:
足球周会员 288牛币=288元
足球月会员 888牛币=888元
足球3月会员 2188牛币=2188元
海外预测周会员 98牛币=98元
海外预测月会员 98牛币=98元
海外预测3月会员 1188牛币=1188元
牛牛解球周会员 98牛币=98元
牛牛解球月会员 388牛币=388元
牛牛解球3月会员 1188牛币=1188元
订单与支付服务
(1)当您在服务中订阅查看付费服务时,我系统为您生成购买该服务的订单。过程中,您至少需提供个人信息:登陆账号、手机号、支付状态信息。如果您拒绝提供此类信息,我们将无法完成相关付费服务;
(2)为向您展示账号的交易信息并保障交易安全,我们会收集您在使用付费服务过程中产生的信息(例如您购买的服务信息、具体订单号、订单创建时间、交易金额)用于向您展示及便于您对订单进行管理。
APP订单使用嵌入第三方SDK
com.alipay,支付宝支付SDK,用于用户支付数据分析及相关售后服务,不收集用户信息。其隐私政策参见https://render.alipay.com/p/c/k2cx0tg8
个人信息的保护
我们设立信息保护责任部门,针对个人信息收集、使用、共享、委托处理等开展个人信息安全影响评估。同时建立相关内控制度,对可能接触到您的信息的工作人员采取最小够用授权原则;对工作人员处理您的信息的行为进行系统监控,不断对工作人员培训相关法律法规及隐私安全准则和安全意识强化宣导,并每年组织相关工作人员参加安全培训。另外,我们的相应网络/系统通过了国家网络安全等级保护的测评。
我们的网络服务采取了传输层安全协议等加密技术,通过https等方式提供浏览服务,确保用户数据在传输过程中的安全。采取加密技术对用户个人信息进行加密保存,并通过访问限制技术进行隔离。在个人信息使用时,例如个人信息展示、个人信息关联计算,我们采用包括内容替换、隐藏等多种数据脱敏技术增强个人信息在使用中的安全性。
保护未成年人的个人信息
如您为14周岁(含)未成年人,建议请您的父母或监护人阅读本政策,并在征得您父母或监护人同意的前提下使用我们的服务或向我们提供您的信息。对于经父母或监护人同意而收集您的信息的情况,我们只会在受到法律允许、父母或监护人明确同意或者保护您的权益所必要的情况下使用或公开披露此信息。如您的监护人不同意您按照本政策使用我们的服务或向我们提供信息,请您立即终止使用我们的服务并及时通知我们,以便我们采取相应的措施。
按照中国相关的法律、法规、标准,以及其他国家、地区的通行做法,我们保障您行使以下权利:
(一)改变您授权同意的范围
后续更新的赛事比分赛果情报功能需要获取基本的移动网络服务才能得以完成。对于是否同意使用网络数据服务,您可以随时设置更改同意。
(二)约束信息系统自动决策
在某些业务功能中,我们可能仅依据信息系统、算法等在内的非人工自动决策机制作出决定。如果这些决定显著影响您的合法权益,您有权要求我们作出解释,我们也将提供适当的救济方式。
(三)响应您的上述请求
如您对我们的内容有任何建议和投诉,可以发送邮件到service@hongdan310.com:对于您合理的请求,我们原则上不收取费用,但对多次重复、超出合理限度的请求,我们将视情况收取一定成本费用。对于那些无端重复、需要过多技术手段(例如,需要开发新系统或从根本上改变现行惯例)、给他人合法权益带来风险或者非常不切实际的请求,我们可能会予以拒绝。也请您理解,出于安全保障的考虑、相关法律法规的要求或技术上的限制,对于您的某些请求我们可能无法做出响应,
例如以下情形:
1、与用户信息控制者履行法律法规规定的义务相关的;
2、与国家安全、国防安全直接相关的;
3、与公共安全、公共卫生、重大公共利益直接相关的;
4、与犯罪侦查、起诉、审判和执行判决等直接相关的;
5、存在主观恶意或滥用权利的;
7、导致主体组织的合法权益受到严重损害的;涉及商业秘密的。
变更:
版本更新时我们可能会更新本隐私政策,我们会在本页面上发布新的隐私政策。请定期查看此页面以了解我们隐私政策的更新内容。
联系我们:
如果您对我们的隐私政策有任何疑问或意见,请联系我们:service@hongdan310.com
"""
private lazy var agreementView: UITextView = {
let textView = UITextView()
textView.text = userAgreement
textView.font = kFontSize(15)
textView.textColor = kMainTitleColor
textView.isEditable = false
textView.contentInsetAdjustmentBehavior = .never
textView.textContainerInset = UIEdgeInsets(top: 0, left: 10, bottom: 0, right: 10)
return textView
}()
override func viewDidLoad() {
super.viewDidLoad()
gk_navTitle = "隐私协议"
view.backgroundColor = kWhite
view.addSubview(agreementView)
agreementView.snp.makeConstraints { make in
make.top.equalToSuperview().offset(kNavBarH)
make.left.right.bottom.equalToSuperview()
}
}
}
......@@ -32,10 +32,7 @@ class ProfileController: BaseController {
extension ProfileController: ProfileViewDelegate {
func tapHeader() {
// navigationController?.pushViewController(UpdateProfileController(), animated: true)
// let loginVC = LoginController()
// loginVC.modalPresentationStyle = .fullScreen
// present(loginVC, animated: true)
LoginTools.toLogin(target: self)
}
......
......@@ -8,9 +8,252 @@
import UIKit
class RegisterController: BaseController {
private var countdownTimer: Timer?
private var remainingSeconds = 60
private lazy var phoneNumberField: UITextField = {
let textField = UITextField()
textField.borderStyle = .roundedRect
textField.keyboardType = .numberPad
textField.clearButtonMode = .whileEditing
textField.font = kFontSize(16)
textField.textColor = kMainTitleColor
let attributes: [NSAttributedString.Key: Any] = [
.foregroundColor: kSubTitleColor,
.font: kFontSize(14)
]
textField.attributedPlaceholder = NSAttributedString(string: "手机号", attributes: attributes)
return textField
}()
private lazy var verificationCodeField: UITextField = {
let textField = UITextField()
textField.borderStyle = .roundedRect
textField.keyboardType = .numberPad
textField.clearButtonMode = .whileEditing
textField.font = kFontSize(16)
textField.textColor = kMainTitleColor
let attributes: [NSAttributedString.Key: Any] = [
.foregroundColor: kSubTitleColor,
.font: kFontSize(14)
]
textField.attributedPlaceholder = NSAttributedString(string: "验证码", attributes: attributes)
return textField
}()
private lazy var getCodeButton: UIButton = {
let button = UIButton()
button.setTitle("获取验证码", for: .normal)
button.setTitleColor(.systemBlue, for: .normal)
button.setTitleColor(kSubTitleColor, for: .disabled)
button.titleLabel?.font = kFontSize(16)
button.addTarget(self, action: #selector(getCodeTapped), for: .touchUpInside)
return button
}()
private lazy var passwordField: UITextField = {
let textField = UITextField()
textField.borderStyle = .roundedRect
textField.clearButtonMode = .whileEditing
textField.font = kFontSize(16)
textField.textColor = kMainTitleColor
textField.isSecureTextEntry = true
let attributes: [NSAttributedString.Key: Any] = [
.foregroundColor: kSubTitleColor,
.font: kFontSize(14)
]
textField.attributedPlaceholder = NSAttributedString(string: "密码", attributes: attributes)
return textField
}()
private lazy var confirmPwdField: UITextField = {
let textField = UITextField()
textField.borderStyle = .roundedRect
textField.clearButtonMode = .whileEditing
textField.font = kFontSize(16)
textField.textColor = kMainTitleColor
textField.isSecureTextEntry = true
let attributes: [NSAttributedString.Key: Any] = [
.foregroundColor: kSubTitleColor,
.font: kFontSize(14)
]
textField.attributedPlaceholder = NSAttributedString(string: "确认密码", attributes: attributes)
return textField
}()
private lazy var agreementCheckBox: UIButton = {
let button = UIButton()
button.setImage(R.image.icon_un_select()?.resize(to: CGSize(width: 12, height: 12)), for: .normal)
button.setImage(R.image.icon_selected()?.resize(to: CGSize(width: 12, height: 12)), for: .selected)
button.setTitle("阅读并同意", for: .normal)
button.titleLabel?.font = kFontSize(12)
button.setTitleColor(kMainTitleColor, for: .normal)
button.imageEdgeInsets = UIEdgeInsets(top: 0, left: -5, bottom: 0, right: 0)
button.addTarget(self, action: #selector(agreementTapped), for: .touchUpInside)
return button
}()
private lazy var privacyBtn: UIButton = {
let button = UIButton()
button.titleLabel?.font = kFontSize(12)
button.setTitle("《用户隐私协议》", for: .normal)
button.setTitleColor(kMasterColor, for: .normal)
button.addTarget(self, action: #selector(privacyTapped), for: .touchUpInside)
return button
}()
private lazy var registerButton: UIButton = {
let button = UIButton()
button.setTitle("注册", for: .normal)
button.titleLabel?.font = kBoldFontSize(16)
button.backgroundColor = kMasterColor
button.setTitleColor(kWhite, for: .normal)
button.corners(radius: 6)
button.addTarget(self, action: #selector(registerTapped), for: .touchUpInside)
return button
}()
override func viewDidLoad() {
super.viewDidLoad()
gk_navTitle = "注册账号"
gk_navLineHidden = true
view.backgroundColor = kWhite
view.addSubview(phoneNumberField)
view.addSubview(verificationCodeField)
view.addSubview(getCodeButton)
view.addSubview(passwordField)
view.addSubview(confirmPwdField)
view.addSubview(agreementCheckBox)
view.addSubview(privacyBtn)
view.addSubview(registerButton)
layoutUI()
}
}
extension RegisterController {
private func layoutUI() {
phoneNumberField.snp.makeConstraints { make in
make.top.equalToSuperview().offset(kNavBarH + 20)
make.left.equalToSuperview().offset(20)
make.right.equalToSuperview().offset(-20)
make.height.equalTo(35)
}
verificationCodeField.snp.makeConstraints { make in
make.top.equalTo(phoneNumberField.snp.bottom).offset(20)
make.left.equalToSuperview().offset(20)
make.width.equalTo(200)
}
getCodeButton.snp.makeConstraints { make in
make.centerY.equalTo(verificationCodeField)
make.left.equalTo(verificationCodeField.snp.right).offset(20)
}
passwordField.snp.makeConstraints { make in
make.top.equalTo(verificationCodeField.snp.bottom).offset(20)
make.left.equalToSuperview().offset(20)
make.right.equalToSuperview().offset(-20)
make.height.equalTo(35)
}
confirmPwdField.snp.makeConstraints { make in
make.top.equalTo(passwordField.snp.bottom).offset(20)
make.left.equalToSuperview().offset(20)
make.right.equalToSuperview().offset(-20)
make.height.equalTo(35)
}
agreementCheckBox.snp.makeConstraints { make in
make.top.equalTo(confirmPwdField.snp.bottom).offset(20)
make.left.equalToSuperview().offset(20)
make.height.equalTo(30)
}
privacyBtn.snp.makeConstraints { make in
make.top.equalTo(agreementCheckBox)
make.left.equalTo(agreementCheckBox.snp.right)
make.height.equalTo(30)
}
registerButton.snp.makeConstraints { make in
make.top.equalTo(privacyBtn.snp.bottom).offset(20)
make.left.equalToSuperview().offset(20)
make.right.equalToSuperview().offset(-20)
make.height.equalTo(40)
}
}
@objc private func getCodeTapped() {
guard let phoneNumber = phoneNumberField.text?.trimmingCharacters(in: .whitespacesAndNewlines), !phoneNumber.isEmpty else {
view.makeToast("请输入手机号", position: .center)
return
}
getCodeButton.isEnabled = false
remainingSeconds = 60
getCodeButton.setTitle("获取验证码(\(remainingSeconds)s)", for: .normal)
countdownTimer?.invalidate()
countdownTimer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(updateCountdown), userInfo: nil, repeats: true)
}
@objc private func updateCountdown() {
remainingSeconds -= 1
getCodeButton.setTitle("获取验证码(\(remainingSeconds)s)", for: .normal)
if remainingSeconds <= 0 {
countdownTimer?.invalidate()
getCodeButton.setTitle("获取验证码", for: .normal)
getCodeButton.isEnabled = true
}
}
@objc private func agreementTapped() {
agreementCheckBox.isSelected.toggle()
}
@objc private func privacyTapped() {
navigationController?.pushViewController(PrivacyController(), animated: true)
}
@objc private func registerTapped() {
if !agreementCheckBox.isSelected {
agreementCheckBox.shakeAnimation()
return
}
guard let phoneNumber = phoneNumberField.text?.trimmingCharacters(in: .whitespacesAndNewlines), !phoneNumber.isEmpty else {
view.makeToast("请输入手机号", position: .center)
return
}
guard let veriCode = verificationCodeField.text?.trimmingCharacters(in: .whitespacesAndNewlines), !veriCode.isEmpty else {
view.makeToast("请输入验证码", position: .center)
return
}
guard let password = passwordField.text, !phoneNumber.isEmpty else {
view.makeToast("请输入密码", position: .center)
return
}
guard let confirmPwd = confirmPwdField.text, !confirmPwd.isEmpty else {
view.makeToast("请输入确认密码", position: .center)
return
}
view.makeToastActivity(.center)
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
self.view.hideToastActivity()
self.view.makeToast("注册成功")
self.dismiss(animated: true)
}
}
private func dealReadPrivacy() {
if !agreementCheckBox.isSelected {
agreementCheckBox.shakeAnimation()
return
}
}
}
//
// UserProvider.swift
// AoleiSports
//
// Created by ilCode on 2024/6/15.
//
import UIKit
enum LoginOrRegisterTarget: TargetType {
case mobileCode(mobile: String) // 手机验证码
var task: Moya.Task {
switch self {
case .mobileCode(let mobile):
let parameters = ["query": """
query{
\(kMobileCode)(mobile:"\(mobile)",randstr:"\(Date().timeIntervalSince1970)",type:7)
}
"""]
return .requestParameters(parameters: parameters, encoding: JSONEncoding.default)
}
}
}
class UserProvider<Target: TargetType>: BaseMoyaProvider<Target> {
// 手机验证码请求
static func mobileCodeRequest(mobile: String, completion: @escaping (Result<Bool, AsError>) -> Void) {
let target: LoginOrRegisterTarget = .mobileCode(mobile: mobile)
moyaPost(api: kMobileCode, target: target) { midResult in
DDLogInfo("midResult:\(midResult)")
switch midResult {
case .success(_):
completion(.success(true))
case .failure(let err):
completion(.failure(err))
}
}
}
}
......@@ -50,7 +50,7 @@ private func TABBAR_INDICATOR_HEIGHT() -> CGFloat {
}
/// 获取window
private func kWindow() -> UIWindow? {
func kWindow() -> UIWindow? {
if #available(iOS 13.0, *) {
let winScene = UIApplication.shared.connectedScenes.first as? UIWindowScene
return winScene?.windows.first
......
......@@ -7,23 +7,50 @@
import Foundation
let SERVER_URL = "https://m.ydn.com/"
let API_BASE_URL = SERVER_URL + "graphql"
/*
网络请求框架方案:
Moya(Base层) + SwiftyJSON(Base层) + ObjectMapper(Mid层,中间业务层)
Base层:BaseMoyaPlugin、BaseMoyaProvider、AsError
Mid层:xxxTarget、xxxProvider
*/
// 共用
extension TargetType {
var baseURL: URL {
return URL(string: API_BASE_URL)!
}
//MARK: - AsError
enum AsError: Swift.Error {
case netErr(String) // 网络错误
case argsErr(String) // 参数错误(接口请求参数)
case resDataErr(String) // 返回数据错误(返回数据缺少相关字段或字段类型不匹配)
case parseErr(String) // 解析错误
case busErr(String) // 业务错误
var path: String {
return ""
var localizedDescription: String {
switch self {
case .netErr(let message):
return "network err: \(message)"
case .argsErr(let message):
return "args err: \(message)"
case .resDataErr(let message):
return "response data err: \(message)"
case .parseErr(let message):
return "parse err: \(message)"
case .busErr(let message):
return "busErr err: \(message)"
}
}
}
//MARK: - TargetType
extension TargetType {
var baseURL: URL { URL(string: API_BASE_URL)! }
var method: Moya.Method {
.post
}
var path: String { "" }
/*
默认所有请求方式为post,
如果需要设置其他请求方式,在Mid层:xxxTarget中重写
*/
var method: Moya.Method { .post }
// 设置头部信息
var headers: [String : String]? {
// [
// "appversion": AppInfo.getAppInfo(),
......@@ -33,16 +60,16 @@ extension TargetType {
}
}
// 自定义Plugin
//MARK: - 自定义Plugin
final class BaseMoyaPlugin: PluginType {
var vc: UIViewController
var vc: UIViewController?
private var spinner: NVActivityIndicatorView!
init(vc: UIViewController, showLoading: Bool = true) {
init(vc: UIViewController?, showLoading: Bool = true) {
self.vc = vc
if showLoading {
if let tmpVC = self.vc, showLoading {
spinner = NVActivityIndicatorView(frame: CGRect(x: 0, y: 0, width: 60, height: 55), type: .ballSpinFadeLoader, color: kMasterColor)
spinner.center = self.vc.view.center
spinner.center = tmpVC.view.center
}
}
......@@ -57,9 +84,9 @@ final class BaseMoyaPlugin: PluginType {
}
func willSend(_ request: any RequestType, target: any TargetType) {
if let spinner = spinner {
DispatchQueue.main.async { [weak self] in
self?.vc.view.addSubview(spinner)
if let tmpVC = self.vc, let spinner = spinner {
DispatchQueue.main.async {
tmpVC.view.addSubview(spinner)
spinner.startAnimating()
}
}
......@@ -79,22 +106,65 @@ final class BaseMoyaPlugin: PluginType {
// 弹出并显示错误信息
let message = error.errorDescription ?? "未知错误"
let alertViewController = UIAlertController(title: "请求失败",
message: "\(message)",
preferredStyle: .alert)
alertViewController.addAction(UIAlertAction(title: "确定", style: .default,
handler: nil))
vc.present(alertViewController, animated: true)
if let tmpVC = self.vc {
tmpVC.view.makeToast(message)
} else {
kWindow()?.makeToast(message)
}
}
}
// 自定义MoyaProvider
//MARK: - 自定义MoyaProvider
class BaseMoyaProvider<Target: TargetType>: MoyaProvider<Target> {
let disposeBag = DisposeBag()
var basePlugin: BaseMoyaPlugin
init(vc: UIViewController, showLoading: Bool = true) {
init(vc: UIViewController? = nil, showLoading: Bool = true) {
self.basePlugin = BaseMoyaPlugin(vc: vc, showLoading: showLoading)
super.init(plugins: [self.basePlugin])
}
static func moyaPost(api: String, target: TargetType, completion: @escaping (Result<[String: JSON], AsError>) -> Void) {
let provider = BaseMoyaProvider<Target>()
provider.request(target as! Target) { result in
switch result {
case let .success(response):
let json = JSON(response.data)
// 解析errors
if let errors = json["errors"].array,
let rootErr = errors.first,
let message = rootErr["message"].string, !message.isEmpty {
DDLogError("rootErr: \(message)")
completion(.failure(AsError.argsErr(message)))
return
}
// 解析data
guard let data = json["data"].dictionary else {
DDLogError("data数据错误")
completion(.failure(AsError.resDataErr("data数据错误")))
return
}
// 解析api
guard let apiDic = data["\(api)"]?.dictionary else {
DDLogError("\(api)数据错误")
completion(.failure(AsError.resDataErr("\(api)数据错误")))
return
}
// 解析业务Error
if let error = apiDic["Error"]?.string, !error.isEmpty {
DDLogError("api bus err: \(error)")
completion(.failure(AsError.busErr(error)))
} else {
DDLogInfo("成功:\(apiDic)")
completion(.success(apiDic))
}
case let .failure(error):
DDLogError("errCode: \(error.errorCode)")
// 应该根据errCode细分错误类型
completion(.failure(AsError.netErr(error.localizedDescription)))
}
}
}
}
//
// ServerApi.swift
// AoleiSports
//
// Created by ilCode on 2024/6/15.
//
import Foundation
let SERVER_URL = "https://m.ydn.com/"
let API_BASE_URL = SERVER_URL + "graphql"
// 获取验证码
let kMobileCode = "getMobileCode"

1013 字节 | 宽: | 高:

2.4 KB | 宽: | 高:

AoleiSports/Src/Vendors/GKNavigationBarSwift/GKNavigationBarSwift.bundle/btn_back_black@2x.png
AoleiSports/Src/Vendors/GKNavigationBarSwift/GKNavigationBarSwift.bundle/btn_back_black@2x.png
AoleiSports/Src/Vendors/GKNavigationBarSwift/GKNavigationBarSwift.bundle/btn_back_black@2x.png
AoleiSports/Src/Vendors/GKNavigationBarSwift/GKNavigationBarSwift.bundle/btn_back_black@2x.png
  • 两方对比
  • 交换覆盖
  • 透明覆盖

1.2 KB | 宽: | 高:

897 字节 | 宽: | 高:

AoleiSports/Src/Vendors/GKNavigationBarSwift/GKNavigationBarSwift.bundle/btn_back_black@3x.png
AoleiSports/Src/Vendors/GKNavigationBarSwift/GKNavigationBarSwift.bundle/btn_back_black@3x.png
AoleiSports/Src/Vendors/GKNavigationBarSwift/GKNavigationBarSwift.bundle/btn_back_black@3x.png
AoleiSports/Src/Vendors/GKNavigationBarSwift/GKNavigationBarSwift.bundle/btn_back_black@3x.png
  • 两方对比
  • 交换覆盖
  • 透明覆盖
支持 Markdown 格式
你添加了 0 到此讨论。请谨慎行事。
Finish editing this message first!