从公共 API 获取 JSON 以在 SwiftUI 的列表视图中呈现

Posted

技术标签:

【中文标题】从公共 API 获取 JSON 以在 SwiftUI 的列表视图中呈现【英文标题】:Getting JSON from a public API to render in a list view in SwiftUI 【发布时间】:2019-12-03 04:29:02 【问题描述】:

我正在尝试从Rest Api 获取数据以下载并在SwiftUI 的列表视图中呈现。 我想我设法让JSON 正确下载并分配给所有相关的结构,但是当我去构建它时,模拟器的列表视图中没有显示任何内容。

我什至不确定是否需要在其中添加“Enum CodingKeys”。

谁能指出我哪里出错了?

import Foundation
import SwiftUI
import Combine


struct ContentView: View 
    @ObservedObject var fetcher = LaunchDataFetcher()

    var body: some View 
        VStack 
            List(fetcher.launches)  launch in
                VStack (alignment: .leading) 
                    Text(launch.mission_name)
                    Text(launch.details)
                        .font(.system(size: 11))
                        .foregroundColor(Color.gray)
                
            
        
    


public class LaunchDataFetcher: ObservableObject 

    @Published var launches = [launch]()
    init()
        load()
    

    func load() 
        let url = URL(string: "https://api.spacexdata.com/v3/launches")!
        URLSession.shared.dataTask(with: url) (data,response,error) in
            do 
                if let d = data 
                    let decodedLists = try JSONDecoder().decode([launch].self, from: d)
                    DispatchQueue.main.async 
                        self.launches = decodedLists
                    
                else 
                    print("No Data")
                
             catch 
                print ("Error")
            

        .resume()

    


struct launch: Codable 
    public var flight_number: Int
    public var mission_name: String
    public var details: String

    enum CodingKeys: String, CodingKey 
           case flight_number = "flight_number"
           case mission_name = "mission_name"
           case details = "details"
        


// Now conform to Identifiable
extension launch: Identifiable 
    var id: Int  return flight_number 



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

【问题讨论】:

如果键和结构成员具有相同的名称,则不需要 CodingKeys。如果您将结构成员命名为 camelCased 并添加 convertFromSnakeCase 键解码策略,您甚至不需要 CodingKeys。 我以为是这样。感谢您的建议。 【参考方案1】:

首先,我尝试找出您的代码的哪个错误,并识别某处响应数据的launch.details为空。所以我只是将此属性标记为可选并且它可以工作。

更多细节,你可以参考下面的代码:

struct launch: Codable 
    public var flight_number: Int
    public var mission_name: String
    public var details: String?

    enum CodingKeys: String, CodingKey 
           case flight_number = "flight_number"
           case mission_name = "mission_name"
           case details = "details"
        

在捕获线,获取错误消息以准确了解发生了什么

func load() 
        let url = URL(string: "https://api.spacexdata.com/v3/launches")!
        URLSession.shared.dataTask(with: url) (data,response,error) in
            do 
                if let d = data 
                    let decodedLists = try JSONDecoder().decode([launch].self, from: d)
                    DispatchQueue.main.async 
                        print(decodedLists)
                        self.launches = decodedLists
                    
                else 
                    print("No Data")
                
             catch let parsingError 
                print ("Error", parsingError)
            

        .resume()
    

希望对您有所帮助!

【讨论】:

@Lofty 给我投票吧,这会让我更有动力^_^【参考方案2】:

控制台中有任何消息吗?我认为您需要在 .plist 中添加 NSAppTransportSecurity>NSAllowsArbitraryLoads>YES

Transport security has blocked a cleartext HTTP

【讨论】:

我不需要这样做。【参考方案3】:

在 Trai Nguyen 的帮助下,我设法让模拟器显示数据。

我所缺少的只是确保名为“详细信息”的变量具有可选属性(通过添加“?”) - 就像这样:

 public var details: String?

然后确保当我在视图中呈现文本时,如果返回为空,我必须给它一个值以插入 - 就像这样:

Text(launch.details ?? "No Data Found")

这是适合我的完整代码:

import Foundation
import SwiftUI
import Combine


struct ContentView: View 
    @ObservedObject var fetcher = LaunchDataFetcher()

    var body: some View 
        VStack 
            List(fetcher.launches)  launch in
                VStack (alignment: .leading) 
                    Text(launch.mission_name)

                    Text(launch.details ?? "No Data Found")
                        .font(.system(size: 11))
                        .foregroundColor(Color.gray)
                
            
        
    



public class LaunchDataFetcher: ObservableObject 

    @Published var launches = [launch]()

    init()
        load()
    

    func load() 
        let url = URL(string: "https://api.spacexdata.com/v3/launches")!
        URLSession.shared.dataTask(with: url) (data,response,error) in
            do 
                if let d = data 
                    let decodedLists = try JSONDecoder().decode([launch].self, from: d)
                    DispatchQueue.main.async 
                        print(decodedLists)
                        self.launches = decodedLists
                    
                else 
                    print("No Data")
                
             catch let parsingError 
                print ("Error", parsingError)
            

        .resume()
    


struct launch: Codable 
    public var flight_number: Int
    public var mission_name: String
    public var details: String?


/// Now conform to Identifiable
extension launch: Identifiable 
    var id: Int  return flight_number 



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

【讨论】:

以上是关于从公共 API 获取 JSON 以在 SwiftUI 的列表视图中呈现的主要内容,如果未能解决你的问题,请参考以下文章

Swift - 从模型中的 API 调用返回 JSON 对象作为字典以在视图控制器中使用

如何从 JSON 数组在 DB 中创建表以在 Spring Boot 中创建 REST API

如何准备 API 响应以在 swift 中与 jsonDecoder 一起使用

使用公共 API 从站点中提取 JSON 数据并将其异步显示在页面上

我可以使用哪些免费和公共图像 JSON API 来测试 JSON 请求 [关闭]

从公共文件夹 ReactJS 获取本地 JSON 文件