SwiftUI新闻应用如何正确设置模型与ViewModel加载NewsAPI数据
问题根因
- 数据模型和API返回结构不匹配:newsapi.org的返回根节点是单个对象,不是数组;且根节点下的
articles字段是文章数组,你当前定义为单个Article类型,是解析失败打印failed的核心原因 - Source结构体的
id字段要设为可选型:部分新闻来源的id为null,不设可选会触发解析失败 - ViewModel属性定义错误:你当前
@Published的是[APIResponse]数组,实际应该直接存储文章数组[Article]更适配列表渲染 - 列表遍历逻辑错误:当前遍历的是API响应数组,应该直接遍历文章数组简化取值逻辑
修正后代码
1. 数据模型(Model)
import Foundation struct APIResponse: Codable { // 改为Article数组 let articles: [Article] } struct Article: Codable { let source: Source let title: String let description: String? // 部分文章描述为空,建议也设为可选 } struct Source: Codable, Identifiable { // id改为可选型,适配null值 let id: String? let name: String }
2. 视图模型(ViewModel)
import Foundation class NewsViewModel: ObservableObject { // 改为存储Article数组 @Published var articles = [Article]() init() { fetchNews() } func fetchNews() { guard let url = URL(string: "https://newsapi.org/v2/top-headlines?country=us&category=business&apiKey=4b6cfa9b54c74b4db7d7d8a2120718d3") else { return } let task = URLSession.shared.dataTask(with: url) { data, _, error in guard let data = data, error == nil else { return } do { // 解析类型改为APIResponse.self,根节点是单个对象不是数组 let response = try JSONDecoder().decode(APIResponse.self, from: data) DispatchQueue.main.async { // 直接赋值文章数组 self.articles = response.articles } } catch { print("解析失败: \(error)") // 打印具体错误信息方便后续定位 } } task.resume() } }
3. ContentView
import SwiftUI struct ContentView: View { @ObservedObject var viewModel = NewsViewModel() var body: some View { NavigationView { List(viewModel.articles, id: \.title) { article in VStack(alignment: .leading) { // 直接取article的属性即可,不需要多一层.articles Text(article.source.name) .font(.headline) Text(article.title) .font(.subheadline) if let desc = article.description { Text(desc) .font(.caption) .foregroundColor(.secondary) } } } .navigationTitle("News List") } } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } }
内容的提问来源于stack exchange,提问作者JS_is_awesome18
相关产品推荐
相关产品推荐

