使用Alamofire、SwiftJSON配合@escaping闭包解析数据并加载至TableView
实现方案
第一步:定义测试用轻量数据模型
你当前方法声明的回调返回[WelcomeElement],首先要定义对应层级的模型结构存储解析出的字段,测试阶段可以只保留你需要用到的属性即可:
// 下注选项模型 struct Outcome { let name: String let coefficient: String } // 下注类型模型 struct BetType { let name: String let outcomesCounter: Int? let outcomes: [Outcome] } // 赛事模型 struct Event { let team1: String let team2: String let score1: String let score2: String let betTypes: [BetType] } // 联赛模型 struct League { let name: String let events: [Event] } // 根元素模型 对应你方法中的WelcomeElement struct WelcomeElement { let name: String let betTypes: [BetType] let leagues: [League] }
第二步:修改网络请求解析逻辑
你原有代码中遍历拿到的字段没有做存储,且存在循环变量重名、强制解析容易崩溃的问题,修改后的代码如下:
class Network { let baseUrl = URL(string:"https://ppbet-327310.appspot.com/api/games") func getCity(completion: @escaping([WelcomeElement]) -> ()) { guard let url = baseUrl else { completion([]) return } AF.request(url, method: .get).responseJSON { (response) in switch response.result { case .success: guard let data = response.data else { completion([]) return } // 替换强制解析为do-catch捕获错误 do { let json = try JSON(data: data) var resultArray = [WelcomeElement]() let arrayCount = json.array?.count ?? 0 // 遍历根数组 for rootIndex in 0..<arrayCount { let rootItem = json[rootIndex] let gameName = rootItem["name"].string ?? "N/A" // 解析根层级betTypes var rootBetTypes = [BetType]() if let betTypesArr = rootItem["betTypes"].array { for betIndex in 0..<betTypesArr.count { let betItem = betTypesArr[betIndex] let outcomesCounter = betItem["outcomesCounter"].int let betName = betItem["name"].string ?? "未知下注类型" // 解析下注选项 var outcomes = [Outcome]() if let outcomesArr = betItem["outcomes"].array { for outcomeIndex in 0..<outcomesArr.count { let outcomeItem = outcomesArr[outcomeIndex] let outcomeName = outcomeItem["name"].string ?? "未知选项" let coefficient = outcomeItem["coefficient"].string ?? "1.0" outcomes.append(Outcome(name: outcomeName, coefficient: coefficient)) } } rootBetTypes.append(BetType(name: betName, outcomesCounter: outcomesCounter, outcomes: outcomes)) } } // 解析联赛数组 var leagues = [League]() if let leaguesArr = rootItem["leagues"].array { for leagueIndex in 0..<leaguesArr.count { let leagueItem = leaguesArr[leagueIndex] let leagueName = leagueItem["name"].string ?? "未知联赛" // 解析赛事数组 var events = [Event]() if let eventsArr = leagueItem["events"].array { for eventIndex in 0..<eventsArr.count { let eventItem = eventsArr[eventIndex] let team1 = eventItem["team1"].string ?? "未知队伍1" let team2 = eventItem["team2"].string ?? "未知队伍2" let score1 = eventItem["score1"].string ?? "0" let score2 = eventItem["score2"].string ?? "0" // 解析赛事下注类型 var eventBetTypes = [BetType]() if let eventBetArr = eventItem["betTypes"].array { for eventBetIndex in 0..<eventBetArr.count { let eventBetItem = eventBetArr[eventBetIndex] let betName = eventBetItem["name"].string ?? "未知下注类型" // 解析下注选项 var outcomes = [Outcome]() if let outcomesArr = eventBetItem["outcomes"].array { for outcomeIndex in 0..<outcomesArr.count { let outcomeItem = outcomesArr[outcomeIndex] let outcomeName = outcomeItem["name"].string ?? "未知选项" let coefficient = outcomeItem["coefficient"].string ?? "1.0" outcomes.append(Outcome(name: outcomeName, coefficient: coefficient)) } } eventBetTypes.append(BetType(name: betName, outcomesCounter: nil, outcomes: outcomes)) } } events.append(Event(team1: team1, team2: team2, score1: score1, score2: score2, betTypes: eventBetTypes)) } } leagues.append(League(name: leagueName, events: events)) } } // 组装根元素加入结果数组 let element = WelcomeElement(name: gameName, betTypes: rootBetTypes, leagues: leagues) resultArray.append(element) } DispatchQueue.main.async { completion(resultArray) } } catch { print("JSON解析错误:\(error)") DispatchQueue.main.async { completion([]) } } case .failure(let error): print("请求错误:\(error)") DispatchQueue.main.async { completion([]) } } } } }
第三步:ViewController中接收数据渲染TableView
- 首先在ViewController中声明数据源数组和TableView相关属性:
class GameListViewController: UIViewController { @IBOutlet weak var tableView: UITableView! // 存储解析后的完整数据 var gameDataList = [WelcomeElement]() // 如果需要平铺开展示所有赛事,可以额外声明一个赛事数组 var allEvents = [Event]() override func viewDidLoad() { super.viewDidLoad() tableView.dataSource = self // 发起网络请求 loadData() } private func loadData() { Network().getCity { [weak self] result in guard let self = self else { return } self.gameDataList = result // 如果需要展示所有赛事,遍历组装赛事数组 self.allEvents = result.flatMap { $0.leagues.flatMap { $0.events } } // 刷新列表 self.tableView.reloadData() } } }
- 实现TableView数据源方法,按需展示对应层级的数据即可,以平开展示所有赛事为例:
extension GameListViewController: UITableViewDataSource { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return allEvents.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "GameCell", for: indexPath) as! GameCell let event = allEvents[indexPath.row] cell.team1Label.text = event.team1 cell.team2Label.text = event.team2 cell.scoreLabel.text = "\(event.score1) : \(event.score2)" return cell } }
内容的提问来源于stack exchange,提问作者Pavel Zaruckov
相关产品推荐
相关产品推荐

