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

SwiftUI解码嵌套JSON返回nil,求排查问题

问题排查与修复方案

看起来你遇到的核心问题是网络请求根本没有被触发,再加上一些错误处理和UI细节的缺失,导致始终拿到nil。下面一步步帮你解决:

1. 最关键的问题:没有发起网络请求

你的ProductsList类里定义了fetchProducts()方法,但在ContentView中,你只是创建了它的实例,却从来没有调用这个方法去发起请求!这就导致prods一直保持初始值nil。

修复:在视图出现时触发请求

修改ContentView,在onAppear回调中调用请求方法:

struct ContentView: View {
    @ObservedObject var decodedProducts = ProductsList() // 修正变量命名,符合Swift小写开头的规范
    
    var body: some View {
        VStack{
            HStack{
                Text("Pasta: ")
                Text(decodedProducts.prods?.products.pasta.description ?? "Loading...")
                    .fontWeight(.bold)
                    .foregroundColor(.red)
            }
            HStack{
                Text("Pizza: ")
                Text(decodedProducts.prods?.products.pizza.description ?? "Loading...")
                    .fontWeight(.bold)
                    .foregroundColor(.blue)
            }
            HStack{
                Text("Apples: ")
                Text(decodedProducts.prods?.products.apples.description ?? "Loading...")
                    .fontWeight(.bold)
                    .foregroundColor(.green)
            }
        }
        .padding()
        .onAppear {
            decodedProducts.fetchProducts() // 新增:视图出现时发起网络请求
        }
    }
}

2. 完善NetworkController的错误处理

原代码的错误处理非常不完善:如果网络出错、没有返回数据或者解码失败,都不会调用completion,也看不到具体的错误原因,很难排查问题。

修复:增强错误处理和日志

struct NetworkController {
    static func fetchProducts(completion: @escaping ((ProductWrapper?) -> Void)) {
        guard let url = URL(string: "http://localhost:1234/index.json") else {
            print("❌ 无效的URL")
            completion(nil)
            return
        }
        
        URLSession.shared.dataTask(with: url) { data, response, error in
            // 统一在主线程处理UI相关的回调
            DispatchQueue.main.async {
                if let error = error {
                    print("❌ 网络请求错误:\(error)")
                    completion(nil)
                    return
                }
                
                guard let data = data else {
                    print("❌ 没有接收到服务器返回的数据")
                    completion(nil)
                    return
                }
                
                do {
                    let prods = try JSONDecoder().decode(ProductWrapper.self, from: data)
                    completion(prods)
                } catch {
                    // 打印完整的解码错误,方便定位问题(比如键名不匹配、类型错误)
                    print("❌ JSON解码错误:\(error)")
                    completion(nil)
                }
            }
        }.resume()
    }
}

3. 验证JSON与结构体的匹配性

仔细核对你的JSON键名和Product结构体的属性:
你的JSON里的键是"pasta"、"pizza"、"apples"、"sausages"、"potatoes"、"cucmbers"、"oranges",和结构体里的属性完全一致,这部分是没问题的。但如果后续修改JSON时出现拼写错误(比如把cucmbers改成cucumbers),一定要同步修改结构体的属性名,否则会解码失败。

最后测试

完成以上修改后,运行代码:

  1. 确保本地服务器正常运行,index.json可以正常访问
  2. 查看控制台日志,如果有错误会清晰打印出来
  3. 视图出现后会自动发起请求,成功后显示对应的产品价格

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 15:13:11