如何为 SwiftUI 新闻应用程序设置模型

问题描述

我正在尝试构建一个从 newsapi.org API 数据源返回新闻项目列表的基本应用程序。我将 viewmodel 设置为从 API 获取新闻并将数据转换为模型。然后,我将我的模型设置为围绕 api 响应中的特定项目进行构建(请参阅下面 viewmodel 中 API 的 URL)。最后,我设置了我的 ContentView 以在列表中返回新闻项目。该应用程序构建良好,但新闻项(来源名称文章标题)未填充在屏幕上,并且在控制台中我收到打印的“失败”消息。我的模型设置不正确吗?文章应该是文章的数组(例如[文章])吗?知道如何设置模型(或视图模型)以在屏幕上填充新闻项目吗?感谢您的反馈。

内容视图

import SwiftUI

struct ContentView: View {
    @Observedobject var viewmodel = Newsviewmodel()
            
    var body: some View {
        NavigationView {
            
            vstack {
                
                List(viewmodel.articles,id: \.articles.source.id) { news in
                    vstack(alignment: .leading) {
                        Text(news.articles.source.name)
                        Text(news.articles.title)
                        Text(news.articles.description)
                    }
                }
            }
            
            .navigationTitle("News List")
        }
    }
}

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

模型

import Foundation

struct APIResponse: Codable {
    let articles: Article
}

struct Article: Codable {
    let source: Source
    let title: String
    let description: String
}

struct Source: Codable,Identifiable {
    let id: String
    let name: String
}

视图模型

import Foundation

class Newsviewmodel: ObservableObject {
    @Published var articles = [APIResponse]()
    
    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 {
                let model = try JSONDecoder().decode([APIResponse].self,from: data)
                //update properties on the main thread
                dispatchQueue.main.async {
                    self.articles = model
                }
            }
            catch {
                print("Failed")
            }
        }
        task.resume()
    }
}

解决方法

总是打印错误,以防您不确定为什么失败:

print("\(error)") // Instead of print("failed")

如果任何值中包含 null,则将其设为可选:

struct APIResponse: Codable {
    let articles: [Article]
}

struct Article: Codable {
    let source: Source
    let title: String
    let description: String?
}

struct Source: Codable,Identifiable {
    let id: String?
    let name: String?
}

更新视图:

struct ContentView: View {
    @ObservedObject var viewModel = NewsViewModel()
    
    var body: some View {
        
        NavigationView {
            List(viewModel.articles,id: \.source.id) { news in
                VStack {
                    VStack(alignment: .leading) {
                        Text(news.source.name ?? "")
                        Text(news.title)
                        Text(news.description ?? "")
                    }
                }
            }
            .navigationTitle("Landmarks")
        }
    }
}

查看模型更新:

@Published var articles = [Article]()

 let model = try JSONDecoder().decode(APIResponse.self,from: data)
                //update properties on the main thread
                DispatchQueue.main.async {
                    self.articles = model.articles
                }