Inspiration

Every café, bakery, and bike shop has dead hours: the mid-afternoon lull when the staff are paid but the room is empty. The usual fix is a discount app, which brings in bargain hunters who never come back. Meanwhile, thousands of people run and walk past those same shops every day, looking for a reason to go somewhere new.

We wondered: what if a business's slowest hour could pull in exactly the people already out moving? Ride-share apps raise prices when demand is high. PitStop is surge pricing in reverse. When demand is low, a business offers a reward, and the app turns someone's run into a trip to its door.

What it does

For runners

You plan the run you want: run, walk, or bike, a distance or a time, one-way or out-and-back. PitStop finds real destinations that fit that goal and builds 2–3 real routes to them. Gemini then proposes a scenic option (parks, waterfronts, quiet streets), which is checked against Apple Maps before you ever see it.

While you run, you get:

  • Turn-by-turn navigation with off-route rerouting
  • Crosswalk and traffic-signal alerts from OpenStreetMap, spoken in a natural voice
  • A city guide that briefly introduces landmarks you pass
  • Quick-stop suggestions for places that match your interests ("Smoothie shop 2 blocks ahead, +0.2 mi. Stop by?")
  • Time-limited Off-Peak offers: when a partner nearby goes quiet, you get a 30-second offer with a countdown, also shown on the Lock Screen and Dynamic Island. Accept it and your route bends to include the business.

When you arrive, you stay 10 seconds to check in. If anti-cheat verifies the run was real, you unlock the partner's reward, a passport stamp for every new place, and milestone badges. You can share a run card, with your start and finish hidden by default.

For businesses

Flip "We're slow right now" and set a reward. You then see, live:

  • Offers shown, accepted, and expired
  • "Runner arriving in 3:42", counting down
  • Check-ins as they happen

An impact dashboard shows check-ins landing inside your quiet hours, plus an offers shown → accepted → visited funnel. Businesses only ever see visits and arrival times, never health data, routes, or names.

For admins

Live stats, user and role management, and tools to clean up demo data.

How we built it

PitStop is a native iPhone app in SwiftUI (iOS 17+) with @Observable MVVM. A single AppState owns one location pipeline: every GPS fix, real or simulated in Demo Mode, flows through one function to navigation, check-in, anti-cheat, alerts, suggestions, and quest matching. That one decision made the indoor demo behave exactly like a real run.

  • Apple frameworks: MapKit (search, directions, snapshots), CoreLocation, CoreMotion (step cadence for anti-cheat), SwiftData (offline history), ActivityKit (Live Activity), Swift Charts (business dashboard), and AVFoundation (natural-sounding voice cues).
  • Destination matching: one MapKit search returns only a few results, so the app searches 6 points around a ring at the right distance. It then measures real walking routes and keeps places within ±15% of the goal.
  • Route generation: Apple Maps only routes A → B. To turn a café 0.8 miles away into a 3-mile run, I place waypoints on an ellipse with the start and destination as its foci. The app routes each candidate for real, then runs one correction pass.
  • Grounded AI agents:
    • Gemini 3.8 Flash runs a function-calling agent with three MapKit-backed tools. It must answer by choosing one of the real routes those tools return, and Swift re-checks its pick.
    • The recommender may only choose from candidates the app provides, by ID.
    • Gemini 3.5 Flash-Lite writes all the city-guide lines for a route in one batched request, using only the place data I send.
    • Every AI feature has a non-AI fallback, and requests go through a Cloudflare Worker, so the API key isn't stored on the phone.
  • Live sync: Firebase Auth and Cloud Firestore sync the runner and business phones in real time, with offline persistence. Security rules enforce roles on the server: nobody can change their own role, and runners may only edit the runner fields of a quest.
  • Safety data: the OpenStreetMap Overpass API supplies crosswalks and traffic signals along the route.

Challenges I ran into

  • MapKit has no waypoints. Hitting a target distance meant inventing waypoints. The ellipse approach worked, but streets rarely match straight-line math, so I added a correction pass that scales the budget by goal ÷ actual distance.
  • Out-and-back routes overlap themselves. Matching the runner to the nearest point on the route jumped to the return leg, so I made it search forward from the last match.
  • Making the AI trustworthy. Free-form AI routes would invent paths and distances. I redesigned it so Gemini can only pick from routes that MapKit produced and Swift validated. I also had to send Gemini's turns back byte-for-byte so its Gemini 3 "thought signatures" survived function calling.
  • Free-tier limits. I built caching, merging of duplicate requests, client-side throttling, 429 backoff with model fallback, and a non-AI path for every feature.
  • Too many popups. Safety alerts, offers, suggestions, and narration all competed for the screen and covered the map. I built a single PopupManager with a priority queue: safety pre-empts everything, and the rest wait their turn. The camera also offsets itself so the runner stays visible between the cards.
  • Check-in in the real world. MapKit routes end on the street, which can be 50 m from a business's pin in the middle of a block. Check-in now accepts either the place or the route's street-side arrival point.
  • Demo Mode vs. anti-cheat. A 6× playback looks like a car. I normalized speeds by the playback rate and generated matching step data, then added a deliberate "fake run at car speed" button so I could show a rejection live.
  • Setup surprises: Firebase returning a vague "internal error" until Authentication was initialized, and // starting a comment in .xcconfig files, which breaks URLs.

Accomplishments that we're proud of

  • Two phones, live, over the internet: a business goes slow, the runner gets a 30-second offer within a second, and the business watches a live arrival countdown.
  • AI that doesn't hallucinate routes. Every route Gemini suggests is real, measured, and verified, and the app still works fully with AI turned off.
  • Privacy built in: no health data collected, AI sees only rounded locations, businesses see visits rather than people, and shared run cards hide your start and finish by default.
  • Real verification: step cadence vs. GPS speed, simulated-location detection, and impossible jumps, so rewards mean something to the business paying for them.
  • Polish that respects runners: Lock Screen and Dynamic Island, slim popups that keep the map visible, a natural voice, Dynamic Type, VoiceOver announcements, and Reduce Motion support throughout.

What we learned

  • Constrain the AI and verify it. The most useful pattern was giving the model real tools, making it choose among their results, and checking its answer in code. That's what makes AI features demo-safe.
  • One data pipeline beats many. Routing every location fix through one place made Demo Mode, anti-cheat, and navigation consistent for free.
  • Design for both sides of a marketplace. The runner experience is fun, but the business dashboard ("did foot traffic arrive during my quiet hours?") is what makes the idea viable.
  • Security rules are part of the product. Writing Firestore rules forced me to decide exactly who can see and change what, which shaped the data model itself.
  • Small UX details carry a demo: a route that draws itself in, a countdown bar that changes color, a voice that sounds human.

Built With

Share this project:

Updates

Submission history