Inspiration

Every calisthenics app I tried counted reps. None of them answered the question I actually cared about: how good am I, really?

In powerlifting you have a number. In running you have a pace. In calisthenics you have a guy at the park telling you your muscle-up "kinda counts." I wanted a scoreboard for a sport that never had one.

The other thing that bugged me: I'd train for months, plateau, and have no idea why. It turned out my pulling was fine and my core was three levels behind. Nobody told me that. I found out the slow way.

What it does

Ranked grades your calisthenics ability across 38 skill lines, from your first push-up to a planche — 154 stages in total, each with real prerequisites.

You get nine ranks, Bedrock to Zenith (F to SSS). Not one overall number, but one per muscle region, so the thing holding you back is named instead of hidden. If your legs are an A and your chest is a B, you can see it on the body map.

From there the app picks your next step. Not a generic program — the specific stage that moves your rank the most, based on what's actually weak.

And there's a workout tracker whose timer survives screen lock. That sounds boring until you read the reviews of every competitor app, where it's the single most complained-about failure. Live Activities on the Lock Screen and in the Dynamic Island solved it.

How I built it

Native iOS, no cross-platform layer: SwiftUI, SwiftData for offline-first storage, ActivityKit for the Live Activity. Supabase for the account backend, PostHog for the funnel, RevenueCat for everything money.

The interesting part is the rank engine. It's deliberately not an XP counter. XP rewards whoever opens the app most; a rank should reward what you can actually do. So each skill line is tier-weighted and anchored, and your region ranks feed each other — a front lever is a pulling skill and a core skill at the same time. It's pure, testable logic sitting in its own module with unit tests, because it's the one part of the app that's genuinely hard to get right.

On the RevenueCat side I leaned on the SDK harder than I expected to. customerInfoStream is the single source of truth for entitlement state — there is no second state machine in my code deciding whether someone is subscribed, which is the bug I've written in every previous app. Offerings drive both the onboarding paywall and the standalone one. logIn/logOut keep the anonymous app user and the real account stitched together, so somebody who subscribes before signing up doesn't lose their purchase. Trial eligibility is checked through the SDK rather than guessed, and win-back offers run off it too.

Challenges I ran into

Calibration. My first version handed out S-ranks like candy and the whole thing felt worthless immediately. A ranking system only motivates if the top actually means something, so I re-tuned the curve against real athletes instead of against my own ego.

Cutting two finished features a week before shipping. I had leaderboards and video-verified attempts built. Then I looked at what they actually required — a forced login right after purchase, user-generated content, and the whole Apple obligation stack that comes with it: account deletion, token revocation, a reporting mechanism. For a 1.0 with no users yet, that's a wall in front of the door. So I put both behind a feature flag and turned them off. Not deleted — the tables, the server code and the screens are still there, waiting for a version where they earn their keep.

That was the hardest call in the project, and the one I'd defend hardest. Shipping the smaller thing that works beats shipping the bigger thing that gets rejected.

Localisation. The app ships in 30 languages, which surfaced a class of bug that's invisible in English: a layout that's fine at "Ranks" and broken at "Классификация".

What I learned

Let the SDK own the state it's responsible for. Every subscription bug I've ever shipped came from me caching entitlement state somewhere "for convenience" and then having two answers to one question.

And feature flags are a product tool, not just an engineering one. Being able to turn something off without deleting it is what made the cut above possible at all.

What's next

Rank history over time, so you can see the curve and not just today's letter. More skill lines. And eventually the leaderboards come back — with the account requirement earning its place instead of standing in the way.

Built With

  • activitykit
  • calisthenics
  • edge-functions
  • fitness
  • ios
  • json
  • live-activities
  • mobile
  • postgresql
  • posthog
  • revenuecat
  • storekit
  • supabase
  • supabase-auth
  • swift
  • swiftdata
  • swiftui
  • widgetkit
  • xcodegen
Share this project:

Updates

Submission history