Caluru

Caluru - Project Documentation

A comprehensive iOS nutrition tracking and meal planning app built with SwiftUI, SwiftData, Supabase, and CloudKit.

🍎 What is Caluru?

Caluru is a hybrid local + cloud nutrition app that helps users:

Language

The app UI is in Spanish (localized strings, labels, recipe content).


πŸ›  Tech Stack

Technology Version / Note Purpose
Swift 5.9+ Language
SwiftUI iOS 17+ UI
SwiftData iOS 17+ Local persistence
CloudKit iOS 17+ Sync (private container iCloud.com.agarcia.caluru)
Supabase β€” Auth (Sign in with Apple), Edge Function (openai-proxy)
OpenAI API β€” GPT-4o-mini (Assistant), GPT-4o (food photo); key server-side only
Open Food Facts β€” Barcode product lookup
AVFoundation β€” Camera / barcode scanning
UserNotifications β€” Meal, goal, streak, weight reminders
SF Symbols β€” Icons

Requirements


πŸš€ How to Run Locally

Step 1: Clone/Open the Project

cd /path/to/Caluru
open Caluru.xcodeproj

Step 2: Select Target

Step 3: Build and Run

# Or press Cmd+R in Xcode

Step 4: First Launch


πŸ“ Project Structure

Caluru/                    # repo root
β”œβ”€β”€ Caluru.xcodeproj
β”œβ”€β”€ Caluru/                  # app source
    β”œβ”€β”€ CaluruApp.swift     # @main; ModelContainer (SwiftData + CloudKit); theme; notifications; AppDelegate (notifications)
    β”œβ”€β”€ ContentView.swift        # β†’ MainTabView
    β”œβ”€β”€ Config.swift            # SupabaseConfig (url, anonKey from Info.plist)
    β”‚
    β”œβ”€β”€ Models/
    β”‚   β”œβ”€β”€ UserProfile.swift   # Extended: onboarding, targets, legacy (dietType, allergies, etc.)
    β”‚   β”œβ”€β”€ FoodItem, Recipe, Ingredient, DailyLog, MealEntry, LoggedItem
    β”‚   β”œβ”€β”€ WeeklyPlan, PlannedDay, ShoppingList, ShoppingListItem
    β”‚   β”œβ”€β”€ WeightEntry.swift
    β”‚   β”œβ”€β”€ OnboardingState.swift, OnboardingTypes.swift  # Onboarding enums & draft state
    β”‚   β”œβ”€β”€ AssistantAction.swift  # LogMeal, CreateRecipe, GenerateMealPlan, AdjustTarget, viewRecipe, viewProgress
    β”‚   └── CloudRecipe.swift (if used)
    β”‚
    β”œβ”€β”€ Views/
    β”‚   β”œβ”€β”€ MainTabView.swift   # 5 tabs; onboarding fullScreenCover; progressive profiling overlay
    β”‚   β”œβ”€β”€ Home/ (HomeView, AddFoodSheet)
    β”‚   β”œβ”€β”€ Plan/ (WeeklyPlanView)
    β”‚   β”œβ”€β”€ Scan/ (ScanView)
    β”‚   β”œβ”€β”€ Recipes/ (RecipesView, RecipeDetailView, AddRecipeSheet)
    β”‚   β”œβ”€β”€ ShoppingList/ (ShoppingListView)
    β”‚   β”œβ”€β”€ Assistant/ (AssistantView)
    β”‚   β”œβ”€β”€ Profile/ (ProfileView, AppPreferencesView, WeightTrackingView, NotificationsSettingsView, LegalView)
    β”‚   └── Onboarding/
    β”‚       β”œβ”€β”€ OnboardingFlowView.swift
    β”‚       β”œβ”€β”€ Screens/ (S0Welcome, S1WelcomeGoal, SMotivations, S15Confidence, EncouragementGoals, SQuickBasics, SBodyMetrics, S8–S10, S12–S13, SMealsDessert, SDislikedFoods, STargetsReveal, SignIn, S0aName, S0bUsername, SFirstLogNudge, AdjustTargetsSheet)
    β”‚       └── Components/ (shared onboarding UI in OnboardingFlowView)
    β”‚
    β”œβ”€β”€ ViewModels/
    β”‚   β”œβ”€β”€ HomeViewModel, WeeklyPlanViewModel, RecipesViewModel
    β”‚   β”œβ”€β”€ ScanViewModel, ShoppingListViewModel, AssistantViewModel
    β”‚   β”œβ”€β”€ ProfileViewModel, OnboardingViewModel
    β”‚   └── (Models/HomeViewModel.swift if present)
    β”‚
    β”œβ”€β”€ Services/
    β”‚   β”œβ”€β”€ NutritionCalculator, SeedDataService, ShoppingListBuilder
    β”‚   β”œβ”€β”€ AssistantResponseService.swift  # AIAssistantService (GPT-4o-mini via OpenAIProxyService)
    β”‚   β”œβ”€β”€ OpenAIProxyService   # Calls Supabase Edge Function openai-proxy
    β”‚   β”œβ”€β”€ OpenFoodFactsService # Barcode lookup
    β”‚   β”œβ”€β”€ FoodRecognitionService # Photo β†’ GPT-4o Vision via proxy
    β”‚   β”œβ”€β”€ MockFoodRecognitionService # Fallback/offline
    β”‚   β”œβ”€β”€ SupabaseClient.swift # SupabaseManager (shared client)
    β”‚   β”œβ”€β”€ AuthService         # Sign in with Apple, session restore, signOut
    β”‚   β”œβ”€β”€ TargetCalculator   # Mifflin-St Jeor; onboarding targets
    β”‚   β”œβ”€β”€ MealPlanGenerator  # Auto-generate weekly plan
    β”‚   β”œβ”€β”€ ProgressiveProfilingService # Post-onboarding profile cards
    β”‚   β”œβ”€β”€ NotificationService # Meal, goal, streak, weight reminders
    β”‚   β”œβ”€β”€ AccountResetService # Reset data / re-onboard
    β”‚   β”œβ”€β”€ RecipeService, OnboardingAnalytics
    β”‚   └── ...
    β”‚
    β”œβ”€β”€ DesignSystem/
    β”‚   β”œβ”€β”€ AppColors, AppTypography, AppSpacing
    β”‚   └── Components/ (Card, PillSegment, MacroChip, PrimaryButton, IconButton, EmptyState, ProgressRing, SearchBar, RecipeCard, StreakBadge, ...)
    β”‚
    └── Assets.xcassets/

