Codekanox

Homeschooling y supervisión familiar en una sola app: los padres acompañan tareas, progreso de estudio y ubicación de sus hijos, con privacidad y batería bajo control.

Rol: Lead móvil2024–2025Equipo de 3
React NativeExpoTypeScriptFirebaseexpo-locationGeofencingCloud Functions
99.5%
crash-free
−45%
batería en segundo plano
+38%
tareas completadas

01 · Contexto y problema

Codekanox es un homeschooling con supervisión familiar en versión móvil: los padres arman el plan de estudio de sus hijos, siguen el progreso y el tiempo de estudio, y ven su ubicación (zonas seguras como casa o el centro de estudio) con alertas — todo en una app con dos roles.

Recibí el proyecto con tres problemas que chocaban: la ubicación en segundo plano usaba GPS continuo y drenaba la batería; el modelo de tareas mezclaba el plan con el progreso (sin histórico fiable); y no había separación real de roles padre/hijo, con riesgo de cruzar datos entre familias.

El objetivo: ubicación en segundo plano fiable sin matar la batería, roles y aislamiento por familia server-side, y registro de estudio que no se pierda sin señal — con la privacidad de menores como base, no como anexo.

Restricciones

Monitoreo de menores → LGPD + consentimiento parentalUbicación en 2.º plano (Doze + battery managers)Permiso background (revisión Google Play / Apple)Firebase como único backendConectividad intermitenteAndroid gama media-bajaDos roles (padre / hijo) en una app

02 · Decisiones técnicas

1

Roles + aislamiento por familia

Problema

El estado vivía disperso y sin separación de roles; las reglas no aislaban por familia, así que un dispositivo podía alcanzar datos de otra.

Opciones

Single role con flags · dos apps separadas · custom claims por rol + familyId

Decisión ✓

Roles con custom claims (padre/hijo) + documento de familia y vínculo por código de invitación; reglas Firestore que exigen el mismo familyId. React Native + Expo (dev client) con Feature-Sliced + DDD pragmático.

Trade-off

Onboarding más complejo (invitación, verificación del vínculo), a cambio de aislamiento por familia garantizado server-side, no por convención.

2

Ubicación en segundo plano + batería

Problema

El GPS continuo drenaba la batería en gama baja, las actualizaciones se perdían sin red y los geofences eran poco fiables.

Opciones

GPS continuo · geofencing nativo · significant-location-change · híbrido adaptativo

Decisión ✓

Geofencing nativo (entrar/salir de zonas seguras) + muestreo adaptativo (alta frecuencia solo en movimiento o dentro de zonas críticas) + cola offline que sincroniza al reconectar. expo-location + expo-task-manager para la tarea en segundo plano.

Trade-off

Menos granularidad cuando el niño está quieto, a cambio de −45 % de batería y cero pérdida de datos cuando la red cae.

3

Estudio/tareas (homeschooling) + offline

Problema

El modelo mezclaba el plan de estudio con el progreso; el avance se perdía sin red y las listas largas tiraban los FPS.

Opciones

Un solo modelo con flags · plan y progreso separados · cola idempotente

Decisión ✓

Separar el plan (currículo) del progreso (registro por día, inmutable), cola idempotente para el avance, cache local (MMKV) y notificaciones de tarea/zona.

Trade-off

Más modelos y migración del schema legacy, a cambio de histórico fiable y avance que no se pierde offline.

Arquitectura

UI · Features (RN + Expo)
App · Hooks + Use Cases
Domain · TS puro (VOs)
Infra · Firestore + Geofencing + CF

03 · Ubicación en segundo plano fiable sin drenar la batería (y sin perder datos offline)

El reto más difícil fue tener ubicación confiable en segundo plano en Android gama baja, donde Doze y los battery managers agresivos matan procesos y recortan el GPS — sin convertir la app en un drenador de batería ni perder posiciones cuando la red cae.

El GPS continuo era inviable: descargaba la batería y generaba escrituras inútiles con el niño quieto. Pero la precisión tampoco podía bajar tanto como para fallar una alerta de «salió de la zona segura».

