I am having a bit of trouble trying to get data return from the MTA API (subway system in New York) (Link Here). The data is in GTFS, so I am using a parser that is set up in a virtual environment that converts the data to JSON and sends it to me (sample JSON posted later in post). Basically, the decoding of GTFS to JSON is completed.
Now that I have the JSON data, I need to get it to display in my app. I have dealt with APIs before, but not one with this many "layers".
The question is: how should I structure my code on the Swift side so I can access each of the datapoints sent in the JSON?
Below is what I am working. The data is not displaying at all in my ContentView. I am guessing this has to do with my model being structured incorrectly, therefore the data I am attempting to display not actually being there:
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()
}
}
Below is a sample of the JSON - It repeats what you see below hundreds of times, but I closed it off in the sample as if it were a complete JSON object (to make it easier to debug)
{
"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"
}
I am really at wits end here. Any thoughts on how my code should be restructured to return the data correctly?
I know I should be nesting the data (e.g. self.routes = data["N"].etc.etc but I can't quite seem to figure out how to structure my code so I can do that. Once that is done, I should have no trouble displaying the data in the app.. just need to get the data into the app first!
EDIT - Adding code for 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 I have updated the data models. They now look like the following:
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
}