Nucleus

I wanted an app for myself. So I built one.

A personal health companion for tracking food, workouts, body measurements and recovery — built from scratch with AI.

Timeline72 hours → MVP
TeamSolo designer + builder
StackSwiftUI + Claude + Apple Health
ReadinessOne number answers “should I train today?”
Colour = stateMint means go. Amber means careful. Nothing is decorative.
Built fromLast night's HRV, resting heart rate and sleep vs my 30-day normal.
Not a screenshotEvery screen here is rebuilt in HTML from the app's tokens. Try light mode.

I was tired of doing the same thing every day.

I eat many of the same meals every day. Existing food trackers made me search and log the same things repeatedly.

I wanted to save my regular foods once and log them in seconds.

10–20 taps → 1 saved meal for a meal I'd already logged a hundred times
A typical trackerSearch every time
0taps
Search→Find food→Select→Quantity→Log
TodayDiaryEdit
Breakfast
Oats with milk310 kcal
+ Add food
Lunch
Rajma chawal540 kcal
+ Add food
Dinner
+ Add food
Snacks
+ Add food
‹ DiaryAdd Food
Search foods
Results
Yogurt, plain, whole milk149 kcal
Greek yogurt, nonfat100 kcal
Yogurt, low fat, fruit173 kcal
Greek yogurt, 2%146 kcal
Yogurt, greek, honey180 kcal
Frozen yogurt, vanilla221 kcal
Yogurt drink, mango142 kcal
‹ SearchFood
Greek yogurt, 2%
Serving size170 g ›
Servings−1+
MealLunch ›
146 kcal · 20 g protein · 4 g fat
Log food
Serving size
100 g
170 g
1 cup (245 g)
Logged. 14 taps later.
NucleusSave once, reuse
0taps
Favourite→Log
Food
Calories today1,000 left
1,500/ 2,500 kcal
Protein
124g/ 157 g
33 g to go
Breakfast460 kcal · 60g
4 egg whites + 2 whole eggs300 · 30g
Whey protein shake160 · 30g
Lunch1,040 kcal · 64g
Grilled chicken bowl680 · 52g
Roti×3360 · 12g
Log meal
CancelLog a MealSave
What did you eat?
BreakfastLunchDinnerSnack
Quick add
Search or pick a meal…
Greek yogurt bowl320 kcal
Whey protein shake160 kcal
Grilled chicken bowl680 kcal
Nutrition
Calories0kcal
Protein0g
Logged · Snack
Logged. Already done.
Both phones start at the same time

Then I thought bigger.

Food was only one part of what I wanted to understand.

I also wanted to know how I was training, how my body was changing, and whether I was actually ready to train.

  1. FoodWhat am I eating?
  2. ExerciseHow am I training?
  3. BodyHow is my body changing?
  4. HealthAm I ready to train today?

What started as a food tracker became Nucleus.

Meet Nucleus

The AI layer of Atom.

Nucleus brings together the data already being collected through Apple Health and turns it into something more useful than a collection of numbers.

Sleep
Activity
Heart rate
Workouts
Body data
Readiness

Can I train today?

ReadinessGood
Ready to train hard

Your body has bounced back. A hard session will pay off today.

Try it — change last night.

HRV50%138 ms · normal 119
Resting HR30%53 bpm · normal 56
Sleep20%6.9 h · normal 6.6

This is the app's real scoring from HomeScores.swift. Each signal is compared with my own 30-day normal, not a population average.

I didn't know how to code.

I'd never built an iOS app before.

00hours
  1. Idea
  2. UX
  3. SwiftUI
  4. HealthKit
  5. Data
  6. Debugging
  7. MVP
Idea

One app for food, training, body and recovery — one that logs my usual meals in a tap.

UX

Structure before style: what each screen answers, which data it needs, how the tabs connect.

SwiftUI

File → New Project. My first “app” was Xcode's template: a list of timestamps.

HealthKit

First real data — my own steps, sleep and heart rate, read from Apple Health.

Data

Meals and workouts saved on the phone with SwiftData. No backend, no account.

Debugging

A camera that opened itself eight times at once. Every bug taught me how SwiftUI actually behaves.

MVP

Briefing, food, workouts and trends — running on my own phone.

fd689a6 · 21 May, 01:40 · Initial Commit 4a778de · 23 May, 18:22 · 46 files · +10,093 lines
‹ Notes

App idea

  • log my usual meals in one tap — I eat the same stuff every day
  • workouts: show what I lifted last time
  • body measurements + weight trend
  • “should I train today?” → use watch data
  • one app, not four