Solución: geofencing nativo para los eventos que importan (entrar/salir de casa o del centro de estudio) + muestreo adaptativo (subir la frecuencia solo en movimiento o dentro de zonas críticas) + una cola offline con clave idempotente por marca de tiempo que sincroniza contra Firestore al reconectar. La tarea corre con expo-task-manager para sobrevivir en segundo plano.

Resultado: −45 % de consumo en segundo plano, geofences fiables para las alertas y cero posiciones perdidas en cortes de red — sin sacrificar la alerta que de verdad importa.

04 · Privacidad y protección de menores

Monitorear a un menor es delicado: el diseño parte de la minimización de datos y el consentimiento. Solo se recoge lo necesario (ubicación y progreso), atado a la familia y nunca expuesto fuera de ella.

Transparencia: el hijo sabe que está siendo acompañado (no es espionaje encubierto); el rol y los permisos son explícitos en el onboarding.

Aislamiento server-side: las reglas Firestore exigen el mismo familyId para leer ubicación o progreso — ningún dispositivo alcanza datos de otra familia, por construcción.

Retención y borrado: purga real de datos al desvincular o eliminar la cuenta (con dry-run previo), cumpliendo LGPD; los datos sensibles no se venden ni se reutilizan para otra cosa.

Demo interactiva

Próximamente: ejecuta el código y míralo correr en vivo, sin instalar nada.

main.dart
Run

Editor en vivo (DartPad) — próximamente

Próximamente

04 · Resultados · antes / después

crash-free
97.0%
99.5%
autonomía con tracking activo
~6 h
~11 h
latencia de alerta de zona
~90s
<20s
posiciones perdidas sin red
frecuentes
0
tareas completadas (semana)
+38%

05 · Retrospectiva

Definiría la estrategia de batería/geofencing desde el día 1: el muestreo adaptativo llegó tarde, tras quejas reales de batería; tenerlo antes habría evitado retrabajo y malas reseñas.

Cerraría antes la revisión de permisos de ubicación en background: las políticas de Google Play y Apple para apps que rastrean menores son estrictas; conviene diseñarlas desde el inicio.

Conservaría el aislamiento por familia en reglas (no en cliente), la separación plan/progreso y la transparencia con el menor como regla de producto.

Código destacado · Ubicación en segundo plano (muestreo adaptativo + cola offline)

La tarea nativa (expo-task-manager) que corre en segundo plano: descarta posiciones irrelevantes para ahorrar batería, encola localmente con clave idempotente y sincroniza al reconectar — sin perder la alerta que importa.

src/tasks/background-location.ts
import * as TaskManager from "expo-task-manager";
import type { LocationObject } from "expo-location";

import { isInsideCriticalZone } from "@/lib/geofences";
import { enqueue, flush } from "@/lib/location-queue";

export const BG_LOCATION = "codekanox.bg-location";

// Tarea nativa (corre aunque la app esté en segundo plano). Muestreo adaptativo
// + cola offline: no drena batería ni pierde datos cuando la red cae.
TaskManager.defineTask(BG_LOCATION, async ({ data, error }) => {
  if (error || !data) return;
  const { locations } = data as { locations: LocationObject[] };
  const last = locations.at(-1);
  if (!last) return;

  const { latitude, longitude, accuracy, speed } = last.coords;

  // Casi quieto y fuera de una zona crítica → no registramos: ahorra batería
  // y escrituras (la posición no cambió de forma relevante).
  const quieto = (speed ?? 0) < 0.6;
  if (quieto && !isInsideCriticalZone(latitude, longitude)) return;

  // Encolamos local (sobrevive sin red) con clave idempotente por timestamp,
  // y vaciamos la cola contra Firestore si hay conexión.
  await enqueue({
    id: String(last.timestamp),
    lat: latitude,
    lng: longitude,
    accuracy: accuracy ?? null,
    at: last.timestamp,
  });
  await flush(); // si falla por red, queda en cola para el próximo ciclo
});

En entrevista se nota: trabajo en segundo plano real, muestreo adaptativo por batería, cola offline con idempotencia (sin duplicados ni pérdidas) y la decisión de qué NO registrar — criterio, no solo código.

Caso anterior
Siguiente caso