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

SwiftUI解析Polygon API嵌套JSON数据不显示问题求助

问题修复方案

核心问题分析

代码无法显示数据主要有三个关键问题:

  • 解码类型不匹配:API返回单个JSON对象(包含results数组),但你尝试解码成[Stocks](Stocks对象数组),导致解码失败
  • 字段名称不一致:模型里last_update_utc和API返回的last_updated_utc拼写差异(少了一个d),触发解码错误
  • 数据存储类型错误:ViewModel中stocks定义为数组,但实际只需要存储单个Stocks对象

分步修复

1. 修正模型字段名称

把Results结构体里的last_update_utc改为last_updated_utc,匹配API返回字段:

struct Results: Hashable, Codable{
    // ... 其他字段保持不变
    var last_updated_utc: String // 修正拼写
}

2. 调整ViewModel的数据类型和解码逻辑

  • 将@Published var stocks: [Stocks] = []改为单个可选对象@Published var stockData: Stocks? = nil
  • 解码时用Stocks.self替代[Stocks].self,同时添加错误打印便于调试:
class ViewModel: ObservableObject{
    @Published var stockData: Stocks? = nil
    func fetch(){
        guard let url = URL(string: "https://api.polygon.io/v3/reference/tickers?market=stocks&active=true&apiKey=<apikey>") else{return}
        
        let task = URLSession.shared.dataTask(with: url) {[weak self]data, _, error in
            guard let data = data, error == nil else{
                print("网络请求错误:", error ?? "未知错误")
                return
            }
            do{
                let stocks = try JSONDecoder().decode(Stocks.self, from: data)
                DispatchQueue.main.async{
                    self?.stockData = stocks
                }
            }
            catch{
                print("解码错误:", error)
            }
        }
        task.resume()
    }
}

3. 更新ContentView的列表渲染逻辑

判断stockData是否存在,再遍历它的results数组,同时替换旧的NavigationView为iOS 16+推荐的NavigationStack:

struct ContentView: View {
    @StateObject var viewModel = ViewModel()

    var body: some View {
        NavigationStack{
            List{
                if let results = viewModel.stockData?.results {
                    ForEach(results, id: \.self){ st in
                        VStack(alignment: .leading, spacing: 4){
                            Text(st.ticker)
                                .font(.headline)
                            Text(st.name)
                                .font(.subheadline)
                                .foregroundColor(.secondary)
                            Text("Currency: \(st.currency_name)")
                                .font(.caption)
                        }
                        .padding(.vertical, 8)
                    }
                } else {
                    ProgressView()
                }
            }
            .navigationTitle("Stocks")
        }
        .onAppear{
            viewModel.fetch()
        }
    }
}

完整修正代码

import SwiftUI

struct Stocks: Hashable, Codable{
    var results: [Results]
    var status: String? // 设为可选,避免API无status字段时解码失败
    
    struct Results: Hashable, Codable{
        var ticker: String
        var name: String
        var market: String
        var locale: String
        var primary_exchange: String
        var type: String
        var active: Bool
        var currency_name: String
        var cik: String
        var composite_figi: String
        var share_class_figi: String
        var last_updated_utc: String // 修正拼写
    }
}

class ViewModel: ObservableObject{
    @Published var stockData: Stocks? = nil
    
    func fetch(){
        guard let url = URL(string: "https://api.polygon.io/v3/reference/tickers?market=stocks&active=true&apiKey=<apikey>") else{return}
        
        let task = URLSession.shared.dataTask(with: url) {[weak self]data, _, error in
            guard let data = data, error == nil else{
                print("网络请求错误:", error ?? "未知错误")
                return
            }
            do{
                let stocks = try JSONDecoder().decode(Stocks.self, from: data)
                DispatchQueue.main.async{
                    self?.stockData = stocks
                }
            }
            catch{
                print("解码错误:", error)
            }
        }
        task.resume()
    }
}

struct ContentView: View {
    @StateObject var viewModel = ViewModel()

    var body: some View {
        NavigationStack{
            List{
                if let results = viewModel.stockData?.results {
                    ForEach(results, id: \.self){ st in
                        VStack(alignment: .leading, spacing: 4){
                            Text(st.ticker)
                                .font(.headline)
                            Text(st.name)
                                .font(.subheadline)
                                .foregroundColor(.secondary)
                            Text("Currency: \(st.currency_name)")
                                .font(.caption)
                        }
                        .padding(.vertical, 8)
                    }
                } else {
                    ProgressView()
                }
            }
            .navigationTitle("Stocks")
        }
        .onAppear{
            viewModel.fetch()
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 22:05:16