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
相关产品推荐
相关产品推荐

