Swift解析JSON数据时返回对象为nil 无法返回数组问题求助
问题根因
- 核心问题为异步任务时序错误:
URLSession.dataTask属于异步执行任务,调用resume()后网络请求会在后台线程运行,不会阻塞当前线程,函数会立刻执行return object语句,此时网络请求还未完成,回调闭包还没触发,object尚未被赋值,自然返回nil。你在void函数里能拿到正确数据,是因为打印逻辑写在了异步回调内部,会等请求完成后才执行。 - 额外可优化点:Swift类型命名遵循大驼峰规范,你定义的
struct catalogobject建议改名为CatalogObjectResponse,避免和内部的[CatalogObject]类型混淆,也能降低解码匹配失败的概率。 - 不要对网络返回的
data做强制解包,网络请求可能失败无返回数据,强制解包会导致App直接崩溃。
解决方案1:闭包回调(兼容所有Swift版本)
把同步返回改成异步回调,拿到结果后通过闭包传回调用方:
// 修正模型命名,遵循规范 struct CatalogObjectResponse: Hashable, Decodable, Encodable { let objects: [CatalogObject]? } // 函数改为无返回值,新增结果回调参数 func getCatalog(completion: @escaping ([CatalogObject]?, Error?) -> Void) { let url = URL(string: "https://connect.squareupsandbox.com/v2/catalog/list")! var request = URLRequest(url: url) request.httpMethod = "GET" request.addValue("Bearer \(token)", forHTTPHeaderField: "Authorization") request.addValue("\(content_type)", forHTTPHeaderField: "Content-Type") request.addValue("\(square_version)", forHTTPHeaderField: "Square-Version") URLSession.shared.dataTask(with: request) { data, res, error in // 先处理错误,避免强制解包 if let error = error { completion(nil, error) return } guard let data = data else { completion(nil, NSError(domain: "GetCatalog", code: -1, userInfo: [NSLocalizedDescriptionKey: "无返回数据"])) return } do { let catalogdata = try JSONDecoder().decode(CatalogObjectResponse.self, from: data) completion(catalogdata.objects, nil) } catch { completion(nil, error) } }.resume() } // 调用示例 getCatalog { objects, error in if let error = error { print("请求失败: \(error)") return } guard let objects = objects else { print("无商品数据") return } // 拿到结果后如果要刷新UI需要切回主线程 DispatchQueue.main.async { // 执行UI更新等后续操作 } }
解决方案2:async/await(Swift 5.5+ 推荐)
如果项目适配iOS 15+/macOS 12+及以上系统,可以用更简洁的新版Swift并发语法:
func getCatalog() async throws -> [CatalogObject]? { let url = URL(string: "https://connect.squareupsandbox.com/v2/catalog/list")! var request = URLRequest(url: url) request.httpMethod = "GET" request.addValue("Bearer \(token)", forHTTPHeaderField: "Authorization") request.addValue("\(content_type)", forHTTPHeaderField: "Content-Type") request.addValue("\(square_version)", forHTTPHeaderField: "Square-Version") let (data, _) = try await URLSession.shared.data(for: request) let catalogdata = try JSONDecoder().decode(CatalogObjectResponse.self, from: data) return catalogdata.objects } // 调用示例 Task { do { let objects = try await getCatalog() DispatchQueue.main.async { // 执行后续操作 } } catch { print("请求失败: \(error)") } }
内容的提问来源于stack exchange,提问作者Leo
相关产品推荐
相关产品推荐

