Me cansé de apps de workout que parecen formularios de hacienda. Así que construí la mía.
Kheper es un editor de entrenamientos por bloques para iOS. Construyes workouts como construirías una pagina en Notion: arrastras bloques, los anidas, editas todo inline. Texto, ejercicios, supersets, timers, media — cada bloque encaja en su sitio y el resultado es un editor de documentos que resulta que sabe de series y repeticiones.

Por que nativa
Tenia claro una cosa: prefiero que la app haga pocas cosas bien que muchas cosas regular. Cada gesto, cada animacion, cada transicion tenia que sentirse como algo que pertenece a iOS, no como un wrapper web disfrazado.
Por eso SwiftUI y UIKit juntos. SwiftUI para montar interfaces rapido, UIKit para cuando necesito control real. El drag-and-drop, la gestion del teclado, las animaciones de scroll… todo eso requiere acceso directo a las APIs nativas de iOS. Podia haberlo resuelto con un framework cross-platform, pero el resultado no habria sido el mismo y yo lo habria notado cada vez que abriera la app.
La mayoria de apps de workout sienten como si las hubieran construido pensando en “que funcione en todas partes”. Yo queria una que funcionara perfecto en un sitio.
Como funciona
El editor trata todo como un bloque. Un titulo es un bloque. Un ejercicio con series y reps es un bloque. Un superset que agrupa tres ejercicios es un bloque que contiene otros bloques. Tocas el ”+”, eliges tipo, y se coloca donde quieras.
Reordenar es drag and drop. Long press, mover, soltar. La lista se actualiza en vivo, sin dialogos de confirmacion, sin boton de guardar. La idea era que editar se sintiera tan fluido como hacer el entrenamiento. Bueno, quizas mas fluido — yo seguro que descanso mas entre series.

Lo que hay debajo
Fractional indexing para el orden de bloques
Los bloques se ordenan con claves string (“a”, “am”, “b”) en vez de posiciones enteras. Insertar un bloque entre dos genera una nueva clave lexicograficamente entre ellas, sin necesidad de actualizar la posicion de todos los demas. El mismo enfoque que usan Figma y Notion. Inserciones O(1), sin rebalanceo, funciona entre plataformas si la app algun dia va a multi-dispositivo.
Drag and drop custom sobre UICollectionView
El reordenamiento de List de SwiftUI no da la talla para este tipo de editor. Lo descubri por las malas despues de una semana intentando que funcionara. Acabe escribiendo un ReorderableCollectionView custom respaldado por UICollectionViewCompositionalLayout de UIKit. Long-press para coger, reordenamiento en vivo con preview flotante, indicadores de drop entre celdas, drag-to-trash para borrar. Se mantiene a 60fps incluso con bloques anidados complejos.
Auto-scroll consciente del teclado
Esto es de esas cosas que nadie nota a menos que este roto. Cuando tocas un campo de reps cerca del fondo de la pantalla y el teclado sube, el campo tiene que seguir visible. Suena simple. No lo es.
Construi un FocusScrollController que escucha keyboardWillShow/keyboardWillHide, calcula si el campo activo esta a punto de desaparecer detras del teclado, y anima el scroll para mantenerlo a la vista — igualando la curva de animacion del sistema para que no se vea raro. Si ya estas escribiendo y cambias de campo, hace scroll inmediato. Si el teclado todavia esta apareciendo, encola el scroll y lo dispara en el momento justo.
Todos los campos editables de la app se enganchan a esto con una sola llamada scrollToField(). Me llevo mas tiempo del que me gustaria admitir, pero ahora simplemente funciona y no pienso en ello. Que es exactamente el objetivo.

Capa de dominio sin dependencias de framework
La capa de dominio es Swift puro. Sin imports de SwiftUI, SwiftData ni UIKit. Block protocol, entidades, value objects, use cases — todo portable. SwiftData entra en la capa de infraestructura, y hay una implementacion in-memory para tests. Si algun dia necesito cambiar la persistencia, es un fichero en el dependency container.
Tipos de bloques
Los bloques cubren todo lo que necesitas en un workout: texto para titulos y notas, ejercicios individuales con series/reps/peso configurables, sets para agrupar ejercicios en supersets o circuitos, timers de cuenta atras, charts de progreso, media para adjuntar imagenes o videos de referencia, y divisores para separar secciones visualmente.

Que viene despues
Si la app sigue atrayendo usuarios, Android esta sobre la mesa. La capa de dominio es Swift puro sin imports de framework, asi que la logica de negocio, los use cases y el algoritmo de fractional indexing se portan a Kotlin casi linea por linea. El trabajo real es reconstruir las capas de UI y persistencia — que es exactamente el tipo de problema que Clean Architecture esta pensada para resolver. Esta bien cuando la inversion inicial realmente te ahorra tiempo en vez de solo quedar bien en una pizarra.