| Code | What It Does | How It Does It |
| ▶ IMPORTS | | |
| import Foundation
import CoreLocation
import MapKit | Framework imports | Imports Foundation, CoreLocation, MapKit. |
| ▶ MODEL | | |
| struct SubwayVehicle: Identifiable {
let id: String
let route: String
let tripId: String // GTFS trip_id — stable across stops for one run
let stopId: String // GTFS stop_id of next/current stop
let latitude: Double
let longitude: Double
let bearing: Double
/// 0 = INCOMING_AT, 1 = STOPPED_AT, 2 = IN_TRANSIT_TO
let currentStatus: Int
/// Unix timestamp of last detected movement (0 if absent)
let timestamp: UInt64
} | `SubwayVehicle` struct | Defines the `SubwayVehicle` struct. Conforms to Identifiable. |
| ▶ MAP ANNOTATION | | |
| final class SubwayVehicleAnnotation: NSObject, MKAnnotation {
let vehicle: SubwayVehicle
@objc dynamic var coordinate: CLLocationCoordinate2D {
CLLocationCoordinate2D(latitude: vehicle.latitude, longitude: vehicle.longitude)
}
var title: String? { vehicle.route }
init(_ v: SubwayVehicle) { self.vehicle = v; super.init() }
} | `SubwayVehicleAnnotation` class | Defines the `SubwayVehicleAnnotation` class. Conforms to NSObject, MKAnnotation. |
| ▶ MINIMAL PROTOBUF READER | | |
| //
// GTFS-RT uses only wire types 0 (varint), 1 (64-bit), 2 (length-delimited), 5 (32-bit).
// We only need to read length-delimited fields (strings and embedded messages)
// and skip everything else. | Documentation comment | Describes the following declaration. |
| private struct ProtoReader {
let data: Data
var pos: Int
init(_ data: Data) {
// Swift Data slices preserve the original index range (startIndex != 0).
// All our pos-based access starts at 0, so force a copy when the slice
// is offset — otherwise data[0] is out of bounds and Foundation traps.
self.data = data.startIndex == 0 ? data : Data(data)
self.pos = 0
}
var hasMore: Bool { pos < data.count }
// Read a little-endian base-128 varint.
mutating func varint() -> UInt64 {
var result: UInt64 = 0
var shift = 0
while pos < data.count {
let b = data[pos]; pos += 1
result |= UInt64(b & 0x7F) << shift
if b & 0x80 == 0 { break }
shift += 7
}
return result
}
// Read (fieldNumber, wireType); returns nil at end-of-buffer.
mutating func tag() -> (f: Int, w: Int)? {
guard hasMore else { return nil }
let v = varint()
return (f: Int(v >> 3), w: Int(v & 0x7))
}
// Read a length-prefixed byte slice (wire type 2).
// Safe: guards against UInt64→Int overflow (malformed or non-protobuf input)
// and against pos+len exceeding the buffer.
mutating func bytes() -> Data? {
let raw = varint()
// UInt64 values that don't fit in a non-negative Int would indicate corrupt data.
guard raw <= UInt64(Int.max) else { pos = data.count; return nil }
let len = Int(raw)
// Guard against integer overflow in pos+len and out-of-bounds slice.
guard len <= data.count - pos else { pos = data.count; return nil }
defer { pos += len }
return data[pos ..< pos + len]
}
// Read a UTF-8 string (wire type 2).
mutating func string() -> String? {
guard let d = bytes() else { return nil }
return String(data: d, encoding: .utf8)
}
// Skip the value for the current wire type without parsing it.
mutating func skip(wire w: Int) {
switch w {
case 0: _ = varint()
case 1: pos = min(pos + 8, data.count)
case 2: _ = bytes()
case 5: pos = min(pos + 4, data.count)
default: pos = data.count // unknown wire type → abandon parsing
}
}
} | `ProtoReader` struct | Defines the `ProtoReader` struct. |
| ▶ GTFS-RT PARSING HELPERS | | |
| private struct VehicleRecord {
var routeId = ""
var tripId = ""
var stopId = ""
// Per GTFS-RT spec: if current_status field is absent, IN_TRANSIT_TO is assumed.
var currentStatus = 2 // 0=INCOMING_AT, 1=STOPPED_AT, 2=IN_TRANSIT_TO
var hasStatus = false // true if field 6 was present in the message
var timestamp: UInt64 = 0 // Unix seconds of last detected movement (field 5)
} | `VehicleRecord` struct | Defines the `VehicleRecord` struct. |
| /// Extracts trip_id (field 1) and route_id (field 5) from a serialised TripDescriptor.
private func parseTripDescriptor(_ data: Data) -> (routeId: String, tripId: String) {
var r = ProtoReader(data)
var routeId = ""
var tripId = ""
while let (f, w) = r.tag() {
switch (f, w) {
case (1, 2): tripId = r.string() ?? ""
case (5, 2): routeId = r.string() ?? ""
default: r.skip(wire: w)
}
}
return (routeId: routeId, tripId: tripId)
} | Documentation comment | Describes the following declaration. |
| /// Extracts trip.route_id, stop_id, and current_status from a serialised VehiclePosition.
///
/// MTA NYCT field layout (verified by raw proto dump):
/// field 1, wire 2 → trip (TripDescriptor)
/// field 3, wire 0 → current_stop_sequence (varint)
/// field 5, wire 0 → timestamp (Unix seconds)
/// field 6, wire 0 → current_status 0=INCOMING_AT 1=STOPPED_AT 2=IN_TRANSIT_TO
/// field 7, wire 2 → stop_id (string)
private func parseVehiclePosition(_ data: Data) -> VehicleRecord? {
var r = ProtoReader(data)
var rec = VehicleRecord()
while let (f, w) = r.tag() {
switch (f, w) {
case (1, 2): // trip: TripDescriptor
if let d = r.bytes() {
let parsed = parseTripDescriptor(d)
rec.routeId = parsed.routeId
rec.tripId = parsed.tripId
}
case (5, 0): // timestamp (Unix seconds of last detected movement)
rec.timestamp = r.varint()
case (6, 0): // current_status
rec.currentStatus = Int(r.varint())
rec.hasStatus = true
case (7, 2): // stop_id
rec.stopId = r.string() ?? ""
default:
r.skip(wire: w)
}
}
return (!rec.routeId.isEmpty && !rec.stopId.isEmpty) ? rec : nil
} | Documentation comment | Describes the following declaration. |
| /// Parses a full GTFS-RT FeedMessage binary blob and returns all vehicle records.
private func parseFeedMessage(_ data: Data) -> [VehicleRecord] {
var r = ProtoReader(data)
var records = [VehicleRecord]()
while let (f, w) = r.tag() {
guard f == 2, w == 2 else { r.skip(wire: w); continue } // entity field
guard let entityData = r.bytes() else { continue }
// Parse FeedEntity: look for field 4 = VehiclePosition.
var er = ProtoReader(entityData)
while let (ef, ew) = er.tag() {
if ef == 4, ew == 2 {
if let vpData = er.bytes(), let rec = parseVehiclePosition(vpData) {
records.append(rec)
}
} else {
er.skip(wire: ew)
}
}
}
return records
} | Documentation comment | Describes the following declaration. |
| ▶ SERVICE | | |
| struct SubwayVehicleService {
/// MTA GTFS-RT feed URLs — one per line group.
/// No API key required (api-endpoint.mta.info, 2024+).
private static let feedURLs: [String] = [
"https://api-endpoint.mta.info/Dataservice/mtagtfsfeeds/nyct%2Fgtfs", // 1 2 3 4 5 6 7
"https://api-endpoint.mta.info/Dataservice/mtagtfsfeeds/nyct%2Fgtfs-ace", // A C E
"https://api-endpoint.mta.info/Dataservice/mtagtfsfeeds/nyct%2Fgtfs-bdfm", // B D F M
"https://api-endpoint.mta.info/Dataservice/mtagtfsfeeds/nyct%2Fgtfs-g", // G
"https://api-endpoint.mta.info/Dataservice/mtagtfsfeeds/nyct%2Fgtfs-jz", // J Z
"https://api-endpoint.mta.info/Dataservice/mtagtfsfeeds/nyct%2Fgtfs-l", // L
"https://api-endpoint.mta.info/Dataservice/mtagtfsfeeds/nyct%2Fgtfs-nqrw", // N Q R W
"https://api-endpoint.mta.info/Dataservice/mtagtfsfeeds/nyct%2Fgtfs-sir", // Staten Island
]
/// Fetches all MTA subway feeds concurrently and returns every active vehicle
/// whose current stop can be resolved from `stopCoords`.
/// The caller is responsible for filtering the result to the visible map region.
static func fetchAll(stopCoords: [String: CLLocationCoordinate2D]) async -> [SubwayVehicle] {
let cfg = URLSessionConfiguration.ephemeral
cfg.timeoutIntervalForRequest = 10
cfg.timeoutIntervalForResource = 15
let session = URLSession(configuration: cfg)
defer { session.invalidateAndCancel() }
// Fetch all 8 feeds in parallel.
var records = [VehicleRecord]()
await withTaskGroup(of: [VehicleRecord].self) { group in
for urlString in feedURLs {
guard let url = URL(string: urlString) else { continue }
group.addTask {
guard let (data, _) = try? await session.data(from: url) else { return [] }
return parseFeedMessage(data)
}
}
for await feedRecords in group {
records.append(contentsOf: feedRecords)
}
}
let s0 = records.filter { $0.currentStatus == 0 }.count
let s1 = records.filter { $0.currentStatus == 1 }.count
let s2 = records.filter { $0.currentStatus == 2 }.count
let sX = records.filter { $0.currentStatus != 0 && $0.currentStatus != 1 && $0.currentStatus != 2 }.count
let present = records.filter { $0.hasStatus }.count
print("🚇 MTA feeds: \(records.count) vehicles — INCOMING_AT:\(s0) STOPPED_AT:\(s1) IN_TRANSIT_TO:\(s2) other:\(sX) | field6_present:\(present)/\(records.count)")
// Resolve each vehicle's coordinate from its stop ID.
return records.compactMap { rec -> SubwayVehicle? in
let stopID = rec.stopId
// Stop IDs end in N (northbound) or S (southbound); strip to get the base ID.
let isN = stopID.hasSuffix("N")
let isS = stopID.hasSuffix("S")
let bearing: Double = isS ? 180 : 0
let baseID = (isN || isS) && stopID.count > 1 ? String(stopID.dropLast()) : stopID
guard let coord = stopCoords[baseID] else { return nil }
return SubwayVehicle(id: UUID().uuidString, route: rec.routeId,
tripId: rec.tripId,
stopId: rec.stopId,
latitude: coord.latitude,
longitude: coord.longitude,
bearing: bearing,
currentStatus: rec.currentStatus,
timestamp: rec.timestamp)
}
}
} | `SubwayVehicleService` struct | Defines the `SubwayVehicleService` struct. |
| // subwayLineUIColor(for:) and subwayLineTextUIColor(for:) are defined in NearbyMapView.swift | Documentation comment | Describes the following declaration. |