Pasos para correr DucodeApp desde cero en local y, opcionalmente, distribuir a TestFlight.
| Requisito | Versión / detalle |
|---|---|
| macOS | Sonoma 14+ recomendado |
| Xcode | 26.0 o superior |
| iOS deployment target | 26.0 |
| Swift | 5.9 |
| iPhone físico | iPhone 15 Pro / Pro Max o iPhone 16+ (Apple Intelligence) |
| Cuenta Whoop developer | Gratis en developer.whoop.com |
| Apple Developer Program | De pago (~99 USD/año) para distribuir a TestFlight |
| Herramientas CLI | brew install xcodegen y, opcionalmente, gem install fastlane |
Nota: el simulador puede correr la app pero (a) el OAuth de Whoop funciona mejor en dispositivo físico, y (b) Apple Intelligence solo funciona si tu Mac la soporta y la tienes activa. Para probar la experiencia real, usa un iPhone físico compatible.
- Ve a developer.whoop.com y entra con tu cuenta de Whoop.
- Acepta los términos de desarrollador.
- Click "Create App" en el dashboard.
- Llena los campos:
- App Name: DucodeApp
- Description: Personal nutrition app
- App Website: cualquier URL válida
- En la sección "Redirect URLs", agrega:
ducode://oauth/callback - En "Scopes", habilita:
read:recoveryread:cyclesread:workoutread:sleepread:profileread:body_measurement
El scope
offlinese solicita en runtime (es necesario para obtenerrefresh_token); no se configura en el dashboard.
Guarda en un lugar seguro:
- Client ID (UUID-style)
- Client Secret (string largo)
La app usa Foundation Models (on-device). No requiere API key, no tiene costo y los datos no salen del dispositivo.
- Ve a Ajustes → Apple Intelligence y Siri.
- Activa Apple Intelligence.
- Espera a que el modelo se descargue (puede tardar varios minutos la primera vez; hazlo conectado a Wi-Fi y con carga).
La app detecta automáticamente el estado y muestra un mensaje si no está disponible (dispositivo no compatible, Apple Intelligence apagada, modelo descargándose). Ver ./INTEGRATIONS.md.
Dispositivos compatibles: iPhone 15 Pro, 15 Pro Max, iPhone 16, 16 Plus, 16 Pro, 16 Pro Max o superior.
Solo se requiere Whoop. Claude API ya no se usa.
cd /ruta/a/ducode_app
cp Secrets.xcconfig.template Secrets.xcconfigEdita Secrets.xcconfig:
WHOOP_CLIENT_ID = tu-client-id-aqui
WHOOP_CLIENT_SECRET = tu-client-secret-aqui
Secrets.xcconfig está en .gitignore. Nunca lo commitees.
Si vas a correr la app firmada (en dispositivo físico o TestFlight) necesitas habilitar HealthKit en el App ID.
- Entra a developer.apple.com → Account → Certificates, IDs & Profiles → Identifiers.
- Busca o crea el App ID
com.ducode.app. - En la lista de Capabilities, marca HealthKit.
- Save.
Si firmas con "Automatic Signing" en Xcode, el provisioning profile se regenerará al siguiente build.
- Ve a appstoreconnect.apple.com → My Apps → "+" → New App.
- Configura:
- Platform: iOS
- Name: Ducode (o el nombre que prefieras)
- Primary Language: Spanish (Spain) o el que apliques
- Bundle ID:
com.ducode.app(debe coincidir conproject.yml) - SKU: cualquier identificador único, p.ej.
ducode-app-001
- Save.
No necesitas llenar metadatos de App Store si solo vas a usar TestFlight Internal Testing.
cd /ruta/a/ducode_app
xcodegen generate
open DucodeApp.xcodeprojproject.yml define todo: bundle ID, deployment target (26.0), entitlements, URL scheme, background modes, Info.plist properties (incluyendo las descripciones de HealthKit).
- Conecta tu iPhone físico (recomendado para OAuth y Apple Intelligence).
- Selecciona el dispositivo en el dropdown de schemes.
- La primera vez Xcode pedirá configurar el iPhone para desarrollo. Acepta.
- Cmd+R para compilar y correr.
- En el iPhone: Settings → General → VPN & Device Management → confía en tu certificado de desarrollador.
./scripts/archive.shGenera build_output/DucodeApp.xcarchive y exporta un .ipa listo para subir vía Transporter o xcrun altool.
Ver ./DISTRIBUTION.md para el detalle completo. Resumen:
- Entra a App Store Connect → Users and Access → Integrations → App Store Connect API.
- Click "+" para generar una nueva key.
- Name: Ducode CI
- Access: App Manager
- Descarga el archivo
.p8(solo se descarga una vez). - Guárdalo en
envs/AuthKey_<KEY_ID>.p8. El nombre debe respetar el formatoAuthKey_<KEY_ID>.p8porque Fastlane y App Store Connect lo derivan del nombre. - El archivo
envs/está en.gitignore.
Crea el archivo fastlane/.env (no se commitea):
ASC_KEY_ID=AG95PDMY96
ASC_ISSUER_ID=<tu issuer id>
El Issuer ID aparece arriba de la lista de keys en App Store Connect.
fastlane betaEsto:
- Regenera el proyecto con
xcodegen. - Lee el último build number en TestFlight y bump al siguiente.
- Build con
gym(wrapper dexcodebuild). - Sube a TestFlight con
pilot. - No envía a Beta App Review (
skip_submission: true).
- En App Store Connect → tu app → TestFlight → Internal Testing.
- Crea un grupo (p.ej. "Friends").
- Agrega como Users (en Users and Access) a las cuentas Apple ID que quieras incluir, con el rol App Manager o Developer.
- Asigna esos Users al grupo.
- Cuando el build termine de procesarse, asígnalo al grupo.
- Los testers reciben un email para instalar TestFlight y la app.
Internal Testing permite hasta 100 usuarios sin Beta App Review. Si necesitas más, usa External Testing (sí requiere review).
- Verifica que el deployment target sea iOS 26.0 (
project.yml). - Product → Clean Build Folder (Cmd+Shift+K) y vuelve a compilar.
- Verifica que el URL scheme en
Info.plistseaducode(lo defineproject.yml). - Verifica que el redirect URL en el dashboard de Whoop sea exactamente
ducode://oauth/callback. - Revisa los logs en Xcode buscando
[OAuth]y[Whoop].
- Confirma que el dispositivo soporta Apple Intelligence (iPhone 15 Pro+ o iPhone 16+).
- Activa Apple Intelligence en Ajustes → Apple Intelligence y Siri.
- Si dice "modelo descargándose", espera a que termine. Pulsa "Reintentar" en la app.
- El refresh token puede haber expirado. Haz logout (botón superior izquierdo en el Dashboard) y vuelve a conectar.
- Verifica que las credenciales de
Secrets.xcconfigcorrespondan a la app correcta en developer.whoop.com.
- Habilita HealthKit en el App ID en Apple Developer Portal (paso 4).
- Si firmas con "Automatic Signing", borra el DerivedData y rebuild para refrescar el provisioning profile.
- Verifica que
DucodeApp/DucodeApp.entitlementsexista y esté referenciado enproject.ymlcomoCODE_SIGN_ENTITLEMENTS.
- Asegúrate de tener una membresía activa de Apple Developer Program.
- En Xcode: Settings → Accounts → tu Apple ID → Manage Certificates → confirma que hay un Distribution Certificate vigente.
- Re-lanza
fastlane beta(gym refresca los profiles automáticamente consigningStyle: "automatic").
- El lane
betalee el último build vialatest_testflight_build_numbery suma 1. Si dos personas suben en paralelo, puede haber colisión. Espera a que el build anterior termine de procesar y reintenta.
- Asegúrate que
AppIcon.appiconsetno tenga canal alpha en ningún tamaño (Apple lo rechaza). Verifica enDucodeApp/Assets.xcassets/AppIcon.appiconset.
ducode_app/
├─ DucodeApp/
│ ├─ App/
│ │ └─ DucodeApp.swift Entry point + ModelContainer + onOpenURL
│ ├─ Models/
│ │ ├─ Alimento.swift
│ │ ├─ Objetivo.swift
│ │ ├─ DiaRegistro.swift
│ │ ├─ WhoopHistory.swift WhoopDailySnapshot + WhoopWeeklyAnalytics
│ │ ├─ UserPreferences.swift + enums Cuisine y DietaryRestriction
│ │ └─ ChatMessage.swift
│ ├─ Services/
│ │ ├─ WhoopService.swift
│ │ ├─ WhoopSyncService.swift
│ │ ├─ NutritionAIService.swift Foundation Models para planes
│ │ ├─ CoachChatService.swift Foundation Models para chat
│ │ └─ HealthKitService.swift
│ ├─ Views/
│ │ ├─ ContentView.swift TabView + intro + onboarding
│ │ ├─ DashboardView.swift
│ │ ├─ Health360Card.swift
│ │ ├─ PreferenciasComidaView.swift
│ │ ├─ ObjetivoView.swift
│ │ ├─ WhoopHistoryView.swift
│ │ ├─ Coach/ CoachView, ChatBubbleView, MarkdownText
│ │ └─ Onboarding/ IntroView + OnboardingCoordinator + steps
│ ├─ Utilities/
│ │ └─ Config.swift AppConfig (lee Info.plist)
│ ├─ Info.plist
│ └─ DucodeApp.entitlements HealthKit
├─ DucodeApp.xcodeproj/ Generado por XcodeGen (gitignored)
├─ docs/ Esta documentación
├─ envs/
│ └─ AuthKey_<KEY_ID>.p8 App Store Connect API key (gitignored)
├─ fastlane/
│ ├─ Fastfile beta / build / latest lanes
│ ├─ Appfile
│ └─ .env ASC_KEY_ID, ASC_ISSUER_ID (gitignored)
├─ scripts/
│ ├─ archive.sh Build CLI puro (xcodebuild)
│ └─ upload.sh Subida CLI / Transporter
├─ Secrets.xcconfig Whoop credentials (gitignored)
├─ Secrets.xcconfig.template
├─ project.yml Configuración XcodeGen
└─ .gitignore
Secrets.xcconfig # WHOOP_CLIENT_ID y WHOOP_CLIENT_SECRET
│
▼
project.yml # Las inyecta a build settings
│
▼
Info.plist # Las expone como $(VARIABLE)
│
▼
AppConfig (Config.swift) # Las lee desde Bundle.main.infoDictionary
│
▼
WhoopService # Las usa para OAuth y API calls
Esta arquitectura evita hardcodear credenciales y permite cambiar valores sin tocar código.