An iOS body-transformation app built with SwiftUI. Tracks daily habits, progressive-overload strength training, and a fully personalised nutrition plan — all derived mathematically from the user’s body statistics and goal body fat percentage.
BodyPhase is a 120-day structured program that combines:
Everything adapts automatically when the user logs a new weight or updates their settings.
| Tool | Minimum version |
|---|---|
| Xcode | 16.0 |
| iOS deployment target | 16.0 |
| Swift | 5.9 |
| xcodegen | 2.x (only required to regenerate .xcodeproj after editing project.yml) |
git clone https://github.com/nsaikrishna15/Fitness-tracker.git
cd Fitness-tracker
open BodyPhase.xcodeproj
Select a simulator or connected device from the Xcode toolbar and press Cmd+R.
Only needed if you have edited project.yml.
brew install xcodegen
xcodegen generate
open BodyPhase.xcodeproj
xcodebuild \
-project BodyPhase.xcodeproj \
-scheme BodyPhase \
-destination 'platform=iOS Simulator,name=iPhone 17' \
build
BodyPhase.xcodeproj in Xcode.Free Apple Developer accounts produce 7-day certificates. The app must be re-signed after 7 days. A paid Apple Developer Program membership ($99/year) is required for TestFlight distribution and 1-year certificates.
The onboarding screen collects six inputs. All nutrition and workout targets are derived from these values and can be updated at any time in Settings.
| Input | How it is used |
|---|---|
| Biological sex | BMR formula constant; body fat formula sex coefficient |
| Height (cm) | BMI calculation; Deurenberg body fat estimate |
| Current weight (kg) | BMR; TDEE; all macro targets; lean body mass; goal weight |
| Age (years) | BMR; Deurenberg body fat estimate |
| Target body fat % | IntensityMode selection; goal weight calculation |
| Preferred protein source | Selects chicken or fish gram amounts in meal plan |
| Program start date | Determines current phase (1-4) and locks past/future days |
All formulas live in Models/DietPlan.swift. There are no hardcoded macro values anywhere in the UI — every number shown is computed at runtime from the user’s stored stats.
Mifflin-St Jeor (1990) — the most validated BMR equation per ACSM guidelines.
Male: BMR = (10 x weight_kg) + (6.25 x height_cm) - (5 x age) + 5
Female: BMR = (10 x weight_kg) + (6.25 x height_cm) - (5 x age) - 161
BMR multiplied by an ACSM activity factor that increases with program phase.
| Phase | Days | Activity factor |
|---|---|---|
| Phase 1 — Foundation | 1-30 | 1.375 (light activity) |
| Phase 2 — Volume | 31-60 | 1.550 (moderate activity) |
| Phase 3 — Strength | 61-90 | 1.725 (very active) |
| Phase 4 — Peak | 91-120 | 1.725 (very active) |
Deurenberg et al. (1991), derived from BMI, age, and sex.
Male: BF% = (1.20 x BMI) + (0.23 x age) - 16.2
Female: BF% = (1.20 x BMI) + (0.23 x age) - 5.4
Result is clamped to [3, 60] to prevent physiologically impossible values. This estimate can overestimate body fat in highly muscular individuals and underestimate it in the elderly.
The gap between estimated current body fat and the user’s target body fat determines the daily calorie adjustment.
| Current BF% minus target BF% | Mode | Daily calorie adjustment |
|---|---|---|
| Greater than 10 | Aggressive Cut | -750 kcal |
| 5 to 10 | Standard Cut | -500 kcal |
| 2 to 5 | Mild Cut | -250 kcal |
| 0 to 2 | Recomp | 0 kcal |
| Below 0 | Bulk | +200 kcal |
IntensityMode is a computed property on HabitStore. It updates reactively whenever the user logs a new weight or changes their target body fat in Settings.
target_kcal = max(TDEE - deficit, calorie_floor)
calorie_floor = 1500 kcal (male) / 1200 kcal (female)
The floor prevents the plan from dropping below safe minimum intake thresholds per WHO and ACSM.
LBM_kg = weight_kg x (1 - estimated_BF% / 100)
LBM is the basis for the protein target rather than total body weight. Using total body weight for overweight individuals inflates the protein requirement significantly. Example: a 100 kg person at 30% body fat receives 220 g/day on total weight vs the correct 154 g/day on LBM. The excess protein displaces carbohydrates and makes the plan unsustainable.
protein_g = LBM_kg x 2.2
Source: ISSN 2017 Position Stand; Phillips and Van Loon (2011). The 2.2 g/kg LBM value is within the ISSN-recommended range of 2.3-3.1 g/kg LBM for athletes in a caloric deficit seeking to preserve lean mass. Falls back to total body weight when body composition data is unavailable (height or age not entered).
fat_g = max(target_kcal x 0.25 / 9, weight_kg x 0.5)
Fat is set to 25% of total calories, with a minimum floor of 0.5 g/kg total body weight to preserve hormonal function (testosterone production, fat-soluble vitamin absorption). Source: ACSM; ISSN.
carbs_g = (target_kcal - (protein_g x 4) - (fat_g x 9)) / 4
Carbohydrates fill the remaining calories after protein and fat are allocated.
Caloric densities used throughout: protein 4 kcal/g, carbohydrate 4 kcal/g, fat 9 kcal/g (Atwater factors).
Goal weight is calculated from lean body mass and the user’s target body fat percentage. BMI is not used as a physique target — it is a population-level screening tool and sets unrealistic individual targets. A lean athletic male at 180 cm with 15% body fat will naturally sit at BMI 24-25, not BMI 22.
goal_weight_kg = LBM_kg / (1 - target_BF% / 100)
Example: 93 kg at 25% estimated body fat, targeting 15% body fat.
LBM = 93 x 0.75 = 69.75 kg
Goal weight = 69.75 / 0.85 = 82.1 kg
The app also calculates the user’s BMI at their goal weight for informational context.
weekly_loss_kg = daily_deficit_kcal x 7 / 7700
weeks_to_goal = ceil((current_weight - goal_weight) / weekly_loss_kg)
Source: Wishnofsky (1958). 1 kg of body fat contains approximately 7,700 kcal. This is a linear estimate and does not account for metabolic adaptation. Returns nil for recomp and bulk modes.
water_litres = clamp(weight_kg x 0.035, min=3.0, max=5.0)
Source: EFSA guidelines. The 35 ml/kg formula is standard in clinical and sports nutrition. The 3-litre minimum reflects active daily training. The 5-litre cap prevents excessive intake.
Total daily macros are split across 5 meals in fixed ratios, front-loading carbohydrates around the morning training window.
| Meal | Time | Protein | Carbs | Fat |
|---|---|---|---|---|
| Pre-Workout | 6:45 AM | 13% | 21% | 7% |
| Breakfast | 8:15 AM | 22% | 23% | 25% |
| Lunch | 12:30 PM | 29% | 31% | 16% |
| Snack | 4:30 PM | 12% | 5% | 25% |
| Dinner | 7:30 PM | 24% | 20% | 27% |
The schedule is fixed for all 120 days. Phase affects only sets, reps, and weight — not which movement is trained on which day.
| Day | Session | Primary focus |
|---|---|---|
| Monday | Upper A | Compound push and pull; chest, back, shoulders, triceps |
| Tuesday | Lower A | Quad dominant; leg press, goblet squat, hamstring curl, reverse lunge |
| Wednesday | Cardio | Conditioning; walk to KB circuit to HIIT across phases |
| Thursday | Upper B | Incline press, lat pulldown, lateral raise, face pull, bicep curl |
| Friday | Cardio | Active recovery; steady-state walk or incline walk |
| Saturday | Lower B | Posterior chain; RDL, hip thrust, leg extension, kettlebell swing |
| Sunday | Rest | Full rest |
All gym days include an evening session: foam rolling, full-body stretch, dead bug core hold, and kegel holds.
| Phase | Days | Sets | Reps (compound) | Reps (isolation) |
|---|---|---|---|---|
| 1 — Foundation | 1-30 | 3 | 12 | 15 |
| 2 — Volume | 31-60 | 4 | 10 | 12 |
| 3 — Strength | 61-90 | 4 | 8 | 10 |
| 4 — Peak | 91-120 | 4 | 6-8 | 10-12 |
Every exercise tracks a current working weight in Models/WorkoutProgression.swift. Rules applied after each logged session:
Weight increments by exercise category:
| Category | Increment per session |
|---|---|
| Leg press (both sides) | 5.0 kg |
| Kettlebell swing | 4.0 kg (tracks standard KB sizes: 12, 16, 20, 24, 28, 32 kg) |
| All other lower body | 2.5 kg |
| All upper body | 2.5 kg |
| Bodyweight and cardio | None |
Starting weights are calibrated for a 90 kg user and scaled linearly for other body weights, then rounded to the nearest 2.5 kg.
The program is designed around the following equipment and is compatible with any small commercial gym.
| Equipment | Used for |
|---|---|
| Hoist Dual Cable Functional Trainer (HD-3000) | Lat pulldown (wide and narrow grip), cable row, cable face pull, cable tricep pushdown; pull-up bar for hanging knee raise |
| Hoist Leg Press | Quad-stance (Tuesday), glute-stance (Saturday) |
| Hoist Leg Extension/Curl combo | Leg extension, hamstring curl |
| Adjustable FID bench | Flat dumbbell bench press, incline dumbbell press, dumbbell hip thrust |
| Dumbbells (full rack) | Shoulder press, bicep curl, lateral raise, Romanian deadlift, reverse lunge, goblet squat |
| Kettlebells | Goblet squat, kettlebell swing |
| Foam roller | Evening recovery |
| Bodyweight | Plank, dead bug, kegels |
All machine exercises have noted alternatives for users without gym access.
Protein gram amounts shown in the meal plan are raw weight — weigh before cooking, as the ingredient comes off the store packaging. This is the standard used by USDA, MyFitnessPal, and Cronometer, and matches how food is bought and portioned in practice.
| Source | USDA protein per 100g raw | Gram formula |
|---|---|---|
| Chicken breast (boneless, skinless) | 23.2 g | target_protein x (100 / 23.2) |
| Tilapia / white fish | 20.1 g | target_protein x (100 / 20.1) |
| Eggs | 6.0 g per large egg | ceil(target_protein / 6) |
Source: USDA FoodData Central SR Legacy — FDC #171477 (chicken), FDC #175177 (tilapia), FDC #748967 (egg).
Note on basa: basa (Pangasius hypophthalmus) contains approximately 14.7 g protein per 100 g raw per FSANZ NUTTAB, which is materially lower than tilapia. The app uses tilapia values for all white fish. Users buying basa should either increase the portion or substitute tilapia.
Carbohydrate gram amounts are dry weight — measure before adding water or cooking.
| Source | USDA carbs per 100g dry | Reference portion | Carbs in reference portion |
|---|---|---|---|
| Rolled oats | 66.3 g | 40 g dry | 26.5 g |
| Basmati rice (long-grain white) | 79.3 g | 50 g dry | 39.7 g |
Source: USDA FoodData Central SR Legacy — FDC #173904 (oats), FDC #169756 (rice).
Gram formulas:
Oats: carb_target x (40 / 26.5)
Rice: carb_target x (50 / 39.7)
Cooked rice reference weight shown in the UI: dry_weight x 2.4 (1:1.5 water absorption method, stovetop).
Fruit carbs are shown for reference only and are not included in the meal macro calculation.
| Fruit | Portion | Approximate carbs |
|---|---|---|
| Banana | 1 medium (118 g) | 27 g |
| Apple | 1 medium (182 g) | 25 g |
| Grapes | 150 g | 26 g |
| Pear | 1 medium (178 g) | 27 g |
Source: USDA FoodData Central.
| Ingredient | When to measure | Unit shown in app |
|---|---|---|
| Chicken breast | Before cooking (raw) | grams |
| White fish / tilapia | Before cooking (raw) | grams |
| Eggs | Count only | whole eggs |
| Oats | Before cooking (dry) | grams |
| Basmati rice | Before cooking (dry) | grams |
| Almonds | Pre-portioned from bag | grams (20 g per portion) |
| Greek yogurt | As purchased | grams (180 g) |
These are fixed portions and are not recalculated from individual macro targets.
| Option | Protein | Fat | Carbs | Calories |
|---|---|---|---|---|
| 3 eggs + 20 g almonds | 22 g | 20 g | 4 g | 275 kcal |
| 180 g plain nonfat Greek yogurt + 15 g almonds | 21 g | 9 g | 10 g | 193 kcal |
Almond values: USDA FDC #170567 — 20 g = 4.2 g protein, 10.0 g fat, 4.3 g carbs, 2.1 g fibre, 116 kcal.
Greek yogurt values: USDA nonfat plain — 180 g = 18.4 g protein, 1.3 g fat, 6.5 g carbs, 106 kcal.
BodyPhase/
HabitTrackerApp.swift App entry point (BodyPhaseApp)
Models/
DietPlan.swift All nutrition formulas, IntensityMode enum, FoodAmounts
Habit.swift HabitID enum, HabitDefinition, static daily schedule
HabitEntry.swift Logged habit completions (Codable)
WeightEntry.swift Daily weight log entries (Codable)
WorkoutModels.swift ExerciseDefinition, WorkoutSession, DaySession,
ProgramPhase, WorkoutSchedule
WorkoutProgression.swift ProgressionEngine, ProgramDayStore, WorkoutSet
ViewModels/
HabitStore.swift Single ObservableObject; all published state and
computed diet, workout, and body composition values
Views/
RootView.swift Tab bar container
OnboardingView.swift First-launch profile setup
DietView.swift Full nutrition plan, meal cards, prep guide
WorkoutView.swift Workout logging, set tracking
SettingsView.swift Profile editing, notification toggle
WeeklyGridView.swift Daily habit grid with week navigation
BodyweightChartView.swift Weight trend chart
Services/
NotificationManager.swift UNUserNotificationCenter scheduling
PersistenceManager.swift JSON serialisation to UserDefaults / iCloud KV store
Extensions/
Color+Theme.swift App colour palette (dark theme)
Date+Week.swift ISO week and dateKey helpers
project.yml XcodeGen project specification
HabitStore is the single source of truth, injected as an @EnvironmentObject from the app entry point. No view owns persistent state independently. All reads and writes go through HabitStore.
Key reactive chain triggered by a new weight log entry:
latestWeight changes (Published)
-> estimatedBodyFatPct recomputes (Deurenberg formula)
-> leanBodyMassKg recomputes (weight x (1 - BF%))
-> intensityMode recomputes (BF gap -> cut/recomp/bulk)
-> targetKcal recomputes (TDEE - deficit, floored)
-> dailyProteinG recomputes (LBM x 2.2)
-> dailyFatG recomputes (25% kcal or 0.5 g/kg floor)
-> dailyCarbsG recomputes (remaining calories / 4)
-> bodyFatTargetWeightKg recomputes (LBM / (1 - targetBF%))
-> weeksToTargetWeight recomputes (weight delta / weekly loss rate)
-> DietView re-renders with all updated values
All data is stored on-device. There is no server, no user account, and no analytics.
| Data | UserDefaults key | Format |
|---|---|---|
| Profile settings | Individual keys (heightCm, age, isMale, etc.) | Primitive types |
| Habit completions | habitEntries_v1 | JSON-encoded array |
| Weight log | weightEntries_v1 | JSON-encoded array |
| Workout sets | workoutSets_v1 | JSON-encoded array |
| Exercise progressions | exerciseProgressions_v1 | JSON-encoded array |
| Program day tracking | programDayStore_v1 | JSON-encoded struct |
PersistenceManager attempts NSUbiquitousKeyValueStore (iCloud key-value store) first for automatic cross-device sync, and falls back to UserDefaults when iCloud is unavailable. The iCloud entitlement is not required for the app to function.
BodyPhase schedules 15 daily reminders covering wake-up hydration, each meal, workout slots, and wind-down. They are toggled in Settings.
project.yml is the XcodeGen specification for BodyPhase.xcodeproj. The .xcodeproj is committed to the repository so no build tools are required to open it. It can be regenerated at any time:
xcodegen generate
Run this after adding or removing source files, changing deployment targets, or modifying build settings. Do not edit project.pbxproj directly.
MIT License — 2026 debugmonk
See LICENSE for full terms.