SwiftUI多股票代码JSON解码异常问题求助
SwiftUI中批量请求股票JSON数据时结果随机、类型不匹配问题的解决方案
一、核心问题:单个AnyCancellable导致订阅被覆盖
你用单个AnyCancellable实例存储订阅,循环请求多个股票代码时,每次新请求的订阅都会覆盖之前的,导致未完成的请求被强制取消,这就是结果随机(有时显示部分、有时全显/全不显)的根本原因。
修复方案:改用Set<AnyCancellable>存储所有订阅,避免覆盖:
class ShareData: ObservableObject { @Published var stocks: [Stock] = [] // 用集合存储所有订阅,每个请求的订阅都会被保留 private var subscriptions = Set<AnyCancellable>() func fetchSingleStock(for symbol: String) { guard let url = URL(string: "你的接口地址?symbol=\(symbol)") else { return } URLSession.shared.dataTaskPublisher(for: url) .map(\.data) .decode(type: Stock.self, decoder: JSONDecoder()) .receive(on: DispatchQueue.main) .sink( receiveCompletion: { completion in if case .failure(let error) = completion { print("请求\(symbol)失败:\(error.localizedDescription)") } }, receiveValue: { [weak self] stock in self?.stocks.append(stock) } ) .store(in: &subscriptions) // 每个订阅存入集合,不会被覆盖 } // 批量触发所有股票请求 func fetchAllStocks(_ symbols: [String]) { stocks.removeAll() // 清空旧数据避免重复 symbols.forEach { fetchSingleStock(for: $0) } } }
二、修复getStockData的返回类型不匹配问题
你遇到的返回类型/解码错误,本质是JSON结构和Stock结构体不匹配,分两种场景处理:
场景1:接口返回单个股票对象(单代码请求)
如果接口返回的JSON是单个Stock结构:
{ "symbol": "AAPL", "price": 150.25, "company_name": "Apple Inc." }
对应的Stock结构体要和字段严格匹配,字段名不一致时需加CodingKeys:
struct Stock: Codable, Identifiable { let id = UUID() // SwiftUI列表所需的唯一标识 let symbol: String let price: Double let companyName: String enum CodingKeys: String, CodingKey { case symbol, price case companyName = "company_name" // 映射JSON的蛇形字段 } }
此时getStockData(用async/await实现)的正确写法:
func getStockData(for symbol: String) async throws -> Stock { guard let url = URL(string: "你的接口地址?symbol=\(symbol)") else { throw NSError(domain: "InvalidURL", code: -1) } let (data, _) = try await URLSession.shared.data(from: url) return try JSONDecoder().decode(Stock.self, from: data) }
场景2:接口返回多个股票数组(批量代码请求)
如果接口支持一次传入多个代码,返回JSON数组:
[ {"symbol": "AAPL", "price": 150.25}, {"symbol": "MSFT", "price": 300.50} ]
getStockData应返回[Stock],结构体保持不变:
func getStockData(for symbols: [String]) async throws -> [Stock] { let symbolStr = symbols.joined(separator: ",") guard let url = URL(string: "你的接口地址?symbols=\(symbolStr)") else { throw NSError(domain: "InvalidURL", code: -1) } let (data, _) = try await URLSession.shared.data(from: url) return try JSONDecoder().decode([Stock].self, from: data) }
三、SwiftUI视图中正确展示数据
用@StateObject绑定数据模型,遍历数组展示:
struct StockListView: View { @StateObject private var shareData = ShareData() let targetSymbols = ["AAPL", "MSFT", "GOOG"] var body: some View { List(shareData.stocks) { stock in VStack(alignment: .leading) { Text(stock.symbol) .font(.headline) Text("$\(stock.price, specifier: "%.2f")") .font(.subheadline) Text(stock.companyName) .font(.caption) } } .onAppear { shareData.fetchAllStocks(targetSymbols) } } }
四、额外排查点
- 检查接口是否有请求频率限制,循环请求过快可能被限流,导致部分请求失败
- 若JSON字段是蛇形命名,需配置解码器:
let decoder = JSONDecoder() decoder.keyDecodingStrategy = .convertFromSnakeCase - 在
sink的failure分支打印完整错误,能快速定位解码失败的具体原因(比如字段缺失、类型不匹配)
内容的提问来源于stack exchange,提问作者imjonu
相关产品推荐
相关产品推荐

