SwiftUI不使用DispatchGroup调用多API时加载器卡顿及逻辑失效问题
问题修复方案
核心问题分析
- 异步请求与同步计数不匹配:你在同步循环里直接递增
apiCountIndex,这个变量只记录了「发起了多少请求」,和「请求是否完成」完全无关——URLSession.dataTask是异步执行的,循环跑完时所有请求还在后台处理,此时的计数逻辑根本无法判断请求是否全部完成。 - 加载器关闭逻辑错误:你把关闭加载器的代码放在了
switch的default分支里,但你的4个API对应的item.id是1-4,永远走不到default,所以正常情况下加载器永远不会关闭。 - 未处理请求失败场景:如果某个API请求报错,对应的回调里没有任何计数逻辑,会导致总完成数永远达不到4,加载器直接卡住。
修复后的代码
func callApiTest() { // 主线程开启加载器(确保UI操作在主线程) DispatchQueue.main.async { self.isMainLoading.toggle() } print("Start Fetching") let cohortId = UserDefaults.standard.integer(forKey: "cohortId") let apiItems = [ ApiModel(id: 1, apiName: "user/get/x/0?cohort_id=\(cohortId)"), ApiModel(id: 2, apiName: "trainings/xx/0?cohort_id=\(cohortId)"), ApiModel(id: 3, apiName: "survey/get/xxx/0?cohort_id=\(cohortId)"), ApiModel(id: 4, apiName: "user/lowest/xxxx/0?cohort_id=\(cohortId)"), ] // 用局部变量记录已完成的请求数,确保线程安全 var completedRequests = 0 let totalRequests = apiItems.count for item in apiItems { URLSession.shared.dataTask(with: getRequestConfig(endPoint: item.apiName)) { [weak self] data, _, error in guard let self = self else { return } defer { // 不管请求成功/失败,都递增完成计数 completedRequests += 1 // 检查是否所有请求都完成 if completedRequests == totalRequests { // 主线程关闭加载器 DispatchQueue.main.async { self.isMainLoading.toggle() print("DONE CALLING ALL APIS..!") } } } if let error = error { print("parser error = \(error.localizedDescription)") return } guard let data = data else { print("No data received for API: \(item.apiName)") return } switch item.id { case 1: do { let decoder = try JSONDecoder().decode(ParticipantModel.self, from: data) let items = decoder.response for item in items { let entity = ParticipantEntity(context: self.dataController.container.viewContext) entity.id = Int64(item.id) entity.first_name = item.first_name entity.last_name = item.last_name } UserDefaults.standard.setValue(items.count, forKey: "pCounts") self.dataController.save() print("Success Saving Participant Data") } catch { print(error) } case 2: do { let decoder = try JSONDecoder().decode(TrainingModel.self, from: data) let items = decoder.response for item in items { let entity = TrainingEntity(context: self.dataController.container.viewContext) entity.training_id = Int64(item.training_id) entity.group_name = item.columns.group_name entity.group_id = Int16(item.columns.group_id ?? 0) } self.dataController.save() print("Success Saving Training Data") } catch { print(error) } case 3: do { let decoder = try JSONDecoder().decode(ParticipantVisitLogModel.self, from: data) let items = decoder.response for item in items { let entity = ParticipantVisitLogEntity(context: self.dataController.container.viewContext) entity.id = Int16(item.id) entity.visit_id = Int16(item.visit_id) entity.type_of_survey = item.type_of_survey entity.survey_id = Int16(item.survey_id) } self.dataController.save() print("Success Saving Participant Visit Log Data") } catch { print(error) } case 4: do { let decoder = try JSONDecoder().decode(GroupModel.self, from: data) let items = decoder.response for item in items { let entity = GroupEntity(context: self.dataController.container.viewContext) entity.id = Int16(item.id) entity.name = item.name } self.dataController.save() print("Success Group Data") } catch { print(error) } default: print("DEFAULT: Unknown API ID \(item.id)") } }.resume() print("API Called = \(item.apiName)") } }
关键修复点说明
- 用
completedRequests计数完成请求:局部变量记录每个请求完成(无论成功失败)的数量,确保只有当所有请求都处理完才关闭加载器。 defer语句保证计数执行:不管请求成功还是失败,defer里的代码都会执行,避免因报错导致计数遗漏。- 主线程更新UI:所有修改
isMainLoading的操作都放在DispatchQueue.main.async里,因为URLSession的回调是后台线程,直接修改UI会崩溃或不生效。 - 移除无效的
apiCountIndex判断:直接遍历所有API发起请求,不需要额外的索引判断逻辑。
内容的提问来源于stack exchange,提问作者Carole Malonda
相关产品推荐
相关产品推荐

