Tunnel Rat

A native iOS app for navigating New York's Subways, designed, built, and shipped to the app store.

Context

What

A native iOS app focused exclusively on the New York subway—free, reliable, private, and accessible at its core.

Why

There's a glut of transit apps—but they try to do too much, and not very well. I aimed to fill that gap with an app that understands how New Yorkers actually think about the system.

Who

Just me, across design, code, and content.

When

Built v1.0 in about 2 months, from April – June 2026, with continued iteration and improvements on an ongoing basis.

Results

Shipped in July 2026 to ~150 initial installs, slowly growing week over week.

Toolkit

Composition

  • Figma
  • Claude Code
  • Framer Motion
  • Xcode
  • Warp
  • Symbols / Composer

Infrastructure

  • Supabase
  • Cloudflare
  • Vercel
  • Claude API
  • Swift
  • React
  • Three.js
  • TypeScript

Workflow

  • GitHub
  • Linear
  • Sentry
  • Slack

Specs & Details

Foundations

A native SwiftUI transit app with the MTA's schedule in local SQLite, live Protobuf feeds behind a Cloudflare edge cache, a thin Supabase backend for what can't run on-device, and every design value single-sourced from Figma in one tokens file.

On device

Tunnel Rat

Native SwiftUI on a strict MVVM split—views render, ViewModels and Services do the work.

Local SQLite

The MTA's entire schedule, plus favorites, saved places, and settings. None of it leaves the phone.

Edge

Cloudflare Workers

One cached fetch shared by every rider, refreshed in the background. Falls back to the raw MTA endpoints when a layer goes down.

Backend

MTA GTFS-RT

Live arrivals as Protobuf feeds, parsed on device.

Supabase

Edge functions and scheduled jobs for what can't run on-device: AI alert summaries, push, reminders. Per-user calls skip the cache.

Design system

Figma variables → DesignTokens.swift → every view. One token change propagates app-wide, rather than being re-decided component by component.

iOS App

Tunnel Rat is built around the glance, not the session—it opens on your stops, live arrivals already on screen, because the only question is whether to run for the stairs. It does less on purpose: no ads, no account, no tracking, and it tells you when the data is stale instead of bluffing.

Core IA: The app features just three primary tabs: Favorites, Map, and Alerts, with search as a secondary option. Users see the latest times at their favorites the second they open the app, for quick reference.
The Favorites list, showing live countdowns for two saved stations
A pinned train's timeline, listing every stop still ahead of it
Realtime Arrivals: MTA GTFS-Realtime snapshots, pulled fresh every 45 seconds. View a timeline of any arrival, including every stop still ahead of it. Pin it and it docks in a banner so you keep that exact train.
Countdowns & Layout: Train times update and pulse smoothly, resembling a live ticker board. Switching platforms or sheet detents feels similarly buttery, inspiring confidence in what you’re using.
The full subway map, every station drawn in its real route colors
A trip in progress, with step-by-step guidance in a sheet
Map & Trips: Every station drawn over MapKit in the real route colors—tap a stop and a sheet slides up with its live arrivals. Routing runs entirely on device: a graph built from the local schedule, with a service-alert overlay applied as it solves, so disruptions actually shape the route.
Service alerts for a single line, rewritten in plain English
Line alerts arriving as push notifications on the lock screen
Enriched Alerts: MTA alerts arrive as all-caps jargon, so a server-side pass rewrites each one in plain English before it ever reaches the app. Subscribe to your lines and that same pipeline pushes them the moment they go live.
Every stop a line serves, in order
The map zoomed in, stations labelled with the lines they carry
A station complex as one stop, with arrivals across all its lines
Enriched Stations: We derive what a line actually serves: every real trip pattern deduped, then merged into one order that can't contradict a real train's sequence. Stations model three levels—platform, station, complex—so Union Square is one big stop, not three separate ones.
Trip preferences, including a switch that requires stations with elevators
A route's accessibility breakdown, with live elevator status per stop
The Favorites list with Increase Contrast turned on
Accessibility: The app is accessible by default. Route trips through stops with elevators, check live equipment status at any stop, and more. Plus, VoiceOver, Dynamic Type, and Increase Contrast live in the design system itself, so the whole app adapts at once rather than screen by screen.

Website

The Tunnel Rat site is a static Next.js build with a custom GLSL tunnel shader behind the hero, doing one job: explain the app and get you to the App Store. Its FAQ, changelog, and legal pages read from the same shared content repo the app bundles, so the two surfaces can't drift apart.

Landing Page: A bold hero lockup, featuring a powerful headline, quick actions, and short app demo. A faint tunnel-shaped shader peers through the background, paired with a dark theme in a nod to the underground.
The Tunnel Rat changelog, listing every shipped version
Changelog: An automated changelog, baked into the ship process, gets updated and published anytime a new version of the app hits the store.
The privacy policy on the Tunnel Rat website
The same privacy policy rendered inside the app
Git Submodule: To manage shared content across the app and website—like the privacy policy, terms of service, and FAQ— I set up a git submodule, and baked a version bump into the ship skills for both surfaces.