header · date
readiness
score
sleep
strain
stress
today's goals
home · body · workouts · food
one question
per screen
Edit
21/05/26, 1:40:02 AM
21/05/26, 1:40:05 AM
21/05/26, 1:40:06 AM
21/05/26, 1:40:11 AM
Nucleus
Today
Steps6,420
Active energy512 kcal
Exercise41 min
Last night
Sleep6 h 54 m
HRV138 ms
Resting heart rate53 bpm
Food
Today
2 eggs + toast310 kcal · 21 g
Greek yogurt bowl320 kcal · 28 g
Rajma chawal540 kcal · 18 g
Whey protein shake160 kcal · 30 g
Workouts
Push6 exercises · 24 sets
Pull6 exercises · 22 sets
Food
Camera presented 8 timesFigCaptureSourceRemote err=-17281
10–15 AVCaptureSessions in 200 ms

I used AI to remove the barrier between knowing what I wanted to build and actually building it.

My workflow with Claude.

Not a list of tools — a loop. Every feature in Nucleus went round it, usually more than once.

REPEATLoop 01
01 · Design

Start from the interaction.

Figma when I needed it, or just the idea in my head. I always started from what it should feel like to use — never from the code.

120kg × 10kgreps✓previous
2
02 · Prompt

Describe it like a brief.

Plain language, plus the one constraint that mattered. The clearer the ask, the closer the first build.

MeBuild this interaction: while I'm logging a set, show what I lifted last time for the same set — right in the row, not on another screen.
03 · Build

Claude generates the implementation.

I read every change, even when I didn't fully understand it yet. These are the real lines that render “previous” in the app today.

// ActiveWorkoutView.swift · SetRowViewprivate var previousLabel: String? {    prior.map { "\(weightString($0.weightKg))kg × \($0.reps)" }} Text(previousLabel ?? "—")    .font(AppFont.mono(12, .medium))    .foregroundStyle(AppStyle.txt3)
04 · Test

Use it for real.

On my own phone, in the gym, between sets. Not in the simulator — that's where the real problems show up.

120kg × 1022.510
Last time's numbers, visible mid-workout
05 · Break

Something doesn't work.

Logging food from a photo kept failing — the same reply every time. This is the real error from June.

I couldn't read that just now — try again, or rephrase the items.
I couldn't read that just now — try again, or rephrase the items.
{"type":"error","error":{"type":"invalid_request_error","message":"messages: text content blocks must contain non-whitespace text"}}
06 · Understand

Ask Claude to explain it.

Not just “fix it” — “explain it”. That one word is how I actually learned.

MeExplain this error like I'm a designer, not an engineer.
ClaudeWhen a photo goes out without a caption, the app still attaches an empty text block. The API rejects blank text, so every photo-only log fails.
07 · Fix

Prompt again — with the rule.

Once I understood the cause, the fix was one sentence. Then straight back to the top of the loop.

MeNever send an empty text block. If there's no caption, send just the photo.
Logged 2 items ✓

Design decisions · Feature 01 — Food

I didn't want to search for food.

CameraNutrition labelAutomatically logged
Nutrition Facts
Serving size1 scoop (32 g)
Calories120
% Daily Value*
Total Fat 1.5g2%
Sodium 85mg4%
Total Carbohydrate 3g1%
Total Sugars 1g
Protein 24g48%
* Percent Daily Values are based on a 2,000 calorie diet.
Reading label…
Cancel
CancelLog a MealSave
What did you eat?
BreakfastLunchDinnerSnack
Nutrition · from label
Calories0kcal
Protein0g
Logged · no search needed

If the nutritional information is already printed on the package, why search for the same information in a database?

Favourite meals

I eat the same meals frequently, so I wanted to save them once and reuse them.

Whey protein shake
BreakfastLunchSnackDinner
Calories160kcal
Protein30g
Quantity1scoop
Saved once · reused forever
Whey protein shake160 kcal
4 egg whites + 2 whole eggs300 kcal
Grilled chicken bowl680 kcal
Roti120 kcal
Today0 kcal · 0 g
Tap + to log. Tap again — repeats fold into one row with ×2.

Feature 02 — Workout

Don't just log your workout. Beat it.

While using the app during a workout, I realised that recording my current set wasn't enough.

I wanted to know what I had done the last time I performed the same exercise.

Last workoutBench Press20kg × 10
TodaySet 120kg × 10
Can you beat it?

Try it on the phone: type a heavier weight or one more rep, then tick set 1

AI Coach
Recovery looks strong today — a good day to push for progression.
Bench Press
SETPREVIOUSKGREPS
PR120kg × 10
PR220kg × 10
PR320kg × 8
Add Set
Incline Dumbbell Press
00:110.0 t
End

I became my own usability tester.

Gym noteswritten mid-session
what did I lift last time?? have to leave the workout to check✓ shipped
too many taps between sets. I just want to tick it✓ shipped
logged eggs 3x → 3 separate rows. messy✓ shipped
keyboard shoves the tab bar up over my sets✓ shipped

I started writing down feedback while I was actually working out.

Not after the session.

During it.

I noticedI changed
Previous performance wasn't visibleSurface previous sets in every row
Logging required too many interactionsSimplified input: tick a blank set and it logs last time's numbers
The same meal logged three times became three rowsRepeats fold into one row with a ×N badge
The keyboard pushed the tab bar over my setsThe tab bar steps aside while I'm typing

