SwiftUI - 无法在应用程序中正确显示 JSON 数据

时间:2021-07-22 03:04:44

标签: json swift api swiftui

我在尝试从 MTA API(纽约地铁系统)(Link Here) 获取数据返回时遇到了一些麻烦。数据在 GTFS 中,所以我使用了一个在虚拟环境中设置的解析器,该解析器将数据转换为 JSON 并将其发送给我(示例 JSON 稍后发布)。基本上就完成了GTFS到JSON的解码。

现在我有了 JSON 数据,我需要让它显示在我的应用程序中。我以前处理过 API,但没有处理过这么多“层”。

问题是:我应该如何在 Swift 端构建我的代码,以便我可以访问在 JSON 中发送的每个数据点?

以下是我的工作。数据根本没有显示在我的 ContentView 中。我猜这与我的模型结构不正确有关,因此我试图显示的数据实际上并不存在:

struct TrainResponse: Codable {
let data: [String]
let n, s: [NS]
let id: String
let lastUpdate: Date
let location: [Double]
let name: String
let routes: [String]
let stops: [String: [Double]]

enum CodingKeys: String, CodingKey {
    case data
    case n = "N"
    case s = "S"
    case id
    case lastUpdate = "last_update"
    case location, name, routes, stops
 }
}

// MARK: - N
struct NS: Codable {
let route: String
let time: Date
}


class API: ObservableObject {

@Published var storedData = String()
@Published var ns = NS.self
@Published var id = String()
@Published var lastUpdate = Date()
@Published var location = [Double]()
@Published var name = String()
@Published var routes = [String]()
@Published var stops = [String: [Double]]()

func loadData() {
    guard let url = URL(string: "http://127.0.0.1:5000/by-route/A") else {
        print("Your API end point is Invalid")
        return
    }
    let request = URLRequest(url: url)
    URLSession.shared.dataTask(with: request) { data, response, error in
        if let data = data {
            if let response = try? JSONDecoder().decode(TrainResponse.self, from: data) {
                DispatchQueue.main.async {
                    if let data = response.data.first {
                        self.storedData = data
                        
                        self.routes = response.routes
                        self.id = response.id
                        self.lastUpdate = response.lastUpdate
                        self.location = response.location
                        self.name = response.name
                        self.routes = response.routes
                        self.stops = response.stops
                    }
                }
                return
            }
        }
    }.resume()
  }
}

下面是 JSON 的示例 - 它重复了您在下面看到的内容数百次,但我在示例中将其关闭,就好像它是一个完整的 JSON 对象(以便更容易调试)

{
"data": [
    {
        "N": [
            {
                "route": "A",
                "time": "2021-07-21T21:33:00-04:00"
            }
        ],
        "S": [
            {
                "route": "A",
                "time": "2021-07-21T21:34:40-04:00"
            }
        ],
        "id": "6d6a",
        "last_update": "2021-07-21T21:25:06-04:00",
        "location": [
            40.681711,
            -73.837683
        ],
        "name": "104 St",
        "routes": [
            "A"
        ],
        "stops": {
            "A63": [
                40.681711,
                -73.837683
            ]
        }
    }
    ],
    "updated": "2021-07-21T21:25:06-04:00"
}

我在这里真的是无所适从。关于如何重构我的代码以正确返回数据的任何想法?

我知道我应该嵌套数据(例如 self.routes = data["N"].etc.etc,但我似乎不太明白如何构建我的代码以便我可以这样做。一旦完成,我应该可以毫无问题地显示app里的数据..只需要先把数据拿到app里即可!

编辑 - 为 contentview 添加代码

import SwiftUI

struct ContentView: View {

@StateObject var api = API()

var body: some View {
    Text("yo")
    
    List {
        ForEach(api.routes, id: \.self) { index in
            Text(index)
        }
    }
    .onAppear { api.loadData() }
    
  }
}

EDIT 2 我已经更新了数据模型。它们现在如下所示:

    struct Main: Codable {
    let data: [Datum]
    let updated: String
}

// MARK: - Datum
struct Datum: Codable {
    let n, s: [N]
    let id: String
    let lastUpdate: Date
    let location: [Double]
    let name: String
    let routes: [String]
    let stops: [String: [Double]]

    enum CodingKeys: String, CodingKey {
        case n = "N"
        case s = "S"
        case id
        case lastUpdate = "last_update"
        case location, name, routes, stops
    }
}

// MARK: - N
struct N: Codable {
    let route: String
    let time: Date
}

2 个答案:

答案 0 :(得分:1)

这对我有用:

编辑:

struct GTFSObject: Codable {
    var data: [TrainResponse]   // <--- var
    let updated: String?
}

struct TrainResponse: Codable, Identifiable {
    let n, s: [NS]?
    let id: String?
    let lastUpdate: String?
    let location: [Double]?
    let name: String?
    let routes: [String]?
    let stops: [String: [Double]]?
    
    enum CodingKeys: String, CodingKey {
        case n = "N"
        case s = "S"
        case id
        case lastUpdate = "last_update"
        case location, name, routes, stops
    }
}

struct NS: Codable {
    let route: String?
    let time: String?
}

   class API: ObservableObject {
    
    @Published var storedData = GTFSObject(data: [], updated: nil)
    
    func loadData() {
        guard let url = URL(string: "https://api.jsonbin.io/b/60f8f8be99892a4ae9a79828") else {
            print("Your API end point is Invalid")
            return
        }
        let request = URLRequest(url: url)
        URLSession.shared.dataTask(with: request) { data, response, error in
            if let data = data {
                if let response = try? JSONDecoder().decode(GTFSObject.self, from: data) {
                   // print("\n-------> response: \(response)\n")
                    DispatchQueue.main.async {
                        self.storedData.data = response.data
                    }
                    return
                }
            }
        }.resume()
    }
}

struct ContentView: View {
    
    @StateObject var api = API()
    
    var body: some View {
        Text("yo")
        List {
            ForEach(api.storedData.data, id: \.id) { train in
                Text(train.name ?? "no name")
            }
        }
        .onAppear { api.loadData() }
    }
}

答案 1 :(得分:0)

根据你的 JSON 你的 Swift Struct DataModel 应该是这样的。

struct GTFSObject: Codable {
    let data : [TrainResponse]?
    let updated : String?
    
    enum CodingKeys: String, CodingKey {
        
        case data = "data"
        case updated = "updated"
    }
}

struct TrainResponse : Codable {
    let n : [N]?
    let s : [S]?
    let id : String?
    let last_update : String?
    let location : [Double]?
    let name : String?
    let routes : [String]?
    let stops : Stops?
    
    enum CodingKeys: String, CodingKey {
        
        case n = "N"
        case s = "S"
        case id = "id"
        case last_update = "last_update"
        case location = "location"
        case name = "name"
        case routes = "routes"
        case stops = "stops"
    }
}

struct N : Codable {
    let route : String?
    let time : String?
    
    enum CodingKeys: String, CodingKey {
        
        case route = "route"
        case time = "time"
    }
}

struct S : Codable {
    let route : String?
    let time : String?
    
    enum CodingKeys: String, CodingKey {
        
        case route = "route"
        case time = "time"
    }
}

struct Stops : Codable {
    let a63 : [Double]?
    
    enum CodingKeys: String, CodingKey {
        
        case a63 = "A63"
    }
}