Files
FamilyMealPlanner/CLAUDE.md
T
alexandrev-tibco d10517b356 2.0.1: bump MARKETING_VERSION 2.0 -> 2.0.1 (fix onboarding)
La 2.0 salio a la App Store el 2026-07-28 con los dos ultimos pasos del
onboarding sin mostrarse. 2.0.1 lleva ese fix.

El Info.plist del widget lleva la version hardcodeada y fastlane no la toca, asi
que se alinea a mano (2.0.1 / 65) antes de construir. Documentado en CLAUDE.md
para no volver a tropezar.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Ks8uUcMA9mjypVK7F2Pkt
2026-08-06 08:15:54 +02:00

3.7 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.0.1 (dev, beta-bound; 2.0 shipped to the App Store 2026-07-28)
  • 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.0.1 → active branch is 2.0.1. Keep them in sync. (The build number bump within the same version does NOT rename the branch — only a version change does.)

The widget version is not automated. MealMoodWidget/Info.plist hardcodes CFBundleShortVersionString and CFBundleVersion; fastlane's increment_build_number only touches the app target. Update the widget by hand to match the app before running fastlane beta, or App Store Connect rejects the upload for a version mismatch:

plutil -replace CFBundleShortVersionString -string "<version>" MealMoodWidget/Info.plist
plutil -replace CFBundleVersion -string "<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