Track is a high-performance, native iOS application designed for the modern New York City commuter. Built strictly with modern Swift and SwiftUI architectures, Track parses real-time GTFS and SIRI data to deliver live countdowns, live vehicle tracking, and highly contextual alerts directly to your phone, Lock Screen, and Dynamic Island.
This application is powered by the Track API Marketplace, a custom Python-based proxy that handles heavy lifting, caching, and Protobuf decoding, allowing the iOS client to remain blistering fast.
Track follows strict MVVM (Model-View-ViewModel) paradigms bound to a persistent SwiftData layer.
graph TD
UI[SwiftUI Views & MapKit] --> VM[ViewModels]
VM --> Repo[TransitRepository]
Repo --> API[TrackAPI Network Client]
Repo --> Cache[(SwiftData On-Device Cache)]
API -->|API Requests| Prox[TrackBackend FastAPI Proxy]
Prox -->|Protobuf/SIRI| MTA[MTA GTFS & OBA Data Feeds]
- UI Framework: SwiftUI (iOS 18+)
- Mapping Engine: Apple MapKit natively rendering custom
MapAnnotationoverlays and decoded Google Polylines. - Persistence Layer: SwiftData container shared seamlessly across App Groups.
- System Extensibility:
- WidgetKit: Configurable Lock Screen and Home Screen Accessory widgets.
- ActivityKit: Live navigation tracking via the Dynamic Island.
- AppIntents: Exposing native backend triggers directly to Siri.
- Machine Learning: On-device CoreML heuristic tracking for analyzing and suggesting commute behaviors.
| Feature | Technical Implementation |
|---|---|
| Unified "Nearby" Dashboard | Fetches aggregated Subway, Bus, LIRR, and MNR data endpoints, instantly routing to heavily modularized, swipe-friendly SwiftUI Carousels grouped by route. |
| Real-Time GPS Mapping | Tracks active buses via SIRI data. Plots buses dynamically tracking along polylines over Apple Maps using Core Animation for smooth translation. |
| GO Mode (Hands-Free) | In-transit navigation state tracking. Removes passed stops, tracks distance remaining via CoreLocation, and projects live ETAs to the Dynamic Island. |
| Smart Suggestions ML | Uses on-device predictive modeling tracking prior TripLog entries in SwiftData. It learns your daily habits (e.g., checking the L-train on Tuesdays at 9 AM) and surfaces relevant cards automatically. |
| Pervasive Widgets | TimelineProvider dynamically pings TrackBackend schedules, bypassing the app entirely to ensure live counts appear flawlessly beneath your clock. |
| Live Accessibility | Constantly polls the MTA elevator-escalator real-time outage feeds, appending "Out of Service" badges explicitly to your saved stations. |
The iOS application avoids massive monolithic single-file implementations in favor of tightly decoupled modules:
Track/
βββ TrackApp.swift # Entry execution & App Group coordination
βββ Models/
β βββ Station.swift # SwiftData persistable Subway Stations
β βββ CommutePattern.swift # Machine Learning statistical heuristics
β βββ BusModels.swift # Decodable generic schemas bound to FastAPI
βββ Views/
β βββ HomeView.swift # The primary MapKit / BottomSheet split
β βββ Components/ # Agnostic sub-views (Rows, Cards, Sheets)
βββ ViewModels/
β βββ HomeViewModel.swift # Presentation logic & MapKit region binding
βββ Repositories/
β βββ TransitRepository.swift # State aggregation & Offline-first caching bridge
βββ Network/
β βββ TrackAPI.swift # URLSession endpoint mappers
βββ Services/
β βββ LocationManager.swift # CoreLocation wrappers
β βββ SmartSuggester.swift # Processing layer for statistical ML inferences
βββ Widgets/
βββ TrackWidget.swift # Standalone WidgetKit execution targets
The Track environment requires configuring both the Xcode workspace and the local Python FastAPI proxy.
- Open up
Track.xcodeprojin Xcode 16+. - Within the Signing & Capabilities tab of the project target, assign your personal or team Apple Developer account.
- Explicitly enable App Groups. Ensure the group identifier is identical on both the
TrackandTrackWidgetstarget (e.g.group.com.yourname.track). - Select an iOS 18+ deployment target and hit Build (
Cmd+B).
By default, the iOS TrackAPI.swift points to the production Render.com remote instance.
To bridge directly to your local machine:
- Fire up the backend locally (see:
TrackBackend/README.md). - Inside the iOS App -> Navigate to Settings -> Developer Tools.
- Toggle the Use Localhost API target and save changes. The app will instantaneously target
127.0.0.1:8000.
This application was engineered strictly avoiding intrusive third-party metrics or telemetry.
- Telemetry Restrictions: Analytics strictly utilize Apple's native opt-in anonymized telemetry channels. No external crash reporting frameworks (Firebase, Crashlytics) are deployed.
- On-Device Logging: Your
CommutePatterndata never touches the cloud. Data is aggregated locally intoSwiftDataSQLite schemas locked beneath standard iOS security partitions. - Data Transit: All network requests to
TrackBackendtransmit over pure TLS-encryptedHTTPS.
Looking to explore the exact JSON payloads that power this frontend interface? π Read the TrackBackend API Marketplace Documentation
__
A high-performance Python proxy API for the Track iOS app. It ingests raw MTA data (GTFS-Realtime Protobuf and JSON feeds) and serves pristine, standardized JSON to the iOS client.
- Python 3.11+
- FastAPI β Lightning-fast async web framework
- Pydantic β Data validation and settings management
- HTTPX β Async HTTP client for MTA feeds
- gtfs-realtime-bindings β Protobuf decoder for GTFS-Realtime feeds
cd TrackBackend
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
uvicorn app.main:app --reloadThe API will be available at http://127.0.0.1:8000. Auto-generated docs are at /docs.
pip install pytest pytest-asyncio httpx
python -m pytest tests/ -vWelcome to the Track API Marketplace, a high-performance Python proxy API powering the Track NYC Transit iOS app.
The API merges highly disparate MTA feeds (GTFS-Realtime Protobuf, GTFS Static JSON/CSV, SIRI, OBA) into unified, pristine JSON schemas ready for mobile clients.
Currently, no API key is required. However, for future enterprise usage, include:
Authorization: Bearer <YOUR_API_KEY>Returns polylines and colors for ALL subway lines. Used to render the full NYC system map.
- Response
200 OK:{"lines": [{"mode": "subway", "route_id": "L", "name": "L", "color_hex": "A7A9AC", "polylines": ["..."]}]}
Returns all stations with their coordinates and routes served.
- Response
200 OK:{"count": 472, "stations": [{"id": "L01", "name": "8 Av", "lat": 40.74, "lon": -74.0, "routes": ["L"]}]}
Returns stations strictly within a GPS radius.
- Parameters:
lat(required),lon(required),radius(optional) - Response
200 OK:{"count": 2, "stations": [...]}
Returns the full geometry (polylines) and ordered list of stops for a single subway line.
- Response
200 OK:{"route_id": "L", "polylines": [...], "stops": [...], "directions": [...]}
Live countdown arrivals for a single subway line.
- Response
200 OK:[{"route_id": "L", "station": "L01", "station_name": "8 Av", "direction": "N", "destination": "8 Av", "minutes_away": 3, "arrival_ts": 1700000000, "status": "On Time"}]
Live countdown arrivals for the Long Island Rail Road or Metro-North.
- Response
200 OK: Array ofTrackArrivalobjects matching the subway schema.
Polylines and brand colors for all branched routes.
- Response
200 OK:{"lines": [...]}
Polyline geometry for a specific branch (e.g. LIRR_9 for Port Washington).
Returns all MTA bus routes.
- Response
200 OK:[{"id": "MTA NYCT_B63", "short_name": "B63", "long_name": "Atlantic Av", "color": "0039A6"}]
Returns all ordered stops for a bus route.
- Response
200 OK:[{"id": "MTA_308214", "name": "5 Av / Union St", "lat": 40.67, "lon": -73.98, "direction": "0"}]
Real-time SIRI bus arrivals at a targeted stop.
- Response
200 OK:[{"route_id": "...", "vehicle_id": "...", "status_text": "Approaching", "expected_arrival": "2026-...", "distance_meters": 150}]
Live GPS tracking positions, bearing, and distance for all active buses on a route.
- Response
200 OK:[{"vehicle_id": "...", "lat": 40.6, "lon": -73.9, "bearing": 180.0, "next_stop": "...", "status_text": "at stop"}]
Returns encoded polylines to draw the bus path on Apple/Google Maps.
The flagship endpoint. Combines Subway, Bus, LIRR, and MNR into a single grouped response, sorted by the absolute fastest arriving trains or buses near the user. Returns cleanly formatted JSON designed for multi-tab UI cards.
- Parameters:
lat(required),lon(required),radius(optional, default 500m),mode(optional filter) - Response
200 OK:
[
{
"route_id": "L",
"display_name": "L",
"color_hex": "A7A9AC",
"mode": "subway",
"directions": [
{
"headsign": "8 Av",
"direction": "N",
"arrivals": [
{ "minutes_away": 2, "destination": "8 Av", "status": "On Time" }
]
}
]
}
]A flattened version of the above, returning purely the nearest raw arrivals.
Real-time service alerts (delays, planned work) matching the MTA Service Status tracker.
- Parameters:
mode(optional) - Response
200 OK:[{"route_id": "L", "title": "Planned Work", "description": "...", "severity": "MODERATE"}]
Live list of broken elevators and escalators across the system.
Logs an interaction metric when a user tracks a route.
- Parameters:
route_id,mode,interaction_type
Returns the most interacted routes across the entire platform.
A heavy, highly-cached endpoint hit once by the iOS app to download route shapes, branding, stops, and colors into local CoreData to avoid massive repeated network fetches.
All behavior is controlled by settings.json in the project root. The iOS app fetches /config on launch to receive dynamic settings.
Important: Replace the
mta_api_keyvalue"YOUR_KEY_HERE"insettings.jsonwith your actual MTA API key before deploying. Never commit real API keys to source control.
TrackBackend/
βββ app/
β βββ main.py # Application entry point
β βββ config.py # Settings loader (Pydantic settings)
β βββ models.py # Data models (Pydantic schemas)
β βββ services/
β β βββ mta_client.py # Handles raw MTA calls (Protobuf/XML)
β β βββ bus_client.py # OBA + SIRI bus API client (stops, arrivals, vehicles, shapes)
β β βββ data_cleaner.py # Converts raw data to clean JSON
β βββ routers/
β βββ subway.py # Endpoints for subway lines
β βββ bus.py # Endpoints for bus (routes, stops, live, vehicles, shapes)
β βββ nearby.py # Unified nearby transit endpoint
β βββ lirr.py # Endpoints for Long Island Rail Road
β βββ status.py # Endpoints for Alerts/Elevators
βββ tests/
β βββ test_nearby.py # Tests for /nearby endpoint
β βββ __init__.py
βββ settings.json # THE MASTER CONFIG FILE
βββ requirements.txt # Dependencies
βββ Dockerfile # Container for cloud deployment
βββ README.md
The iOS app displays subway, LIRR, and Metro-North route lines on the map. This data comes from the /static/bundle endpoint which parses GTFS static files.
Subway GTFS data is included in the repo at app/data/subway/ (shapes, stops, etc.).
To enable LIRR and Metro-North route lines on the map, download GTFS static data from MTA and place it in these directories:
app/data/lirr/gtfslirr/
βββ shapes.txt # Route polyline coordinates
βββ trips.txt # Trip definitions (links routes to shapes)
βββ stops.txt # Station locations
βββ routes.txt # Route definitions
app/data/metro_north/gtfsmnr/
βββ shapes.txt
βββ trips.txt
βββ stops.txt
βββ routes.txt
Download GTFS feeds from: https://new.mta.info/developers
Once the data is in place:
- Restart the backend server
- The
/static/bundleendpoint will include LIRR routes (prefixedLIRR_*) and MNR routes (prefixedMNR_*) - The iOS app will display these routes as dashed lines on the system map
| Source | Protocol | Usage |
|---|---|---|
| MTA GTFS-Realtime | Protobuf | Subway & LIRR real-time arrivals |
| MTA GTFS Static | CSV | Route shapes for map display |
| MTA SIRI | JSON | Bus arrivals, vehicle positions |
| MTA OBA | JSON | Bus routes, stops, route shapes |
| MTA Alerts | JSON | Service alerts, elevator status |
| Supabase REST | REST | User data, analytics, schedules |
| Supabase Storage | S3 | GTFS static data archives |
The backend manages MTA's quarterly GTFS static updates automatically. No manual steps required.
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β STARTUP β
β β
β 1. Download 6 .tar.gz archives from Supabase Storage β
β (ETag caching β skips unchanged files) β
β 2. If Supabase is down β use Docker-bundled files (fallback) β
β 3. transit_schedule.db always comes from Docker image β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β BACKGROUND (every 24 hours) β
β β
β 1. HEAD requests to MTA GTFS URLs (~0.6s, checks Last-Modified)β
β 2. If MTA published new data: β
β β Download + extract .zip feeds β
β β Rebuild transit_schedule.db (atomic swap) β
β β Upload changed archives to Supabase β
β β Clear all @lru_cache'd data (zero-downtime refresh) β
β 3. If no updates β do nothing β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
| Archive | Contents | Compressed |
|---|---|---|
subway_core.tar.gz |
shapes.txt, trips.txt, stops.txt, shape_stops.json | 1.0 MB |
subway_routes.tar.gz |
routes.txt | <1 KB |
subway_supplemented.tar.gz |
Full supplemented GTFS for iOS bundle | 18 MB |
lirr.tar.gz |
LIRR GTFS static feed | 1.8 MB |
mnr.tar.gz |
Metro-North GTFS static feed | 1.9 MB |
bus_config.tar.gz |
Bus route tag overrides | <1 KB |
transit_schedule.db(863 MB) stays Docker-bundled β too large for Supabase free tier.
| Method | Path | Description |
|---|---|---|
| GET | /data/status |
Data group availability + GTFS feed freshness |
| POST | /data/refresh |
Manually trigger GTFS update check |
| POST | /data/refresh?full=true |
Check all feeds including bus (slower) |
cd TrackBackend
# 1. Download fresh GTFS from MTA
python scripts/download_gtfs.py
# 2. Rebuild the schedule database
python scripts/ingest_gtfs.py
# 3. Upload to Supabase Storage
export SUPABASE_SERVICE_KEY='your-service-role-key'
python scripts/upload_gtfs_to_supabase.py
# 4. Generate iOS static bundle (optional)
python scripts/generate_static_bundle.py| Variable | Default | Description |
|---|---|---|
SUPABASE_SERVICE_KEY |
β | Required for Supabase Storage uploads |
GTFS_BUCKET |
gtfs-data |
Supabase Storage bucket name |
GTFS_CHECK_INTERVAL |
86400 (24h) |
Seconds between automatic MTA checks |
The backend connects to Supabase for user analytics and data sync.
Set these environment variables (or use defaults for development):
export SUPABASE_URL=https://octpebjxadbufiplgjqg.supabase.co
export SUPABASE_KEY=sb_publishable_lAEZ_x8O4vjdGaw-I-QUMg_oS5iWKIn| Table | Purpose | Used By |
|---|---|---|
profiles |
User accounts from Apple Sign-In | iOS SupabaseManager |
route_interactions |
Analytics - what routes are popular | iOS HomeViewModel, Backend analytics router |
schedules |
Widget activation schedules | iOS WidgetSchedules, SyncManager |
commute_patterns |
Smart suggestions based on habits | iOS SmartSuggester |
| Method | Path | Description |
|---|---|---|
| GET | /analytics/popular |
Get most popular routes |
| POST | /analytics/log |
Log a route interaction |
curl "http://localhost:8000/analytics/popular?mode=subway&limit=5"Response:
{
"popular_routes": [
{"route_id": "L", "mode": "subway", "clicks": 42, "tracks": 15, "total": 57},
{"route_id": "7", "mode": "subway", "clicks": 38, "tracks": 12, "total": 50}
],
"count": 2
}