iOS开发求助:无法将远程JSON解析为[String : [ShopItem]]字典
问题分析与修复方案
你的代码存在几个关键问题:
- completion调用时机错误:
completion()放在了task.resume()之后,网络请求是异步操作,此时请求还未完成就执行了completion,导致外部无法获取到最新数据。 - 错误处理不当:滥用
fatalError()会直接导致程序崩溃,且try?会吞掉解码错误,无法定位解码失败的具体原因。 - 强制解包风险:
results["latest"]!如果JSON中没有"latest"键会直接崩溃,应该用可选绑定处理。
修复后的代码:
struct ShopItem: Codable { let category: String let name: String let price: Int let image_url: String } public class NetworkHelper { var items: [ShopItem] = [] let latestURL = URL(string: "https://run.mocky.io/v3/cc0071a1-f06e-48fa-9e90-b1c2a61eaca7") let flashSaleURL = URL(string: "https://run.mocky.io/v3/a9ceeb6e-416d-4352-bde6-2203416576ac") let urlSession = URLSession.shared // 修改completion为带Result类型的闭包,传递成功或失败信息 func downloadLatest(_ completion: @escaping (Result<[ShopItem], Error>) -> Void) { guard let latestURL = latestURL else { completion(.failure(NSError(domain: "Invalid URL", code: -1, userInfo: nil))) return } let task = urlSession.dataTask(with: latestURL) { [weak self] data, response, error in // 先处理网络错误 if let error = error { completion(.failure(error)) return } // 验证响应状态码 guard let httpResponse = response as? HTTPURLResponse, (200..<300).contains(httpResponse.statusCode) else { completion(.failure(NSError(domain: "Invalid response", code: (response as? HTTPURLResponse)?.statusCode ?? -2, userInfo: nil))) return } // 验证数据存在 guard let data = data else { completion(.failure(NSError(domain: "No data received", code: -3, userInfo: nil))) return } do { // 解码JSON let results = try JSONDecoder().decode([String: [ShopItem]].self, from: data) // 安全获取"latest"对应的数据 guard let latestItems = results["latest"] else { completion(.failure(NSError(domain: "Missing 'latest' key in JSON", code: -4, userInfo: nil))) return } self?.items = latestItems completion(.success(latestItems)) } catch { // 捕获解码错误,方便调试 print("解码失败:\(error)") completion(.failure(error)) } } task.resume() // 移除这里的completion调用,放到闭包内部执行 } }
使用示例:
let helper = NetworkHelper() helper.downloadLatest { result in switch result { case .success(let items): print("获取到\(items.count)个商品") print(items) case .failure(let error): print("请求失败:\(error)") } }
内容的提问来源于stack exchange,提问作者максим мартынов
相关产品推荐
相关产品推荐

