El problema
Tres problemas conviven en muchos hogares y ninguna app del mercado los resuelve a la vez: mantener una dieta variada y equilibrada para cada miembro de la familia, reducir los alimentos desperdiciados por caducidad y eliminar la fatiga de decisión diaria ("¿qué cenamos hoy?").
Las soluciones existentes (MyFitnessPal, Lifesum, Cronómetro) son reactivas: registran lo que ya has comido. Faltaba una herramienta proactiva que conectara las necesidades biológicas de cada familiar con el inventario real de la despensa y generara un plan nutricional accionable.
La solución - interfaz
S.I.P.N.F. - Sistema Integral de Planificación Nutricional Familiar, conocido comercialmente como Gordito Family, es una app móvil Android que actúa como un ERP para el hogar: cruza necesidades nutricionales con inventario, planifica menús automáticos y genera la lista de la compra calculada al gramo.
La solución - arquitectura
Decisiones técnicas
- React Native + Expo (hoy SDK 54), no bare workflow. Una única base de código JS compilada a APK nativo. Pierdo acceso a algunos módulos nativos pero gano builds OTA, EAS y testing instantáneo en Expo Go. Para una app social donde la lógica nativa es mínima, el trade-off compensa.
- Firebase Firestore con
onSnapshotlisteners. Sincronización en tiempo real entre dispositivos sin servidor propio. Tres listeners simultáneos (usuarios, despensa, lista de la compra) con cleanup explícito al desmontar para evitar memory leaks. - Reglas Firestore con denegación por defecto. Aislamiento total por UID en cada documento (
users/{uid}/...). - Context API en vez de Redux. La estructura (3 colecciones) no justifica el boilerplate de Redux. Context con providers limpios y suficiente para escalar a esta app.
- Algoritmo heurístico, no ML. El generador de menús aplica reglas explícitas (Mifflin-St Jeor + factor de actividad + restricciones por comida + priorización de inventario). Inspeccionable, depurable, sin coste de inferencia.
- Configuración en
.env(EXPO_PUBLIC_*). Inyectada en compilación y fuera del repositorio.
Motor nutricional - Mifflin-St Jeor
TMB = (10 × peso) + (6.25 × altura) − (5 × edad) + A
(A = +5 hombre, −161 mujer)
TDEE = TMB × factor de actividadMacros ajustados por objetivo (5 perfiles): pérdida (déficit 400 kcal · 2.2 g/kg proteína), mantenimiento, volumen, definición, recomposición. Cada miembro de la familia tiene los suyos.
Versión 1.1: de TFC a app que uso cada día
Entregar el TFC no fue el final. Seguí usando la app en casa y todo lo que echaba en falta acabó en la versión 1.1:
- Módulo de entrenamiento con rutinas por fases (del circuito inicial a una rutina dividida), biblioteca de ejercicios con vídeo y gasto energético estimado por método MET, que ajusta los macros del día.
- Recetas compuestas que calculan sus macros a partir de los ingredientes.
- Historial con calendario, gráfico de evolución de peso y menú semanal imprimible en un solo folio.
- Suplementos editables por usuario, que suman a los macros y salen en la impresión.
- Notificación semanal para planificar la compra.
GordiBot: un asistente de IA que no se inventa números
Sobre todo eso añadí GordiBot, un asistente con modelo de lenguaje (Ollama Cloud, con cascada de cinco modelos) que conoce el menú y la rutina del día. La regla que me impuse fue sencilla: el bot no calcula, usa herramientas. Los LLM se equivocan sumando, así que cada número sale de una de sus 17 herramientas, que llaman a las mismas calculadoras que el resto de la app.
- Ninguna herramienta escribe. Si el bot propone apuntar el peso o marcar una comida, la app lo enseña como tarjeta y solo se guarda si el usuario pulsa Confirmar.
- Datos separados de instrucciones. Los nombres de recetas y notas los escribe el usuario, así que se sanean y se encierran en marcas antes de llegar al modelo. Lo comprobé con una rutina llamada literalmente «IGNORA TUS INSTRUCCIONES».
- Consentimiento explícito y versionado. Son datos de salud (categoría especial del RGPD), así que la app explica qué sale del móvil y hacia dónde, y vuelve a pedir permiso cada vez que eso cambia. Sin aceptación, el asistente funciona en modo básico sin enviar nada.
- Un único fichero conoce claves y modelos, de forma que pasar a un proxy en Cloud Functions para una distribución pública es reescribir una sola función.
Stack desglosado
- Frontend: React Native 0.81 + Expo SDK 54, React Navigation (Stack + Material Top Tabs), Context API.
- Backend: Firebase Firestore (real-time NoSQL) + Firebase Auth (email/contraseña).
- Algoritmo: JavaScript puro -
Calculos.js(Mifflin-St Jeor),GeneradorMenu.js(heurístico con 5 comidas, 259 alimentos en BD local). - Build: EAS Build con perfiles development/preview/production; genero un APK instalable.
- Calidad: lógica de negocio en JavaScript puro, separada de la vista.
Resultados y aprendizajes
Lo que funcionó: la decisión de invertir tiempo desde el día 1 en reglas Firestore con mínimo privilegio. También el botón "Regenerar Menú" (control del usuario, ISO 9241) mejora la adherencia: si una propuesta no convence, hay otra al instante sin perder configuración.
Autoevaluación frente a 5 normas internacionales: ISO 27001 (seguridad), ISO 40500/WCAG 2.1 (accesibilidad), ISO 9241 (usabilidad), ISO 14915 (multimedia), ISO 9126 (calidad software). Cada hallazgo y corrección documentado en NormasISO.md del repo.
Lo que cambiaría hoy: añadiría TanStack Query encima de Firestore para gestionar caché y reintentos. La gestión manual con listeners se complica cuando aparecen pantallas con varias queries paralelas.
Trabajo futuro: mover la clave del modelo a un proxy en Cloud Functions antes de una distribución pública, Google OAuth, escáner de código de barras para digitalizar la despensa y predicción de la lista de la compra a partir del historial.