Then everything became slow.

01
Problem

Interactions started taking seconds.

Ticking off a set, switching tabs, opening a meal — the screen just stopped. Feel it. Tap both buttons.

Before0.00 s
120kg × 10
After0.00 s
120kg × 10

The freeze on the left lasts as long as my first real hang report: 2.57 s.

02
I didn't know why

Five hangs in three minutes.

iOS was writing hang reports and I couldn't read them. These are the real ones from that night — up to 9.24 seconds of a frozen screen.

Runloop hang reports1 June 2026 · 00:58 → 01:00
10s5s0
My budget · 0.5 s
2.57s
9.24s
7.58s
8.20s
7.45s
00:58:4900:59:1700:59:4701:00:2401:00:52
03
Investigate

Claude suggested adding a logger.

One line for every tap, every screen redraw, every database read. For the first time I could see what the app was doing when I touched it.

$ log stream --predicate 'subsystem == "com.ujjawal.atom"' [workout] tap → complete set 2 [home] body re-evaluated [home] fetch WorkoutSession · every row [home] fetch FoodEntry · every row [home] streak · walking the full history ⚠ main thread blocked — hang reported
04
Discovery

Too much work was happening on the main thread.

The main thread is the one that draws the screen. Mine was also re-reading my entire history on every tap — so the screen had to wait its turn.

Beforetap waits
Maindraws the UI
UIread every workoutUIrecompute streaksgroup all meals UIread every workoutUIrecompute streaksgroup all meals
Backgroundidle
Your tap is waiting in line…
05
Fix

Move heavy processes away from the main thread.

The heavy work moved to the background. Results got cached, and only recompute when something actually changes.

Aftertap answered
Maindraws the UI
UIUIUIUIUIUIUIUIUIUIUIUI UIUIUIUIUIUIUIUIUIUIUIUI
Backgroundheavy work
read workoutscache streaksgroup meals read workoutscache streaksgroup meals
Your tap gets answered right away.
06
Result

A much more responsive app.

Same phone, same data, same history. The difference is what the main thread is allowed to do.

SecondsResponsive

Scroll back to step 01 and tap After — that's how it feels now.

The real lesson

I didn't know what was wrong, so I learned enough to find out.

Not every idea survived

I removed a feature I really wanted.

The idea
Analyse my data and recommend calorie + protein targets for body recomposition.
The problem
It became too complex, and I wasn't confident enough in the reliability of the recommendations.
The decision
So I removed it.
Body Recomp Zone · May 2026v0
BRZ StatusIn Body Recomp ZoneBurned 2,640 · Ate 2,410 · −230 kcal
BRZ Streak—Log food to start
Calories vs. what my watch says I burned−300 → +100
Protein≥ 157 g
Trained recentlylast 48 h
Removed
What survived
On track for calories and protein
A plain check against targets I set myself — no recommendations the app can't back up.
Nope. Still not shipping it.

Product lesson

Good UX isn't about fitting every useful idea into the product. Sometimes it's knowing what not to ship.

Finally, UI.

I designed last, on purpose. By the time I touched the visuals, every screen had already been used, broken and rebuilt.

An instrument, not a wellness toy.

79Recovery

Information first

The interface prioritises useful data over decoration.

Sleep80
Exertion3.7
Stress30

Low visual noise

Nothing exists simply to make the app feel “fun.”

1Protein 2132g 325 g to go

Strong hierarchy

Important information should be understood at a glance.

Hover me

Functional motion

Animation exists to communicate state and relationships.

Colour is rationed.

Greyscale by default · one accent · three status colours · gold only for records. Click a swatch to copy it.

The evolution.

CancelLog a MealSave
Egg
BreakfastLunchDinnerSnack
DateSaturday, 20 June
✎ Edit

QUICK ADD

Search or pick a meal…⌄

NUTRITION

Calories0kcal
Protein0g
More Macros›

Could not estimate nutrition. Enter values manually.

ATOM

TODAY'S GOALS

Steps6,420
Calories1200
Protein98 g
Exercise41 min
Food · 43 daysWorkout · 8 wks

RECOVERY & READINESS

Recovery79 / 100
79
HomeBodyWorkoutsFood

The first version worked. It just wasn't very good yet.

I still use it every day.

Nucleus started as a small experiment to make food logging easier.

It became a personal system for understanding how I eat, train and recover.

And somewhere along the way, it taught me something much more valuable:

I don't need to know everything before I start. I need to know how to figure things out when I get stuck.

What I'm taking with me.

01

AI lowers the barrier. It doesn't replace thinking.

02

Real usage exposes problems prototypes can't.

03

When I don't know something, I learn enough to solve it.

View more projects

MKU SalesPro · MKU Website Revamp

Browse projects

Explore the blog

60 Minutes to Ship: How a Sleeping TV Sparked a Better Event Experience

Read posts