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
相关产品推荐
相关产品推荐

