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

Swift单函数处理双API请求Completion Handler的类型错误问题

问题分析

你遇到的编译错误根源很清晰:定义的completionHandler要求返回[OnSaleItem], [SlideItem], [NewArrivalItem], [EditorChoiceItem], Error?这组参数,但第二个API回调里传入了[DaftarProdukItem]和nil,参数的类型、数量完全不匹配,导致编译器报错。

同时代码还存在逻辑问题:当前会在第一个API完成后立刻触发一次回调,第二个API完成后又尝试触发一次,这不符合你「获取所有结果后统一返回」的需求——必须等两个API请求都完成,汇总所有数据后再调用一次回调。

解决方案

步骤1:调整Completion Handler签名

要让回调能承载所有返回数据,扩展参数列表或者封装成模型:

方案A:扩展参数列表

func fetch(completionHandler: @escaping ([OnSaleItem], [SlideItem], [NewArrivalItem], [EditorChoiceItem], [DaftarProdukItem], Error?) -> Void) {
    // 后续逻辑对应修改
}

方案B:用模型封装结果(更优雅)

struct FetchResult {
    let onSaleItems: [OnSaleItem]
    let slideItems: [SlideItem]
    let newArrivalItems: [NewArrivalItem]
    let editorChoiceItems: [EditorChoiceItem]
    let daftarProdukItems: [DaftarProdukItem]
}

func fetch(completionHandler: @escaping (FetchResult?, Error?) -> Void) {
    // 后续逻辑对应修改
}

步骤2:用DispatchGroup等待两个API完成

两个API是并行发起的,需要用DispatchGroup跟踪任务状态,等两者都完成后再汇总数据返回:

func fetch(completionHandler: @escaping ([OnSaleItem], [SlideItem], [NewArrivalItem], [EditorChoiceItem], [DaftarProdukItem], Error?) -> Void) {
    // 用局部变量存储结果,避免类属性被多次调用污染
    var onSaleItems: [OnSaleItem] = []
    var slideItems: [SlideItem] = []
    var newArrivalItems: [NewArrivalItem] = []
    var editorChoiceItems: [EditorChoiceItem] = []
    var daftarProdukItems: [DaftarProdukItem] = []
    var requestError: Error?
    
    let dispatchGroup = DispatchGroup()
    
    apiCaller.fetchUser(completionHandler: { baseData, error in
        guard let baseData = baseData, error == nil else {
            completionHandler([], [], [], [], [], error)
            return
        }
        
        // 标记第一个API任务开始
        dispatchGroup.enter()
        self.apiCaller.fetchSlideOnSaleEditorChoiceNewArrivalData(baseData: baseData, completionHandler: { decodedString, error in
            // 任务结束时必须标记,用defer确保执行
            defer { dispatchGroup.leave() }
            
            guard let decodedString = decodedString, error == nil else {
                requestError = error
                return
            }
            
            guard let data = convertToDictionary(text: decodedString) else {
                requestError = NSError(domain: "YourAppDomain", code: -1, userInfo: [NSLocalizedDescriptionKey: "字符串转字典失败"])
                return
            }
            
            // 用map替代嵌套循环,更简洁安全
            if let onSaleArray = data["onsale"] as? [[String: String]] {
                onSaleItems = onSaleArray.compactMap { OnSaleItem(dict: $0) }
            }
            if let slideArray = data["slide"] as? [[String: String]] {
                slideItems = slideArray.compactMap { SlideItem(dict: $0) }
            }
            if let newArrivalArray = data["newarrival"] as? [[String: String]] {
                newArrivalItems = newArrivalArray.compactMap { NewArrivalItem(dict: $0) }
            }
            if let editorChoiceArray = data["featured"] as? [[String: String]] {
                editorChoiceItems = editorChoiceArray.compactMap { EditorChoiceItem(dict: $0) }
            }
        })
        
        // 标记第二个API任务开始
        dispatchGroup.enter()
        self.apiCaller.fetchProducts(baseData: baseData, completionHandler: { dataProducts, error in
            defer { dispatchGroup.leave() }
            
            guard let dataProducts = dataProducts, error == nil else {
                requestError = error
                return
            }
            
            guard let data = convertToDictionary(text: dataProducts) else {
                requestError = NSError(domain: "YourAppDomain", code: -1, userInfo: [NSLocalizedDescriptionKey: "字符串转字典失败"])
                return
            }
            
            if let productArray = data["data"] as? [[String: String]] {
                daftarProdukItems = productArray.compactMap { DaftarProdukItem(dict: $0) }
            }
        })
        
        // 等待两个任务都完成,在主线程触发回调
        dispatchGroup.notify(queue: .main) {
            completionHandler(onSaleItems, slideItems, newArrivalItems, editorChoiceItems, daftarProdukItems, requestError)
        }
    })
}

额外优化说明

  • 移除了原代码中的强制解包(as!),改用可选绑定和compactMap,避免因接口返回数据格式异常导致崩溃
  • 把类属性数组改成函数局部变量,防止多次调用fetch时出现数据残留问题
  • 用defer确保dispatchGroup.leave()一定会执行,避免因分支逻辑遗漏导致死锁

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 06:17:12