Claude Terminal
Loading...
π Consistent calorie totals across all your devices
What we're fixing:
After your meals sync between your iPhone, iPad, and Mac, the total calories for the day should be exactly the same on all devices. Currently, different devices can show different calorie totals for the same dayβsometimes varying by several caloriesβand the numbers can even change when you swipe between days without making any edits. This task identifies why the day-level calorie calculation is inconsistent and ensures all devices show the same stable total after syncing.
After syncing meals and meal items between devices, the day's total energy value is inconsistent and unstable:
- βDifferent values on different devices: One device shows 7752 kcal, another shows 7759 kcal for the same day
- βValues changing without user action: Swiping the day back and forth caused the value to change: 7752 β 7758 β 7759 β 7754
- βOnly affects energy: Macros and micros appear to be consistent across devices
- βMeal-level totals are correct: The nutrient bar in the meal headers shows the same values on both devices
What's Consistent
- βIndividual meal energy values match between devices
- βMacro totals (protein, carbs, fat) are stable
- βMicro nutrient totals are stable
- βThe issue only affects the day-level energy total
What's Inconsistent
- βDay-level total energy calculation
- βValue changes spontaneously when:
- βSwiping between days
- βWaiting a few moments
- βNo user edits being made
1. Rounding/Precision Issues
The energy total might be calculated differently in different places:
- βSumming meal energy values (already rounded)
- βRecalculating from macros (protein Γ 4 + carbs Γ 4 + fat Γ 9)
- βDifferent rounding strategies causing drift
2. Sync/State Management Race Condition
- βBackground calculations updating while UI is refreshing
- βCoreData context conflicts between sync and UI layer
- βCached values not being invalidated properly after sync
3. Macro-to-Energy Conversion Inconsistency
- βSome foods might store pre-calculated energy values
- βOthers calculate energy from macros on-the-fly
- βDifferences in which approach is used in different contexts
4. Incomplete Sync State
- βSome meal items might not be fully synced/hydrated
- βRelated food data might be missing temporarily
- βCalculations might be running with incomplete data
Relevant Files to Investigate:
- βDay energy calculation logic
- βMeal item energy aggregation
- βSync merge logic for meals/items
- βCoreData relationship loading (faulting issues?)
Questions to Answer:
- βWhere is day-level energy calculated? Multiple places?
- βIs it stored or always calculated on-demand?
- βHow do we ensure all meal items are loaded when calculating?
- βAre there any background recalculations that could cause UI updates?
- βDay-level energy total should be identical on all devices after sync completes
- βValue should be stable and not change unless user edits data
- βCalculation should be deterministic regardless of when/where it runs
- βShould match the sum of individual meal energy values shown in headers
- β Both devices show identical energy totals after sync (within 1 kcal tolerance for rounding)
- β Energy value remains stable when swiping between days
- β Energy value doesn't change spontaneously without user action
- β Day total matches sum of meal header values exactly
- β Root cause identified and documented
- β Fix verified across multiple sync scenarios
Severity: High - This affects the core accuracy of the app's primary metric User Trust: Users rely on these numbers for tracking; inconsistency erodes confidence Data Integrity: Suggests potential broader issues with sync or calculation logic
Implementation Summary
Issue 1: Inconsistent calorie totals across devices
Root Cause: When displaying a day, DayDTO.recalculateNutrientTotals() correctly calculates totals from meals, but the recalculated values were never saved back to the Day model in the database. This caused:
- βDifferent devices showing different values (each had different stale cached values)
- βValues changing when swiping between days (stale cache vs fresh calculation)
- βSync pushing/pulling stale values
Fix in DayViewController.swift (lines 742-755):
// PXL-682: Save recalculated nutrient totals back to the Day model if they changed
let tolerance: Double = 0.01
let energyChanged = abs(fetchedDay.energyInKcal - dayDTO.energyInKcal) > tolerance
let carbChanged = abs(fetchedDay.carb - dayDTO.carb) > tolerance
let fatChanged = abs(fetchedDay.fat - dayDTO.fat) > tolerance
let proteinChanged = abs(fetchedDay.protein - dayDTO.protein) > tolerance
if energyChanged || carbChanged || fatChanged || proteinChanged {
fetchedDay.setNutrientTotals(fromDTO: dayDTO)
fetchedDay.markAsNeedingSync()
try? context.save()
}
Issue 2: Meal item moves being reverted after sync
Root Cause: In SyncCoordinator.swift, after calling applyTo(existingItem), the code unconditionally re-linked the item to the remote's meal ID - even when applyTo had correctly skipped applying remote data due to local pending changes.
Fix in SupabaseConversions.swift:
- β
SupabaseMealItem.applyTo()now returnsBoolindicating whether remote data was applied
Fix in SyncCoordinator.swift (lines 1302-1329):
// PXL-682: applyTo now returns whether remote data was applied
let remoteDataApplied = remoteItem.applyTo(existingItem)
// PXL-682: ONLY re-link if remote data was applied, otherwise local mealID is preserved
// This prevents reverting meal item moves when the item has pending local changes
if remoteDataApplied {
// ... re-link to remote's meal
}
Additional Improvement
In DayViewController.insertMeal() (lines 1686-1691):
- βWhen inserting a meal, update the day DTO's nutrient totals immediately
- βEnsures UI shows correct totals without waiting for refetch
Files Changed
- β
NutriKit/Refactor Inbox/UIKit/DayViewController.swift - β
Packages/App/Sources/App/Supabase/SupabaseConversions.swift - β
Packages/App/Sources/App/Supabase/SyncCoordinator.swift
Expected Results
- βDay totals will be identical on all devices after sync
- βValues will remain stable when swiping between days
- βDay totals will match the sum of meal values exactly
- βMeal item moves are preserved and not reverted by sync