You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift中如何异步请求两个URL并解析返回的JSON数据

实现方案

你可以通过DispatchGroup来管控多个异步请求的执行状态,等两个URL的拉取解析都完成后再统一刷新UI,同时复用现有解析逻辑即可,具体修改步骤如下:

  • 第一步:把单URL拉取解析的逻辑从viewDidLoad中抽成独立复用方法,避免重复写代码
  • 第二步:使用DispatchGroup对两个请求做计数,每个请求开始前调用enter(),请求完成(无论成功失败)都调用leave()
  • 第三步:通过DispatchGroup的notify方法监听所有请求完成的时机,统一触发UI刷新
  • 补充:为了避免两个异步回调同时操作customers数组导致的数据异常,新增一个串行队列专门处理customers数组的读写操作,保证线程安全
import UIKit

class ViewController: UIViewController {
    
    @IBOutlet weak var customerNameLabel: UILabel!
    @IBOutlet weak var cardNumberLabel: UILabel!
    @IBOutlet weak var dateNTimeLabel: UILabel!
    @IBOutlet weak var amountLabel: UILabel!
    
    var customers = [Customer]()
    var currentCustomerIndex = 0
    // 串行队列,保证customers数组操作线程安全
    private let dataQueue = DispatchQueue(label: "com.yourname.customerDataQueue")
    // 网络请求通用session,避免重复创建
    private let session = URLSession(configuration: .default)

    override func viewDidLoad() {
        super.viewDidLoad()
        
        let group = DispatchGroup()
        // 替换为你的两个JSON接口地址
        guard let url1 = URL(string: "第一个URL地址"),
              let url2 = URL(string: "第二个URL地址") else {
            print("URL格式非法")
            return
        }
        
        // 发起第一个请求
        group.enter()
        fetchAndParseData(url: url1, group: group)
        
        // 发起第二个请求
        group.enter()
        fetchAndParseData(url: url2, group: group)
        
        // 所有请求完成后刷新UI
        group.notify(queue: .main) { [weak self] in
            self?.displayData()
        }
    }
    
    // 抽离的单URL拉取+解析通用方法
    private func fetchAndParseData(url: URL, group: DispatchGroup) {
        let task = session.dataTask(with: url) { [weak self] data, response, error in
            // 无论请求成功失败都要离开group,避免死等
            defer {
                group.leave()
            }
            
            if let error = error {
                print("数据请求失败,错误信息:" + error.localizedDescription)
                return
            }
            
            guard let httpResponse = response as? HTTPURLResponse,
                  httpResponse.statusCode == 200,
                  let validData = data else {
                print("JSON数据生成失败")
                return
            }
            
            do {
                let jsonObj = try JSONSerialization.jsonObject(with: validData, options: .mutableContainers) as? [Any]
                // 切换到串行队列解析数据,避免多线程同时操作customers数组
                self?.dataQueue.async {
                    self?.ParseData(jsonObject: jsonObj)
                }
            } catch {
                print(error.localizedDescription)
            }
        }
        task.resume()
    }


    func ParseData(jsonObject: [Any]?) {
        guard let json = jsonObject else {
            print("解析失败,可选值解包失败")
            return
        }
        
        for firstLevelItems in json {
            guard let object = firstLevelItems as? [String: Any],
                  let fname = object["first_name"] as? String,
                  let lname = object["last_name"] as? String,
                  let fullName = fname + " " + lname as? String,
                  let customerNumber = object["customer_number"] as? Int,
                  let purchase = object["purchase"] as? [String: Any],
                  let time = purchase["time"] as? String,
                  let date = purchase["date"] as? String,
                  let amount = purchase["amount"] as? String
            
            else { continue }
         
            // 嵌套函数:添加交易记录
            func addTransaction(_customer: Customer) {
                if let cardNumber = purchase["card_number"] as? String? {
                    _customer.transactions.append(Transaction(firstName: fname, lastName: lname, time: time, date: date, amount: amount, cardNumber: cardNumber))
                } else {
                    _customer.transactions.append(Transaction(firstName: fname, lastName: lname, time: time, date: date, amount: amount))
                }
            }
            
            let filteredCustomers = customers.filter({ (customer) -> Bool in
                return customer.transactions[currentCustomerIndex].customerName == fullName
            })
            
            if filteredCustomers.count == 0 {
                customers.append(Customer(customerNumber: customerNumber))
                addTransaction(_customer: customers.last!)
            } else if filteredCustomers.count == 1 {
                addTransaction(_customer: filteredCustomers[0])
            } else {
                assertionFailure("客户数组中不允许存在重复客户")
            }
        }
    }
    
    func displayData() {
        // 操作customers数组前先切到串行队列读数据,再切回主队列刷新UI
        dataQueue.async { [weak self] in
            guard let self = self,
                  self.currentCustomerIndex < self.customers.count else {
                return
            }
            let customer = self.customers[self.currentCustomerIndex]
            DispatchQueue.main.async {
                self.customerNameLabel.text = customer.customerName
                self.cardNumberLabel.text = customer.cardNum
                self.dateNTimeLabel.text = customer.dateNTime
                self.amountLabel.text = "$" +  customer.customerAmount.description
            }
        }
    }
    
    
    @IBAction func changeCustomer(_ sender: UIButton) {
        dataQueue.async { [weak self] in
            guard let self = self else { return }
            self.currentCustomerIndex += sender.tag
            
            if self.currentCustomerIndex < 0 {
                self.currentCustomerIndex = self.customers.count - 1
            } else if self.currentCustomerIndex >= self.customers.count {
                self.currentCustomerIndex = 0
            }
            
            DispatchQueue.main.async {
                self.displayData()
            }
        }
    }
}

内容的提问来源于stack exchange,提问作者Molag_Bork

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.12 23:21:03