Finished wiring up loadTrainStatus()

Did a little code formatting after that cause Xcode is weird
This commit is contained in:
Tony Bark 2026-08-22 17:49:41 -04:00
parent 3eb2018a8e
commit f20d49f075
5 changed files with 62 additions and 45 deletions

View file

@ -9,56 +9,60 @@ enum AmtrakError: Error {
struct AmtrakClient {
var decoder = JSONDecoder()
let stationBaseURL = "https://api.amtraker.com/v3/stations"
let trainsBaseURL = "https://api.amtraker.com/v3/trains"
let trainsBaseURL = "https://api.amtraker.com/v3/trains"
let staleBaseURL = "https://api.amtraker.com/v3/stale"
func fetchTrain(number trainid: String) async throws -> TrainResponse {
try await get("\(trainsBaseURL)/\(trainid)")
}
func fetchAllTrains() async throws -> TrainResponse {
try await get(trainsBaseURL)
}
func fetchAllTrainStatus(num trainNum: String) async throws -> [Train] {
let reponse = try await fetchTrain(number: trainNum)
return reponse.values.flatMap(\.self)
}
func fetchStation(code stationid: String) async throws -> StationResponse {
try await get("\(stationBaseURL)/\(stationid)")
}
func fetchAllStations() async throws -> StationResponse {
try await get(stationBaseURL)
}
func trainNumber(fromTrainId trainId: String) -> String {
trainId.split(separator: "-").first.map(String.init) ?? trainId
}
func fetchStationTrainNumbers(stationCode code: String) async throws -> [String] {
let reponse = try await fetchStation(code: code)
guard let meta = reponse[code] else {
throw AmtrakError.stationNotFound(code)
}
return Array(Set(meta.trains.map(trainNumber(fromTrainId:))))
}
func get<T: Decodable>(_ urlString: String) async throws -> T {
guard let url = URL(string: urlString) else {
throw AmtrakError.invalidURL(urlString)
}
let (data, reponse) = try await URLSession.shared.data(from: url)
guard let http = reponse as? HTTPURLResponse else {
throw AmtrakError.invalidResponse
}
guard (200..<300).contains(http.statusCode) else {
guard (200 ..< 300).contains(http.statusCode) else {
throw AmtrakError.httpStatus(http.statusCode)
}
return try decoder.decode(T.self, from: data)
}
}

View file

@ -1,5 +1,5 @@
import SwiftCrossUI
import DefaultBackend
import SwiftCrossUI
@main
struct AmtrakStatusApp: App {

View file

@ -2,12 +2,12 @@ import SwiftCrossUI
struct ContentView: View {
@State var client = AmtrakClient()
@State var stationCode = "ASD" // Ashland, VA
@State var rows: [TrainStatusRow] = []
@State var isLoading = false
@State var errorMessage: String?
var body: some View {
VStack {
// TODO: Add pre-selected options in the form of buttons like the original
@ -15,28 +15,25 @@ struct ContentView: View {
Text("Station Code:")
TextField("e.g. ASD", text: $stationCode)
.frame(width: 100)
Button("Refresh")
{
Button("Refresh") {
Task {
await loadTrainStatus(forStationCode: normalizedStationCode)
}
}.disabled(normalizedStationCode.isEmpty || isLoading)
}.padding(10)
if let errorMessage {
Text(errorMessage).padding(10)
}
Table(rows)
{
Table(rows) {
TableColumn("Number", value: \TrainStatusRow.trainNum)
TableColumn("Number", value: \TrainStatusRow.routeName)
TableColumn("On Time", value: \TrainStatusRow.onTime)
TableColumn("Platform", value: \TrainStatusRow.platform)
}.overlay(alignment: .bottomTrailing)
{
}.overlay(alignment: .bottomTrailing) {
if isLoading {
ProgressView()
.padding(10)
@ -49,39 +46,53 @@ struct ContentView: View {
}
}
}
var normalizedStationCode: String {
stationCode.trimmingCharacters(in: .whitespacesAndNewlines).uppercased()
}
func loadTrainStatus(forStationCode code: String) async {
guard !code.isEmpty else {
return
}
isLoading = true
errorMessage = nil
defer { isLoading = false }
do {
let trainNumbers = try await client.fetchStationTrainNumbers(
stationCode: code
stationCode: code,
)
guard !trainNumbers.isEmpty else {
errorMessage = "Station \(code) has no trains listed right now"
rows = []
return
}
var newRows: [TrainStatusRow] = []
for number in trainNumbers {
// let trains = await client.fet
let trains = try await client.fetchAllTrainStatus(num: number)
for train in trains {
let platform = train.stations?.first(
where: { $0.code == code
},
)?.platform
newRows.append(TrainStatusRow(
trainID: train.trainID,
trainNum: train.trainNum,
routeName: train.routeName,
onTime: train.trainTimely.isEmpty ? "" : train.trainTimely,
platform: (
platform?.isEmpty == false,
) ? platform! : "",
))
}
}
} catch AmtrakError.stationNotFound(let code)
{
rows = newRows
} catch let AmtrakError.stationNotFound(code) {
errorMessage = "Station \(code) not foudn"
rows = []
} catch {

View file

@ -66,7 +66,9 @@ typealias TrainResponse = [String: [Train]]
typealias StationResponse = [String: StationMeta]
struct TrainStatusRow: Identifiable {
var id: String { trainID }
var id: String {
trainID
}
var trainID: String
var trainNum: String