Inspiration

I built Amble for a group chat. My friends and I kept saying we should walk more, and it never turned into anything, because there was nothing to look at the next morning. Step counts sat in Apple Health where nobody else could see them, and a number nobody else sees is a number that stops mattering by Wednesday.

So I made the step count social. Not a global leaderboard full of strangers, just the handful of people who would actually give you grief for finishing last.

The Step Wallet came out of my own frustration. I would string together a good week, then have one day get away from me, and the streak would reset to zero. Losing weeks of work to a single bad day felt less like motivation and more like punishment. Banking the steps I walked over my goal, and spending them to cover a day I fell short, was the fix I wanted as a user before it was a feature I built.

What it does

You join an "Amble", a small group of friends, and every day your steps are ranked against theirs on a live leaderboard. At the end of each local day the board freezes, a winner is crowned, and win streaks build up over time.

Alongside the group game there is a personal one: a daily step goal, and a streak for hitting it. That is where the Step Wallet comes in. Every step you walk over your goal is banked, up to a 30,000 cap. Fall short on a bad day and the wallet can spend those banked steps to cover the difference, so one bad day does not erase weeks of work.

Around that: head-to-head step races, group challenges, group chat and DMs, a workout feed with hype and comments, Live Activities, home-screen widgets, and a map route flyover you can export and share.

How I built it

  • iOS client in SwiftUI, with WidgetKit and ActivityKit for widgets and Live Activities, AppIntents for shortcuts, and MapKit with CoreLocation for route recording and flyover export.
  • HealthKit day totals sync up to Postgres, so leaderboards can rank people against each other.
  • Supabase Postgres with row-level security throughout. Scheduled work runs on pg_cron: freezing yesterday's board, settling the Step Wallet, and sending the morning streak verdict.
  • Notification rows fan out through a Deno edge function to APNs, so every alert exists both as an in-app row and a push banner.
  • RevenueCat drives the Pro subscription and free trial, with a webhook edge function keeping entitlement state in sync server-side rather than trusting the client.

The Step Wallet is the piece most tied to monetization. A settled day is immutable, so the wallet has to decide at settle time whether a day is banked, spent, or lost. Pro accounts have shortfalls filled automatically. Free accounts get a 24-hour window to upgrade and rescue the day.

Challenges I ran into

Time zones. Every member's day ends at their own local midnight, so "yesterday" is a different moment for every person in the same group. Freezing the board, settling the wallet, and sending the morning verdict all had to run per-user on an hourly sweep rather than once globally.

Late-arriving Health data. Apple Health backfills step counts hours or days after the fact. If a settled day could be rewritten by a late sync, streaks and wallet balances would silently change under people. Settled days are now frozen with a snapshot of the count and the goal they were actually judged on.

One device, one account. Push tokens were being claimed by whoever signed in last, so people were getting each other's notifications. Claiming a token is now exclusive and runs server-side, because the next account physically could not clear the previous one's row under RLS.

Two devices syncing at once deadlocked on unordered upserts, until rows were sorted by date before writing.

What I learned

Most of the hard problems were not in the app at all, they were in the database. Anything involving "yesterday", a streak, or a balance turns into a question about exactly when a day ends and whether it is allowed to change afterwards. Writing those rules down as immutable settled records, instead of recomputing them live, removed a whole category of bugs I kept re-fixing.

Built With

Share this project:

Updates

Submission history