Skip to content
UA.
All projects
LiveFull-stackMay 2026 — Present

FitApp

One .NET API, three native clients, and the habits that keep someone coming back.

The FitApp dashboard shown on the web, on iOS and on Android at the same time.
My role
Solo developer — API design, data model, all three clients and deployment.
Status
Live

The problem

Fitness tracking data is only useful when it follows the person, not the device — and a tracker nobody opens after the second week is not useful at all. Building the same feature set three times, once per platform, would have meant three sources of truth and three sets of bugs; building only the tracking would have meant an app with nothing to come back for.

What I built

One C# .NET 8 API over MySQL as the single source of truth, with thin clients on top: SwiftUI on iOS, Kotlin on Android and Next.js on the web. Beyond logging meals, water, steps and workouts, the server runs what makes it a habit — a Gemini-backed coach, daily and friend quests, a gold balance with a shop, a leaderboard, and chat over SignalR. Auth, validation and every rule live in the API; the clients present state and collect input.

What it looks like

  • The AI coach screen on iOS, answering a question about the day's plan.
    The Gemini-backed coach. It runs behind the API, so the key stays on the server and the prompt changes without an app release.
  • The leaderboard on Android, ranking users by their gold balance.
    Leaderboard, quests and a gold balance — the same services on every client, because the ranking has to agree with itself.
  • Workout programs on the web client, with exercises and progress.
    Workout programs on the web client, built from the same endpoints the phones use.
  • The daily report on the web client: calories, water, steps and workout totals.
    The daily report — the numbers every client renders and none of them calculates.

Architecture

  1. Clients

    Render state and capture input. No business rules, so no platform can disagree with another about a calorie, a streak or a balance.

    • SwiftUI (iOS)
    • Kotlin (Android)
    • Next.js (Web)
  2. API

    Authentication, validation and every calculation — the one place a rule is written down, behind an OpenAPI document the clients are built against.

    • .NET 8
    • JWT
    • BCrypt
    • Google sign-in
  3. Engagement services

    The coach, the quests, the currency and the leaderboard: each its own service, because the part of a fitness app that decides whether it survives month one is not the part that adds up calories.

    • Gemini
    • SignalR
    • Daily & friend quests
    • Gold and shop
  4. Persistence & media

    A relational schema for users, meals, workouts, quests and balances, grown through migrations rather than rewritten; images go to object storage, never into the database.

    • EF Core 8
    • MySQL on AWS RDS
    • Cloudinary
    • S3

Decisions & trade-offs

  • Business logic in the API, never in the client

    A calorie formula duplicated across Swift, Kotlin and TypeScript is a formula that will eventually give three different answers. Keeping it server-side made the clients replaceable — and made the web client possible at all without a fourth implementation.

  • The AI coach lives on the server

    Calling a model from three apps would mean shipping the key in three binaries and begging for an app-store release every time the prompt is wrong. The coach is one service behind the API: the key never leaves the server, and the advice can change on a Tuesday afternoon.

  • Gamification is a schema, not a feature flag

    Quests, gold, the shop and the leaderboard are their own tables and their own services rather than conditionals bolted onto the tracking code. Adding a quest type is a row; it is the difference between a system that grows and one that accumulates special cases.

  • Stateless JWT auth

    Three clients with different session lifecycles meant server-side sessions would have become the bottleneck. Token-based auth let each platform manage refresh the way its own SDK expects.

Outcome

A feature is one API change and three presentation layers, instead of three implementations that drift apart — and the reason to open it on day thirty is a server-side system rather than a notification.