Commit 4b21ff31 ilCode

情报接口调试

1 个父辈 5c74fd19
正在显示 40 个修改的文件 包含 764 行增加27 行删除
......@@ -12,6 +12,11 @@
5E1E66B02C194337009339F0 /* LayoutTools.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E1E66AF2C194337009339F0 /* LayoutTools.swift */; };
5E1E66B22C1990EE009339F0 /* ProfileModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E1E66B12C1990EE009339F0 /* ProfileModel.swift */; };
5E1E66B42C19AAC7009339F0 /* UpdateProfileController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E1E66B32C19AAC7009339F0 /* UpdateProfileController.swift */; };
5E47C9732C1FCB87002EA39E /* InfoController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E47C9722C1FCB87002EA39E /* InfoController.swift */; };
5E47C9752C1FD35E002EA39E /* InfoProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E47C9742C1FD35E002EA39E /* InfoProvider.swift */; };
5E47C9772C1FD7F6002EA39E /* InfoModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E47C9762C1FD7F6002EA39E /* InfoModel.swift */; };
5E47C9792C20157C002EA39E /* InfoDetailController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E47C9782C20157C002EA39E /* InfoDetailController.swift */; };
5E47C97D2C202ED3002EA39E /* AsEmptyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E47C97C2C202ED3002EA39E /* AsEmptyView.swift */; };
5E66B2D72C1BE2ED00590452 /* UIImage+Ext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E66B2D62C1BE2ED00590452 /* UIImage+Ext.swift */; };
5E66B2D92C1C295100590452 /* UIButton+Ext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E66B2D82C1C295100590452 /* UIButton+Ext.swift */; };
5E6CF00A2C12FAD600BF3CF5 /* TargetController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E6CF0092C12FAD600BF3CF5 /* TargetController.swift */; };
......@@ -74,6 +79,11 @@
5E1E66AF2C194337009339F0 /* LayoutTools.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LayoutTools.swift; sourceTree = "<group>"; };
5E1E66B12C1990EE009339F0 /* ProfileModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileModel.swift; sourceTree = "<group>"; };
5E1E66B32C19AAC7009339F0 /* UpdateProfileController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateProfileController.swift; sourceTree = "<group>"; };
5E47C9722C1FCB87002EA39E /* InfoController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InfoController.swift; sourceTree = "<group>"; };
5E47C9742C1FD35E002EA39E /* InfoProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InfoProvider.swift; sourceTree = "<group>"; };
5E47C9762C1FD7F6002EA39E /* InfoModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InfoModel.swift; sourceTree = "<group>"; };
5E47C9782C20157C002EA39E /* InfoDetailController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InfoDetailController.swift; sourceTree = "<group>"; };
5E47C97C2C202ED3002EA39E /* AsEmptyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AsEmptyView.swift; sourceTree = "<group>"; };
5E66B2D62C1BE2ED00590452 /* UIImage+Ext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImage+Ext.swift"; sourceTree = "<group>"; };
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>"; };
......@@ -173,6 +183,37 @@
path = User;
sourceTree = "<group>";
};
5E47C9712C1FCB22002EA39E /* Information */ = {
isa = PBXGroup;
children = (
5E47C9722C1FCB87002EA39E /* InfoController.swift */,
5E47C9782C20157C002EA39E /* InfoDetailController.swift */,
5E47C9742C1FD35E002EA39E /* InfoProvider.swift */,
5E47C9762C1FD7F6002EA39E /* InfoModel.swift */,
);
path = Information;
sourceTree = "<group>";
};
5E47C97A2C20186D002EA39E /* Score */ = {
isa = PBXGroup;
children = (
5EC03E522C118FA00068A5CB /* ScoreProvider.swift */,
5E9A1A3F2C0F04CC00321AC5 /* HomeController.swift */,
5E9A1A512C0F205A00321AC5 /* LeagueController.swift */,
);
path = Score;
sourceTree = "<group>";
};
5E47C97B2C2018AD002EA39E /* Root */ = {
isa = PBXGroup;
children = (
5E9A19EB2C0EAC0200321AC5 /* AppDelegate.swift */,
5E9A1A3D2C0F043100321AC5 /* RootController.swift */,
5E9A19F62C0EAC0300321AC5 /* LaunchScreen.storyboard */,
);
path = Root;
sourceTree = "<group>";
};
5E9A19DF2C0EAC0200321AC5 = {
isa = PBXGroup;
children = (
......@@ -230,12 +271,9 @@
5E9A1A3A2C0F00A000321AC5 /* Utils */,
5E9A1A372C0EFF0C00321AC5 /* Ext */,
5E9A1A122C0EF4E200321AC5 /* Vendors */,
5E9A19EB2C0EAC0200321AC5 /* AppDelegate.swift */,
5E9A19F62C0EAC0300321AC5 /* LaunchScreen.storyboard */,
5E9A1A3D2C0F043100321AC5 /* RootController.swift */,
5E9A1A3F2C0F04CC00321AC5 /* HomeController.swift */,
5E9A1A512C0F205A00321AC5 /* LeagueController.swift */,
5EC03E522C118FA00068A5CB /* ScoreProvider.swift */,
5E47C97B2C2018AD002EA39E /* Root */,
5E47C97A2C20186D002EA39E /* Score */,
5E47C9712C1FCB22002EA39E /* Information */,
5E1E66AA2C192F39009339F0 /* User */,
);
path = Src;
......@@ -310,6 +348,7 @@
5E9A1A472C0F0DC400321AC5 /* BaseController.swift */,
5E6CF0092C12FAD600BF3CF5 /* TargetController.swift */,
5E93B4872C1A7DA200CD6536 /* BaseTableViewCell.swift */,
5E47C97C2C202ED3002EA39E /* AsEmptyView.swift */,
);
path = Base;
sourceTree = "<group>";
......@@ -472,6 +511,8 @@
5E9165DA2C1D9A09004A3C5E /* ServerApi.swift in Sources */,
5E9A1A342C0EF51600321AC5 /* UIScrollView+GKExtension.swift in Sources */,
5E9A1A402C0F04CC00321AC5 /* HomeController.swift in Sources */,
5E47C97D2C202ED3002EA39E /* AsEmptyView.swift in Sources */,
5E47C9792C20157C002EA39E /* InfoDetailController.swift in Sources */,
5E1E66AE2C192F7F009339F0 /* ProfileView.swift in Sources */,
5E9A1A2B2C0EF51600321AC5 /* GKNavigationBarSwift.swift in Sources */,
5E9A1A2C2C0EF51600321AC5 /* GKNavigationInteractiveTransition.swift in Sources */,
......@@ -482,6 +523,7 @@
5E6CF00A2C12FAD600BF3CF5 /* TargetController.swift in Sources */,
5E9A1A4C2C0F144200321AC5 /* ColorTools.swift in Sources */,
5E9A1A482C0F0DC400321AC5 /* BaseController.swift in Sources */,
5E47C9732C1FCB87002EA39E /* InfoController.swift in Sources */,
5E9A1A422C0F04DB00321AC5 /* ProfileController.swift in Sources */,
5E9A1A352C0EF51600321AC5 /* UIViewController+GKExtension.swift in Sources */,
5E93B48C2C1A985F00CD6536 /* LoginController.swift in Sources */,
......@@ -489,10 +531,12 @@
5E9A1A2F2C0EF51600321AC5 /* UIBarButtonItem+GKExtension.swift in Sources */,
5EA670622C104D2800CEEA01 /* LoggerTools.swift in Sources */,
5E9A1A312C0EF51600321AC5 /* UINavigationController+GKExtension.swift in Sources */,
5E47C9772C1FD7F6002EA39E /* InfoModel.swift in Sources */,
5E93B48E2C1A98BE00CD6536 /* RegisterController.swift in Sources */,
5E6CF0222C1305DD00BF3CF5 /* AoleiSports.xcdatamodeld in Sources */,
5E1E66B02C194337009339F0 /* LayoutTools.swift in Sources */,
5E93B4922C1AA0D200CD6536 /* UIViewController+Ext.swift in Sources */,
5E47C9752C1FD35E002EA39E /* InfoProvider.swift in Sources */,
5E9A1A3C2C0F00AD00321AC5 /* ImportTools.swift in Sources */,
5E9A19EC2C0EAC0200321AC5 /* AppDelegate.swift in Sources */,
5EC03E502C118A420068A5CB /* AppInfo.swift in Sources */,
......
{
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"filename" : "net_err.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "net_err@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "net_err@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"filename" : "no_data.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "no_data@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "no_data@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"filename" : "ssqb.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "ssqb@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "ssqb@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"filename" : "ssqb_selected.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "ssqb_selected@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "ssqb_selected@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
//
// AsEmptyView.swift
// AoleiSports
//
// Created by ilCode on 2024/6/17.
//
import UIKit
// 缺省页面类型
enum EmptyType {
case netErr // 网络问题
case noData // 无数据
}
// 缺省页
class AsEmptyView: UIView {
var refreshAction: (() -> Void)?
private lazy var placeHolder: UIImageView = {
let iv = UIImageView()
return iv
}()
private lazy var netLab: UILabel = {
let lab = UILabel()
lab.text = "请检查您的网络设置或刷新重试"
lab.textColor = kSubTitleColor
lab.textAlignment = .center
lab.font = kFontSize(14)
return lab
}()
private lazy var noDataLab: UILabel = {
let lab = UILabel()
lab.text = "暂无数据"
lab.textColor = kSubTitleColor
lab.textAlignment = .center
lab.font = kFontSize(14)
return lab
}()
private lazy var refreshBtn: UIButton = {
let btn = UIButton()
btn.backgroundColor = kMasterColor
btn.setTitle("刷新", for: .normal)
btn.setTitleColor(kWhite, for: .normal)
btn.titleLabel?.font = kFontSize(14)
btn.corners(radius: 15)
btn.addTarget(self, action: #selector(refreshButtonTapped), for: .touchUpInside)
return btn
}()
private lazy var netStackView: UIStackView = {
let stackView = UIStackView(arrangedSubviews: [netLab, refreshBtn])
stackView.axis = .vertical
stackView.alignment = .center
stackView.distribution = .equalSpacing
stackView.spacing = 15
return stackView
}()
private lazy var noDataStackView: UIStackView = {
let stackView = UIStackView(arrangedSubviews: [noDataLab])
stackView.axis = .vertical
stackView.alignment = .center
stackView.distribution = .equalSpacing
stackView.spacing = 20
return stackView
}()
private lazy var containerStackView: UIStackView = {
let stackView = UIStackView(arrangedSubviews: [placeHolder, netStackView, noDataStackView])
stackView.axis = .vertical
stackView.alignment = .center
stackView.distribution = .equalSpacing
stackView.spacing = 15
return stackView
}()
override init(frame: CGRect) {
super.init(frame: frame)
isHidden = true
addSubview(containerStackView)
containerStackView.snp.makeConstraints { make in
make.center.equalToSuperview()
}
refreshBtn.snp.makeConstraints { make in
make.size.equalTo(CGSize(width: 60, height: 30))
}
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
extension AsEmptyView {
func show(type: EmptyType, message: String?) {
isHidden = false
if type == .netErr {
placeHolder.image = R.image.net_err()
netStackView.isHidden = false
noDataStackView.isHidden = true
netLab.text = message ?? "请检查您的网络设置或刷新重试"
}
else if type == .noData {
placeHolder.image = R.image.no_data()
netStackView.isHidden = true
noDataStackView.isHidden = false
}
}
func dismiss() {
isHidden = true
}
@objc private func refreshButtonTapped() {
refreshAction?()
}
}
......@@ -11,9 +11,36 @@ import UIKit
class BaseController: UIViewController {
let disposeBag = DisposeBag()
lazy var emptyView: AsEmptyView = {
let empty = AsEmptyView(frame: .zero)
empty.refreshAction = { [weak self] in
self?.handleRefresh()
}
return empty
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = kMainBgColor
view.addSubview(emptyView)
emptyView.snp.makeConstraints { make in
make.edges.equalToSuperview()
}
}
}
extension BaseController {
func showEmpty(type: EmptyType, message: String? = nil) {
emptyView.show(type: type, message: message)
}
func dismissEmpty() {
emptyView.dismiss()
}
@objc func handleRefresh() {
}
}
//
// InfoController.swift
// AoleiSports
//
// Created by ilCode on 2024/6/17.
//
import UIKit
// 赛事情报页面
class InfoController: BaseController {
var provider: InfoProvider<InfoTarget>?
private lazy var infoListView: UITableView = {
let tv = UITableView(frame: CGRect(x: 0, y: kNavBarH, width: kScreenW, height: kScreenH - kNavBarH - kTabBarH), style: .plain)
tv.delegate = self
tv.dataSource = self
tv.rowHeight = 100
tv.backgroundColor = kMainBgColor
tv.register(MatchInfoCell.self, forCellReuseIdentifier: "MatchInfoCell")
return tv
}()
override func viewDidLoad() {
super.viewDidLoad()
gk_navTitle = "赛事情报"
provider = InfoProvider(vc: self, showErr: false)
view.addSubview(infoListView)
provider?.infoDataList
.asObservable()
.subscribe(onNext: { infoModels in
print("InfoDataList changed: \(infoModels)")
self.infoListView.reloadData()
if infoModels.isEmpty {
self.infoListView.isHidden = true
self.showEmpty(type: .noData)
} else {
self.dismissEmpty()
self.infoListView.isHidden = false
}
})
.disposed(by: disposeBag)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
handleRefresh()
}
override func handleRefresh() {
super.handleRefresh()
dismissEmpty()
provider?.liveScoreRequest(completion: { logicResult in
if case .failure(let err) = logicResult {
if case AsError.netErr(let message) = err {
self.infoListView.isHidden = true
self.showEmpty(type: .netErr, message: message)
} else {
self.view.makeToast(err.localizedDescription, position: .center)
}
return
}
})
}
}
extension InfoController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return provider?.infoDataList.value.count ?? 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let model = provider?.infoDataList.value[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: "MatchInfoCell", for: indexPath) as! MatchInfoCell
cell.model = model
return cell
}
}
class MatchInfoCell: BaseTableViewCell {
private lazy var cornerBgView: UIView = {
let view = UIView()
view.backgroundColor = kWhite
view.corners(radius: 10)
return view
}()
private lazy var teamLab: UILabel = {
let lab = UILabel()
lab.textColor = kMainTitleColor
lab.font = kFontSize(16)
return lab
}()
var model: InfoModel? {
didSet {
teamLab.text = (model?.hostName ?? "") + "VS" + (model?.guestName ?? "")
}
}
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
contentView.addSubview(cornerBgView)
cornerBgView.addSubview(teamLab)
cornerBgView.snp.makeConstraints { make in
make.left.top.equalToSuperview().offset(5)
make.right.equalToSuperview().offset(-5)
make.bottom.equalToSuperview()
}
teamLab.snp.makeConstraints { make in
make.left.top.equalToSuperview().offset(5)
}
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
//
// InfoDetailController.swift
// AoleiSports
//
// Created by ilCode on 2024/6/17.
//
import UIKit
// 情报详情页面
class InfoDetailController: BaseController {
var model: InfoModel? {
didSet {
gk_navTitle = (model?.hostName ?? "") + "VS" + (model?.guestName ?? "")
}
}
override func viewDidLoad() {
super.viewDidLoad()
}
}
//
// InfoModel.swift
// AoleiSports
//
// Created by ilCode on 2024/6/17.
//
import Foundation
class InfoModel: Mappable {
var matchId: Int?
var infoId: Int?
var issueName: String?
var matchTime: String?
var matchNumber: String?
var hostName: String?
var hostRed: Int?
var hostYellow: Int?
var guestName: String?
var guestRed: Int?
var guestYellow: Int?
var gameName: String?
var qcBf: String?
var bcBf: String?
var matchState: String?
var matchRound: String?
var sportsInfoCount: Int?
var jcInfo: String?
required init?(map: ObjectMapper.Map) {
}
func mapping(map: ObjectMapper.Map) {
matchId <- map["Id"]
infoId <- map["InfoId"]
issueName <- map["IssueName"]
matchTime <- map["MatchTime"]
matchNumber <- map["MatchNumber"]
hostName <- map["HostName"]
hostRed <- map["HostRed"]
hostYellow <- map["HostYellow"]
guestName <- map["GuestName"]
guestRed <- map["GuestRed"]
guestYellow <- map["GuestYellow"]
gameName <- map["GameName"]
qcBf <- map["QcBf"]
bcBf <- map["BcBf"]
bcBf <- map["BcBf"]
matchState <- map["MatchState"]
matchRound <- map["MatchRound"]
sportsInfoCount <- map["SportsInfoCount"]
jcInfo <- map["JcInfo"]
}
}
//
// InfoProvider.swift
// AoleiSports
//
// Created by ilCode on 2024/6/17.
//
import UIKit
enum InfoTarget: TargetType {
case info
var task: Moya.Task {
let parameters = ["query": """
query{
\(APIs.kLiveScore)(lotIds: "72,45",order:[["MatchTime","desc"]], limit:100) {
count
matchs {
Id
InfoId
IssueName
MatchTime
MatchNumber
HostName
HostRed
HostYellow
GuestName
GuestRed
GuestYellow
GameName
QcBf
BcBf
HostTeam {
LogoFullPath
}
GuestTeam {
LogoFullPath
}
MatchState
MatchRound
PreTotalScore
Temperature
SportsInjury
SportsInfoCount
Sportsdt {
SportsdtMatchId
}
JcInfo
YiqiuMatchId
SportteryMatchId
}
}
}
"""]
return .requestParameters(parameters: parameters, encoding: JSONEncoding.default)
}
}
class InfoProvider<Target: TargetType>: BaseMoyaProvider<Target> {
var infoDataList = BehaviorRelay<[InfoModel]>(value: [])
func liveScoreRequest(completion: @escaping (Result<Bool, AsError>) -> Void) {
moyaPost(api: APIs.kLiveScore, target: InfoTarget.info) { midResult in
DDLogInfo("赛事情报接口数据:\(midResult)")
switch midResult {
case let .success(midSuccessResult):
guard let matchs = midSuccessResult["matchs"] as? [[String : Any]] else {
self.infoDataList.accept([])
return
}
// 过滤出 JcInfo 非空的项
let filteredMatchs = matchs.filter { match in
guard let jcInfo = match["JcInfo"] as? String, !jcInfo.isEmpty else {
return false
}
return true
}
let infoModels = Mapper<InfoModel>().mapArray(JSONArray: filteredMatchs)
self.infoDataList.accept(infoModels)
completion(.success(true))
case .failure(let err):
completion(.failure(err))
}
}
}
}
......@@ -25,6 +25,7 @@ class RootController: UITabBarController {
tabBar.unselectedItemTintColor = kSubTitleColor
addController(rootVC: HomeController(), title: "赛事比分", image: R.image.home(), selectedImage: R.image.home_selected())
addController(rootVC: InfoController(), title: "赛事情报", image: R.image.ssqb(), selectedImage: R.image.ssqb_selected())
addController(rootVC: ProfileController(), title: "我的", image: R.image.profile(), selectedImage: R.image.profile_selected())
}
......
......@@ -15,7 +15,7 @@ enum LoginOrRegisterTarget: TargetType {
case .mobileCode(let mobile):
let parameters = ["query": """
query{
\(kMobileCode)(mobile:"\(mobile)",randstr:"\(Date().timeIntervalSince1970)",type:7)
\(APIs.kMobileCode)(mobile:"\(mobile)",randstr:"\(Date().timeIntervalSince1970)",type:7)
}
"""]
return .requestParameters(parameters: parameters, encoding: JSONEncoding.default)
......@@ -28,7 +28,7 @@ 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
moyaPostWithSJ(api: APIs.kMobileCode, target: target) { midResult in
DDLogInfo("midResult:\(midResult)")
switch midResult {
......
......@@ -14,3 +14,4 @@
@_exported import Toast_Swift
@_exported import SwiftyJSON
@_exported import RxGesture
@_exported import ObjectMapper
......@@ -17,6 +17,7 @@ import Foundation
//MARK: - AsError
enum AsError: Swift.Error {
case netErr(String) // 网络错误
case formatErr(String) // 格式错误(格式不正确)
case argsErr(String) // 参数错误(接口请求参数)
case resDataErr(String) // 返回数据错误(返回数据缺少相关字段或字段类型不匹配)
case parseErr(String) // 解析错误
......@@ -26,6 +27,8 @@ enum AsError: Swift.Error {
switch self {
case .netErr(let message):
return "network err: \(message)"
case .formatErr(let message):
return "format err: \(message)"
case .argsErr(let message):
return "args err: \(message)"
case .resDataErr(let message):
......@@ -38,9 +41,15 @@ enum AsError: Swift.Error {
}
}
//MARK: - Response解析方式
enum ParseMethod {
case none // 系统默认
case swiftyJSON // SwiftyJSON
}
//MARK: - TargetType
extension TargetType {
var baseURL: URL { URL(string: API_BASE_URL)! }
var baseURL: URL { URL(string: APIs.API_BASE_URL)! }
var path: String { "" }
......@@ -64,9 +73,12 @@ extension TargetType {
final class BaseMoyaPlugin: PluginType {
var vc: UIViewController?
private var spinner: NVActivityIndicatorView!
var showErr: Bool = true
init(vc: UIViewController?, showLoading: Bool = true) {
init(vc: UIViewController?, showLoading: Bool = true, showErr: Bool = true) {
self.vc = vc
self.showErr = showErr
if let tmpVC = self.vc, showLoading {
spinner = NVActivityIndicatorView(frame: CGRect(x: 0, y: 0, width: 60, height: 55), type: .ballSpinFadeLoader, color: kMasterColor)
spinner.center = tmpVC.view.center
......@@ -105,11 +117,13 @@ final class BaseMoyaPlugin: PluginType {
guard case let Result.failure(error) = result else { return }
// 弹出并显示错误信息
let message = error.errorDescription ?? "未知错误"
if let tmpVC = self.vc {
tmpVC.view.makeToast(message)
} else {
kWindow()?.makeToast(message)
if showErr {
let message = error.errorDescription ?? "未知错误"
if let tmpVC = self.vc {
tmpVC.view.makeToast(message)
} else {
kWindow()?.makeToast(message)
}
}
}
}
......@@ -119,12 +133,73 @@ class BaseMoyaProvider<Target: TargetType>: MoyaProvider<Target> {
let disposeBag = DisposeBag()
var basePlugin: BaseMoyaPlugin
init(vc: UIViewController? = nil, showLoading: Bool = true) {
self.basePlugin = BaseMoyaPlugin(vc: vc, showLoading: showLoading)
init(vc: UIViewController? = nil, showLoading: Bool = true, showErr: Bool = true) {
self.basePlugin = BaseMoyaPlugin(vc: vc, showLoading: showLoading, showErr: showErr)
super.init(plugins: [self.basePlugin])
}
static func moyaPost(api: String, target: TargetType, completion: @escaping (Result<[String: JSON], AsError>) -> Void) {
/// Moya Post
/// - Parameters:
/// - api: Api
/// - target: TargetType
/// - completion: completion([String: Any])
func moyaPost(api: String, target: TargetType, completion: @escaping (Result<[String: Any], AsError>) -> Void) {
request(target as! Target) { result in
switch result {
case let .success(response):
do {
guard let json = try JSONSerialization.jsonObject(with: response.data, options: []) as? [String: Any] else {
DDLogError("JSON格式不正确")
completion(.failure(.formatErr("JSON格式不正确")))
break
}
// 解析errors
if let errors = json["errors"] as? [[String: Any]],
let rootErr = errors.first,
let message = rootErr["message"] as? String, !message.isEmpty {
DDLogError("rootErr: \(message)")
completion(.failure(AsError.argsErr(message)))
return
}
// 解析data
guard let data = json["data"] as? [String: Any] else {
DDLogError("data数据错误")
completion(.failure(AsError.resDataErr("data数据错误")))
return
}
// 解析api
guard let apiDic = data["\(api)"] as? [String: Any] else {
DDLogError("\(api)数据错误")
completion(.failure(AsError.resDataErr("\(api)数据错误")))
return
}
// 解析业务Error
if let error = apiDic["Error"] as? String, !error.isEmpty {
DDLogError("api bus err: \(error)")
completion(.failure(AsError.busErr(error)))
} else {
// DDLogInfo("成功:\(apiDic)")
completion(.success(apiDic))
}
} catch {
DDLogError("JSON解析失败:\(error.localizedDescription)")
completion(.failure(.parseErr(error.localizedDescription)))
}
case let .failure(error):
DDLogError("errCode: \(error.errorCode)")
// 应该根据errCode细分错误类型
// completion(.failure(AsError.netErr(error.localizedDescription)))
completion(.failure(AsError.netErr("网络连接失败,请重试")))
}
}
}
/// Moya Post
/// - Parameters:
/// - api: Api
/// - target: TargetType
/// - completion: completion(SwiftJSON处理)
static func moyaPostWithSJ(api: String, target: TargetType, completion: @escaping (Result<[String: JSON], AsError>) -> Void) {
let provider = BaseMoyaProvider<Target>()
provider.request(target as! Target) { result in
......@@ -157,7 +232,7 @@ class BaseMoyaProvider<Target: TargetType>: MoyaProvider<Target> {
DDLogError("api bus err: \(error)")
completion(.failure(AsError.busErr(error)))
} else {
DDLogInfo("成功:\(apiDic)")
// DDLogInfo("成功:\(apiDic)")
completion(.success(apiDic))
}
case let .failure(error):
......@@ -168,3 +243,38 @@ class BaseMoyaProvider<Target: TargetType>: MoyaProvider<Target> {
}
}
}
//MARK: - 数据解析
extension BaseMoyaProvider {
func parseResponse(data: Data) -> Result<[String: Any], AsError> {
/*
错误的:
json:{
"errors" : [
{
"locations" : [
{
"column" : 7,
"line" : 2
}
],
"message" : "Cannot query field \"getMobileCod\" on type \"Query\". Did you mean \"getMobileCode\" or \"getMyFollow\"?"
}
]
}
正确的:
json:{
"data" : {
"getMobileCode" : {
"Error" : "手机号码格式错误",
"ResponseSign" : "",
"UsePool" : false,
"Result" : null
}
}
}
*/
return .success([:])
}
}
......@@ -7,8 +7,13 @@
import Foundation
let SERVER_URL = "https://m.ydn.com/"
let API_BASE_URL = SERVER_URL + "graphql"
struct APIs {
static let SERVER_URL = "https://m.ydn.com/"
static let API_BASE_URL = SERVER_URL + "graphql"
// 获取验证码
static let kMobileCode = "getMobileCode"
// 赛事情报列表
static let kLiveScore = "liveScore"
}
// 获取验证码
let kMobileCode = "getMobileCode"
......@@ -305,7 +305,16 @@ open class GKDevice {
}
public static func statusBarNavBarHeight() -> CGFloat {
return statusBarFrame().size.height + navBarHeight()
// 适配灵动岛状态栏高度
var statusBarHeight: CGFloat = 0
if #available(iOS 13.0, *) {
let window = UIApplication.shared.windows.first
let topPadding = window?.safeAreaInsets.top
statusBarHeight = topPadding ?? 20.0
} else {
statusBarHeight = UIApplication.shared.statusBarFrame.height
}
return statusBarHeight + navBarHeight()
}
public static func navBarHeight() -> CGFloat {
......@@ -372,6 +381,19 @@ open class GKDevice {
return safeAreaInsets
}
static func STATUSBAR_HEIGHT() -> CGFloat {
// 适配灵动岛状态栏高度
var statusBarHeight: CGFloat = 0
if #available(iOS 13.0, *) {
let window = UIApplication.shared.windows.first
let topPadding = window?.safeAreaInsets.top
statusBarHeight = topPadding ?? 20.0
} else {
statusBarHeight = UIApplication.shared.statusBarFrame.height
}
return statusBarHeight
}
public static func statusBarFrame() -> CGRect {
var statusBarFrame = CGRect.zero
if #available(iOS 13.0, *) {
......
......@@ -788,10 +788,10 @@ extension UIViewController {
}else {
if GKDevice.isNotchedScreen { // 刘海屏手机
// iPhone 14 Pro 状态栏高度与安全区域高度不一致,这里改为使用状态栏高度
var topH = GKDevice.statusBarFrame().height
var topH = GKDevice.STATUSBAR_HEIGHT()
if topH == 0 { topH = GKDevice.safeAreaInsets().top }
navBarH = topH + gkNavBarH
}else {
} else {
navBarH = self.gk_statusBarHidden ? gkNavBarH : gkStatusBarNavBarH
}
}
......
......@@ -73,7 +73,7 @@ struct _R {
var accentColor: RswiftResources.ColorResource { .init(name: "AccentColor", path: [], bundle: bundle) }
}
/// This `_R.image` struct is generated, and contains static references to 18 images.
/// This `_R.image` struct is generated, and contains static references to 22 images.
struct image {
let bundle: Foundation.Bundle
......@@ -122,6 +122,12 @@ struct _R {
/// Image `match_setting`.
var match_setting: RswiftResources.ImageResource { .init(name: "match_setting", path: [], bundle: bundle, locale: nil, onDemandResourceTags: nil) }
/// Image `net_err`.
var net_err: RswiftResources.ImageResource { .init(name: "net_err", path: [], bundle: bundle, locale: nil, onDemandResourceTags: nil) }
/// Image `no_data`.
var no_data: RswiftResources.ImageResource { .init(name: "no_data", path: [], bundle: bundle, locale: nil, onDemandResourceTags: nil) }
/// Image `profile`.
var profile: RswiftResources.ImageResource { .init(name: "profile", path: [], bundle: bundle, locale: nil, onDemandResourceTags: nil) }
......@@ -130,6 +136,12 @@ struct _R {
/// Image `right_arrow_icon`.
var right_arrow_icon: RswiftResources.ImageResource { .init(name: "right_arrow_icon", path: [], bundle: bundle, locale: nil, onDemandResourceTags: nil) }
/// Image `ssqb`.
var ssqb: RswiftResources.ImageResource { .init(name: "ssqb", path: [], bundle: bundle, locale: nil, onDemandResourceTags: nil) }
/// Image `ssqb_selected`.
var ssqb_selected: RswiftResources.ImageResource { .init(name: "ssqb_selected", path: [], bundle: bundle, locale: nil, onDemandResourceTags: nil) }
}
/// This `_R.file` struct is generated, and contains static references to 6 resource files.
......
支持 Markdown 格式
你添加了 0 到此讨论。请谨慎行事。
Finish editing this message first!