Caluru

Caluru - Runbook

This document provides debugging checklists, common issues, and operational guidance.


🔍 Debug Checklist

App Not Starting / Crashes on Launch

Data Not Persisting

UI Not Updating

Camera Not Working


🐛 Common Issues & Solutions

Issue: “Multiple commands produce Info.plist”

Cause: Manual Info.plist file conflicts with auto-generated one.

Solution:

# Delete any Info.plist in source folder
rm Caluru/Caluru/Info.plist

Issue: “Instance method is not available due to missing import of module ‘SwiftData’”

Cause: Missing import statement.

Solution:

import SwiftData  // Add to file

Issue: “Reference to captured var ‘self’ in concurrently-executing code”

Cause: Swift 6 strict concurrency.

Solution:

// Option 1: Mark class as @MainActor
@MainActor @Observable
class MyViewModel { ... }

// Option 2: Capture self explicitly
Task { @MainActor in
    self.property = value
}

Issue: Seed data not appearing

Cause: Seed check finds existing profile (or CloudKit synced profile).

Solution:

  1. Delete app from device/simulator
  2. Clean build folder
  3. Run again

Or temporarily modify SeedDataService:

// Force re-seed (for debugging only)
guard false else { // Change from existingProfiles.isEmpty

Issue: Assistant / photo scan “no pude conectarme” or 401

Cause: No network, or user not signed in (proxy requires JWT).

Solution:

Issue: Onboarding shows again after reinstall

Cause: CloudKit may not have synced profile yet; hasCompletedOnboarding is false until profile appears.

Solution: Wait a few seconds (app uses ~2s delay) or sign in with Apple so profile can sync. If testing, complete onboarding again.

Issue: SwiftData predicate errors

Cause: Complex predicates not supported.

Solution:

// Bad: Complex computed property
#Predicate { $0.mealTypeEnum == .desayuno }

// Good: Compare raw value
#Predicate { $0.mealType == "desayuno" }

Issue: JSON decoding fails silently

Cause: Codable struct mismatch.

Solution:

// Add explicit error handling
var entries: [MealEntry] {
    get {
        guard let data = entriesData else { return [] }
        do {
            return try JSONDecoder().decode([MealEntry].self, from: data)
        } catch {
            print("Decoding error: \(error)")
            return []
        }
    }
}

📋 Where Logs Are Emitted

Console Output

Location Log Type
SeedDataService.seedIfNeeded() “Seeding initial data…” / “Seed data already exists”
CaluruApp ModelContainer fatalError on creation failure

Adding Debug Logs

// In ViewModels
func loadData(context: ModelContext) {
    #if DEBUG
    print("[HomeViewModel] Loading data for \(selectedDate)")
    #endif
    
    // ... implementation
    
    #if DEBUG
    print("[HomeViewModel] Loaded \(dailyLog?.entries.count ?? 0) entries")
    #endif
}

Xcode Debug Tools

  1. View Debugger: Debug → View Debugging → Capture View Hierarchy
  2. Memory Graph: Debug → Debug Memory Graph
  3. SwiftData Logging: Add launch argument -com.apple.CoreData.Logging.stderr 1

🔄 How to Reproduce Common Bugs

Bug: Progress ring shows wrong value

Reproduce:

  1. Add food to Desayuno
  2. Switch to different date
  3. Switch back

Check:

Bug: Shopping list doesn’t update

Reproduce:

  1. Add recipe to weekly plan
  2. Go to shopping list
  3. List is empty

Check:

Bug: Assistant gives wrong calorie count

Reproduce:

  1. Log some food
  2. Ask “¿cuántas calorías?”
  3. Response shows 0

Check:


⚡ Performance Hot Spots

JSON Encoding/Decoding

Location: DailyLog.entries, Recipe.ingredients, WeeklyPlan.days

Symptom: Slow UI when accessing nested data

Mitigation:

Large Recipe Grid

Location: RecipesView LazyVGrid

Symptom: Scroll jank with many recipes

Mitigation:

Shopping List Regeneration

Location: ShoppingListBuilder.build(from:)

Symptom: Slow when plan has many recipes

Mitigation:


🗄 Data Recovery

Export Data (Future Feature)

Currently not implemented. To manually export:

// Add to a debug menu
func exportData() {
    let descriptor = FetchDescriptor<DailyLog>()
    let logs = try? modelContext.fetch(descriptor)
    let data = try? JSONEncoder().encode(logs)
    // Save to file
}

Reset to Clean State

// Nuclear option - delete all data
func resetAllData(context: ModelContext) {
    try? context.delete(model: UserProfile.self)
    try? context.delete(model: FoodItem.self)
    try? context.delete(model: Recipe.self)
    try? context.delete(model: DailyLog.self)
    try? context.delete(model: WeeklyPlan.self)
    try? context.delete(model: ShoppingList.self)
    try? context.save()
    
    // Re-seed
    SeedDataService.seedIfNeeded(context: context)
}

📊 Monitoring Checklist

Before Release

User-Reported Issues

  1. Get iOS version and device model
  2. Ask for reproduction steps
  3. Check if issue is data-related (offer to reset)
  4. Check console logs if possible

Groups UGC reports (Guideline 1.2)

  1. Open Supabase → Table Editor → content_reports where status = 'pending'.
  2. Review within 24 hours.
  3. To hide content for the group: as a family admin in-app is not required — run SQL as service_role, or have a group admin call hide_family_ugc(content_type, content_id, reason, report_id).
  4. Mark handled: update content_reports set status = 'actioned', reviewed_at = now() where id = … (or let hide_family_ugc do it when p_source_report_id is set).
  5. For abusive members: users can Block in-app; admins can still remove members.

🔐 Security Considerations

Data Storage

Backend / Auth