βš™οΈ Configuration

Nutrition Targets

Default targets are defined in Services/SeedDataService.swift:

UserProfile(
    calorieTarget: 3500,
    proteinTarget: 190,
    carbsTarget: 250,
    fatTarget: 85,
    language: "es"
)

Color Palette

Edit DesignSystem/AppColors.swift:

Color Hex Usage
accentGreen #8FB996 Primary accent, buttons, progress
background #F5F6F8 Screen backgrounds
cardBackground #FFFFFF Card surfaces
textPrimary #1A1A2E Main text
textSecondary #6B7280 Secondary text

Supabase (Info.plist or xcconfig)

Permissions

The app does not request microphone access (NSMicrophoneUsageDescription is not present).

See App Store Connect listing for store metadata that matches in-app branding.


πŸ”¨ Build Commands

Command Action
Cmd+R Build and run
Cmd+B Build only
Cmd+U Run tests
Cmd+Shift+K Clean build folder

Testing

# Unit tests
xcodebuild test -scheme Caluru -destination 'platform=iOS Simulator,name=iPhone 15'

πŸ› Common Troubleshooting

β€œMultiple commands produce Info.plist”

The project auto-generates Info.plist. Delete any manually created Info.plist file in the source folder.

SwiftData not persisting

Ensure the ModelContainer is configured with isStoredInMemoryOnly: false in CaluruApp.swift.

Camera not working in simulator

Camera features require a physical device. The simulator will show a blank preview.

Seed data not appearing

  1. Delete the app from the simulator/device
  2. Clean build folder (Cmd+Shift+K)
  3. Run again to trigger fresh seed

Swift 6 concurrency warnings

The project uses SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor. ViewModels that access UI state from async contexts must be marked @MainActor.


πŸ“š Additional Documentation


πŸ“„ License

This project is for educational and demonstration purposes.