我在尝试从 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
}
答案 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"
}
}