Files
FamilyMealPlanner/CLAUDE.md
T
alexandrev-tibco c0b485387d 2.1.0: app de Apple Watch, complicacion y widget semanal en iOS
- Target MealMoodWatch (watchOS 10, SwiftUI): vista de hoy y de la
  semana en TabView vertical; recibe el snapshot del iPhone por
  WatchConnectivity (updateApplicationContext) y lo guarda en su app
  group para la complicacion
- Target MealMoodWatchWidget: complicacion accessoryRectangular/
  Circular/Inline con las comidas de hoy (heuristica comida/cena por
  hora); icono del watch generado desde el logo
- WatchWeekPayload compartido entre iOS app, widget iOS, watch app y
  complicacion — llega ya localizado desde el iPhone
- Widget iOS nuevo "Semana" (systemMedium/Large) con los 7 dias;
  el widget de hoy pasa a WidgetBundle
- Targets creados via gema xcodeproj (sin abrir Xcode); embed del watch
  antes del script de Crashlytics para evitar ciclo de build

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013H6bXqGX1ygwib1Dm3n3UG
2026-09-08 16:38:58 +02:00

4.3 KiB

MealMood — Project Context for Claude Code

App

  • Name: MealMood: Family Meal Planner
  • Bundle ID: com.alexandrevazquez.mealmood
  • Platform: iOS/iPadOS (also runs as iOS app on Mac)
  • Current version: 2.1.0 (dev; 2.0.5 build 78 in App Store review)
  • Main branch: 1.0.1 (production), feature branches named after versions

Branch ↔ version convention (IMPORTANT)

The active dev branch is named after the current MARKETING_VERSION, not the one it was created with. Whenever the iOS app version changes, rename the branch to match:

# after bumping MARKETING_VERSION in the Xcode project to e.g. 1.1.6
git branch -m <old-version> 1.1.6   # e.g. git branch -m 1.1.5 1.1.6

Right now: MARKETING_VERSION = 2.1.0 → active branch is 2.1.0. Keep them in sync. (The build number bump within the same version does NOT rename the branch — only a version change does.)

MARKETING_VERSION in the Xcode project does NOT drive the shipped version. Both Info.plists hardcode their values, so bumping the project setting alone builds an IPA with the old version and App Store Connect rejects it (90062: must contain a higher version than that of the previously approved version). A version bump means editing three places, and fastlane's increment_build_number only handles the app's build number:

What Where Bumped by fastlane?
App version MealMood/Resources/Info.plistCFBundleShortVersionString by hand
App build MealMood/Resources/Info.plist + pbxproj
Widget version + build MealMoodWidget/Info.plist by hand

The widget must match the app or the upload is rejected for a version mismatch. Since fastlane beta increments the build as its first step, set the widget to the next build number before running it:

plutil -replace CFBundleShortVersionString -string "<version>" MealMood/Resources/Info.plist
plutil -replace CFBundleShortVersionString -string "<version>" MealMoodWidget/Info.plist
plutil -replace CFBundleVersion -string "<next build>" MealMoodWidget/Info.plist

Credentials — all managed via pass (GPG-encrypted, syncs to Gitea)

Never hardcode credentials. Always read from pass:

# Apple
pass show apple/mealmood/apple-id                          # alexandre.vazquez@gmail.com
pass show apple/mealmood/developer-team-id                 # 2825Q76T7H
pass show apple/mealmood/appstore-connect-team-id          # 128443966
pass show apple/mealmood/app-store-app-id                  # 6759255553
pass show apple/mealmood/app-specific-password-fastlane    # for fastlane uploads

# Firebase
pass show firebase/mealmood/api-key
pass show firebase/mealmood/project-id
pass show firebase/mealmood/google-app-id

# AdMob
pass show admob/mealmood/app-id
pass show admob/mealmood/banner-home-unit-id

TestFlight upload

export FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD=$(pass show apple/mealmood/app-specific-password-fastlane)
fastlane beta

Stack

  • SwiftUI + SwiftData (iOS 17+)
  • Firebase Analytics (AnalyticsService.swift)
  • Google AdMob (banner, free tier only, hidden on Mac)
  • StoreKit 2 (subscription com.mealmood.premium.monthly)
  • iCloud KV sync (ICloudSyncService.swift) — does NOT sync isPremium
  • Widget extension (MealMoodWidget/) — requires App Group group.com.alexandrevazquez.mealmood
  • Fastlane for CI/CD

Premium logic

  • PremiumSyncService is the source of truth for premium state transitions
  • isPremium is determined only by StoreKit — never by iCloud sync
  • Transaction.updates listener in ContentView catches purchases in real-time
  • Tests: MealMoodTests/PremiumSyncServiceTests.swift

Key files

File Purpose
MealMood/ContentView.swift App root, premium sync, Transaction.updates listener
MealMood/Services/PremiumSyncService.swift Premium state machine (testable)
MealMood/Services/StoreManager.swift StoreKit 2 purchase/restore
MealMood/Services/ICloudSyncService.swift Cross-device sync (dishes, tags, plans — NOT isPremium)
MealMood/Views/Home/WeekPlanShareView.swift Export styles (default, school, vertical A4)
MealMoodWidget/MealMoodWidget.swift Home screen widget
fastlane/Fastfile beta and release lanes
fastlane/metadata/ App Store metadata in 6 languages