WTV
NYC Block Intelligence — Native iOS
New York City runs on local knowledge. WTV is a native iOS app that puts a live block-level intelligence layer on top of the city: what's happening, what to eat, where to go, and what to avoid. One app, every layer of the block.
Overview
WTV is a personal project I designed and built from scratch, a native iOS app built entirely in SwiftUI. It gives New Yorkers a single surface for block-level city intelligence: curated spots, restaurant grades, live events, transit crowding, traffic incidents, rat activity reports, and pothole conditions. The design language sits between a neighborhood newspaper and a real-time data dashboard. I handled every layer: product strategy, UX, design system, and all SwiftUI engineering.
Block Report
A curated intelligence card for your block: vibe summary, events, food grades, transit status, and street conditions. Personalized to your location and interests.
Live Feed
A swipeable home feed of spots, eats, events, and transit — filtered by your neighborhood and categorized with haptic-driven card interactions.
Map Layer
An interactive SwiftUI Map with a sliding bottom tray: spot, food, event, and transit carousels with live annotations and direct detail sheets.
Problem Statement
New York City produces more local data than any city on earth. None of it is in one place.
The fragmentation is exhausting: You check Google Maps for spots, Yelp for grades, Twitter/X for incidents, the MTA app for transit, NYC Open Data for permit activity — and none of it talks to each other. There's no layer that puts your block in context.
WTV is that layer. One app, every signal, organized by block. Whether you're deciding where to eat tonight, checking if your train is packed, or figuring out why there's construction noise at 7am, WTV has the answer.
Design System
The WTV design system draws from two references: the typography and texture of a neighborhood broadsheet, and the data density of a real-time transit board. The visual accent is terracotta — warm, NYC, grounded. Status colors follow clear signal logic: green is safe, yellow is caution, red is alert.
Terracotta
#E85D3A
Primary accent, CTAs, active states
Forest
#2D8B4E
Good / safe / Grade A
Amber
#D4A017
Caution / Grade B / moderate
Alert Red
#C43333
Danger / Grade C / high activity
Typography
Three-tier type system: Serif for headings and editorial labels (New York feel), Sans-serif for body and UI text, and Monospace for all data: ratings, walk times, delay minutes, and status codes.
MTA Subway Colors
Official MTA line colors, pixel-perfect, used for inline subway badges throughout the app.
Haptic Rhythm
Every interaction has a corresponding haptic weight. Light for navigation and chip selection, medium for card taps and confirmations, heavy for long-press previews and alerts. The feedback system mirrors the visual hierarchy, giving the app tactile depth.
Card System
Spot Card
Rating, category badge, vibe tags
Food Card
DOH grade, cuisine, neighborhood
Event Card
Type, time, price, location
Transit Card
Lines, crowding level, walk time
Features
Five tabs, each purpose-built. Every screen is fully functional, every interaction has feedback, and every data layer is sourced from real NYC open data and mock providers that mirror live API shapes.
Tab 01
Home Feed
- •Personalized greeting: Time-aware (morning/afternoon/evening), shows your saved name or neighborhood
- •Layered content: Spots, food, events, transit, traffic, rat activity, and potholes, all in one feed
- •Layer toggles: Quickly filter the feed by content type using animated chip controls
- •Long press to preview: Medium detent sheet opens on hold, full sheet on tap, with haptic feedback for each
Home Feed — Personalized, Layered
Tab 02
Map + Tray
- •Overlay tray architecture: The bottom tray uses
.overlay(alignment: .bottom), not a sheet, so the tab bar always remains accessible - •UnevenRoundedRectangle: iOS 16 API used to give the tray top-only corner radius, eliminating the bottom gap between tray and tab bar
- •Category carousels: Spots, Eats, Events, Transit — snap-scrolling horizontal carousels in the tray, each card opens a full detail sheet
- •Search from map: Tapping the search bar opens the full SearchView as a sheet over the map
Map — Tray, Annotations, Carousels
Tab 03
Block Report
- •WTV Says: A dynamically generated summary card that writes itself based on your block data, time of day, and saved interests. No hardcoded copy.
- •Change block: MKLocalSearch-powered address picker finds the nearest matching block report for any NYC address
- •Data layers: Events, food grades, transit (with MTA line badges), rat activity level, pothole count, and traffic conditions — all in one card stack
- •Vibe score: Composite signal that weights activity, safety, and transit data into a single number
Block Report — Personalized, Contextual
Tab 04
Search
- •Cross-entity search: One query runs across spots, restaurants, events, and transit — filtered by category chips
- •Intent-aware matching: Search by name, neighborhood, cuisine, vibe tag, event type, or subway line
- •Long press to preview: Medium-detent sheet preview on hold, full detail sheet on tap — same pattern as the Feed
Search — Intent-Driven, Cross-Entity
Onboarding
Personalization doesn't happen by accident. A 5-step onboarding flow collects your name, location, age range, and interests before you ever touch the main app. That data flows directly into the Block Report summary, Feed greeting, and Map defaults. All stored via @AppStorage, persisted across launches.
01
Welcome
WTV branding, tagline, what you're signing up for
02
Name
Optional name for personalized greetings
03
Location
MKLocalSearch inline — sets your default block
04
Age Range
18–24, 25–34, 35–44, 45+ pill selection
05
Interests
Food, Nightlife, Arts, Music, Parks and more
5-Step Onboarding
Spring transitions between steps, inline MKLocalSearch, interest grid with press states
Block Picker
Change your active block at any time. Address search uses geo-matching to find the nearest block report.
Technical Breakdown
UI Framework
SwiftUI (iOS 16+), @StateObject, @AppStorage, @FocusState
Map Layer
MapKit (SwiftUI Map API), MKLocalSearch, MKCoordinateRegion, custom @MapContentBuilder annotations
Navigation
Custom tab bar in ContentView ZStack, modal sheets with PresentationDetent, overlay tray (not sheet) for map
Data
NYC Open Data (Socrata) for events, restaurant inspections, 311 potholes, and rodent activity. Swift actor-based NYCDataService with 1-hour in-memory cache. AppStorage for user preferences.
Key Engineering Decisions
Overlay tray, not a sheet
Using .sheet(isPresented: .constant(true)) renders the tray at the window level, above the tab bar. Switching to .overlay(alignment: .bottom) keeps the tray inside MapView's bounds so ContentView's ZStack tab bar renders on top — no workarounds needed.
UnevenRoundedRectangle
The map tray had a 1-2px gap between its rounded bottom corners and the tab bar due to anti-aliasing. UnevenRoundedRectangle (iOS 16+) clips the tray with topLeadingRadius: 20 and zero bottom radii, eliminating the gap at the geometry level.
Dynamic WTV Says generation
The Block Report summary could have been a hardcoded string. Instead, personalizedSummary(for:) generates it at runtime using the user's saved neighborhood, interests, time of day, and live block data — so it reads differently every session.
ButtonStyle for tray cards
SwiftUI scroll views eat TapGesture recognizers inside horizontal scrollers. Wrapping each carousel card in a Button with a custom ButtonStyle (TrayCardPressStyle) routes touches correctly through the scroll gesture recognizer without conflicting with drag.
Single .sheet() anchor
Having two .sheet() modifiers on the same parent view causes SwiftUI to only present one. Lifting selectedDetail as a @Binding into MapBottomTray and placing the single .sheet(item:) inside the tray's body resolved silent non-presentation bugs.
Euclidean geo-matching
The block picker stores the nearest BlockReport ID using a sqrt-free distance comparison across known block lat/lon coordinates. No CLLocationManager required — fast, offline, deterministic.
Future Plans
WTV is a week-old live app that I'm actively using every day in NYC. Here's what's coming next.
Live Data
MTA + Transit Real-Time
GTFS-RT integration for real subway crowding, delay minutes, and service alerts by line. The transit layer currently uses interpolated data — live feeds are next.
Social
Crowd Reports
User-submitted block updates: "It's packed right now", "Smells like something died on Halsey". Layered on top of official data as a community signal.
Intelligence
WTV AI Layer
A Claude-powered natural language interface for the Block Report. Ask "what's good for a night out in Bed-Stuy tonight?" and get a real answer from real data.
Distribution
App Store Submission
On TestFlight now. App Store submission is next — assets, review guidelines, and privacy nutrition labels are in progress.
Expansion
Saved Tab
Bookmark spots, events, and restaurants. View them offline, get reminders for upcoming saved events, and share lists with friends.
Polish
Animations + Transitions
Matched geometry for tab transitions, skeleton loading states for live API fetches, and pull-to-refresh with a custom WTV branded animation.
5
Tabs
Feed, Map, Block, Search, Saved
7+
Data Layers
Spots, food, events, transit, traffic, rats, potholes
100%
SwiftUI
Zero UIKit, native all the way down
1
Person
Design, SwiftUI, data architecture
WTV started as a personal question: why is there no single app that tells me what's actually happening on my block? A week of building in Swift later, there is — pulling live data from NYC Open Data, DOH inspections, 311, and rodent reports in real time. The hardest parts weren't the features, they were the architecture decisions that let all five tabs coexist without stepping on each other. Next: MTA GTFS-RT, App Store submission, and crowd reports.