Ir al contenido
8 de agosto de 2026 · Publicación

Apps nativas sin el dolor nativo

Este artículo describe el producto en el momento de su publicación. Consulta AI Builder y Equipos de Agentes para conocer las funciones actuales.

Apps nativas sin el dolor nativo

Supongamos que llevas un par de semanas creando una app de lista de tareas en el chat. Por ahora es un sitio web —React, una base de datos, nada sofisticado—. Escribes "hazme una versión para Android" y presionas enter. Esto es lo que realmente ocurre entre esa pulsación de tecla y un .aab que aparece en tu carpeta de descargas, porque la mayoría de las plataformas no te muestran esta parte, y esa parte oculta es donde solía vivir todo el dolor.

El .aab de Android

Gradle toma el control primero. Los módulos nativos de tu app —acceso a la cámara, almacenamiento local, cualquier plugin que haya incorporado el build— declaran cada uno contra qué versión de NDK fueron compilados, y esas declaraciones no siempre coinciden. He visto un módulo compilado contra NDK r25 negarse a enlazar con otro que asumía r26, y el error que genera no dice "incompatibilidad de versiones", sino algo sobre un símbolo faltante tres capas dentro de un archivo .so. Las versiones de Kotlin hacen algo más traicionero: una versión fijada dentro de un módulo de Gradle puede opacar silenciosamente la declarada en la parte superior de tu script de build, y el build se completa con éxito; simplemente produce un binario que falla en versiones específicas de Android en producción. Nada de esto es exótico. Es el costo estándar de lanzar apps nativas de Android, y por eso los equipos contratan a alguien cuyo trabajo consiste únicamente en saber qué flag hace desaparecer el error de esta semana.

El build aquí ejecuta el toolchain real y asume todo ese trabajo de resolución:

15–25 minfase de compilación nativa, toolchain real de Gradle
  • Conflictos de dependencias detectados antes de que se conviertan en un fallo en tiempo de ejecución
  • Configuración del toolchain que mejora cada vez que un nuevo fallo le enseña algo

Una versión falsa y más rápida de esto —algo que da forma a un .aab sin ejecutar tareas reales de Gradle— se generaría en menos de un minuto. También fallaría en cuanto tu app necesitara un servicio en segundo plano o una librería de criptografía nativa, y la revisión de Play Store lo detectaría en un día. Preferimos invertir los veinte minutos.

El .dmg de macOS

En este único archivo confluyen dos builds. Las herramientas de línea de comandos de Xcode compilan por separado un binario para Apple Silicon y otro para Intel, y luego lipo los une en un único ejecutable universal.

90%+de las ventas nuevas de Mac son Apple Silicon

Resulta tentador —lanzar un único binario y darlo por terminado— hasta que recuerdas que mucha gente usa el portátil que le da su empleador, no el hardware que elegiría, y que ese portátil puede tener tres años y ser Intel. En lugar de obligar al usuario a averiguar qué chip tiene (la mayoría no sabría decírtelo), lanzamos ambos y dejamos que el sistema operativo elija en silencio. La alternativa, que probamos al principio, es compilar todo de forma cruzada desde una máquina Linux usando toolchains emulados. Es más rápido. También es la forma de acabar con un caso límite de firma de código que solo aparece en hardware real con macOS 12, seis semanas después del lanzamiento, reportado por un usuario confundido que no tiene idea de por qué su app no abre.

El instalador de Windows

Aquí es donde la primera ejecución decide si tu usuario confía en la app. Windows SmartScreen todavía no conoce tu instalador —no ha acumulado reputación en los servidores de Microsoft—, así que muestra una pantalla azul de "Windows protegió tu PC" con un botón que dice "No ejecutar" en negrita y un enlace apenas visible de "Más información" que, al hacer clic, revela "Ejecutar de todos modos". macOS hace su propia versión de este baile: clic derecho, Abrir, confirmar, porque las apps fuera de la App Store tampoco son de confianza por defecto. Al principio enlazamos ambos casos a una página de FAQ genérica. Los tickets de soporte nos dijeron que eso no funciona: alguien que mira una pantalla que dice que su descarga podría ser malware no va a leer documentación, hace una captura y pregunta si lo hackearon. Así que el flujo de instalación detecta el sistema operativo y muestra los tres clics exactos necesarios, sin necesidad de FAQ. Es un detalle pequeño, pero el archivo en sí también importa: la descarga lleva el nombre de tu producto, no el de un artefacto de compilación. Nadie debería tener que explicar por chat de soporte que descargó "app-release-signed-v2-final.exe" y no sabe si es el correcto.

El manifiesto de la extensión de navegador

Este es el caso atípico de todo el pipeline: sin Gradle, sin NDK, sin un paso de compilación en el sentido habitual. Lo que tiene en su lugar es un manifiesto, y el manifiesto es una negociación con un revisor de la Chrome Web Store con quien nunca hablarás directamente.

Permiso solicitadoResultado de la revisión
<all_urls> (más amplio de lo que necesita la funcionalidad)Dos semanas de idas y venidas con alguien que no dirá exactamente qué objetó
activeTab (limitado a la necesidad real)Se aprueba el mismo día

MV3 también complica algo que MV2 hacía fácil: los service workers en segundo plano se descargan a mitad de tarea por diseño, una decisión de política de Google orientada a la duración de la batería, y una función que necesita sobrevivir a eso debe construirse en torno a esa restricción y no en contra de ella. Por defecto, damos a cada extensión el conjunto de permisos más reducido que su funcionalidad real requiere, y lo ampliamos solo cuando una función específica lo exige.

El keystore

Debajo del build de Android hay un artefacto que nunca ves y que no puedes permitirte perder: la clave de firma. Si la pierdes, no solo pierdes la capacidad de actualizar tu app: pierdes la capacidad de actualizarla bajo su identidad existente, de forma permanente, sin ninguna vía de recuperación que Google vaya a concederte jamás. No es una infraestructura vistosa. Es un archivo. Pero marca la diferencia entre lanzar la versión seis dentro de seis meses como una actualización perfecta y lanzarla como un listado completamente nuevo que empieza desde cero instalaciones y cero reseñas. Generamos una por proyecto y la conservamos para que cada build futuro firme con la misma clave que el primer día.

El hilo de chat que hay debajo de todo esto

Nada de lo anterior vive en un "proyecto móvil" aparte. Es la misma conversación que creó la app web. Pides un cambio de interfaz y el build web se actualiza; pides después un paquete de Android, y se compila a partir de ese mismo estado actual, no de una bifurcación que se desincronizó hace tres semanas. La mayoría de los equipos que he visto intentar añadir lo nativo después terminan manteniendo dos bases de código que se distancian entre sí: una app web que se lanza cada día y un envoltorio nativo que alguien tiene que acordarse de poner al día antes de cada versión. Ahí es donde vive el desfase, y es exactamente lo que elimina un único historial de build. Aunque funciona en ambos sentidos: si el chat ha estado descuidado últimamente, el build de Android también hereda eso. No es un pase de pulido aparte, es una compilación directa de lo que realmente hay, lo que en la práctica mantiene la honestidad, porque no existe la opción de "ya lo limpiaremos antes de enviarlo".

Cuando esté listo para una tienda, la ruta de lanzamiento lo entrega a tu propio listado de Play Store y a tu propia cuenta de Apple Developer. No a la nuestra. No quisimos interponernos entre tú y tu propia distribución.

Próximamente: builds para App Store de iOS y macOS. La maquinaria de empaquetado tiene básicamente la misma forma que lo anterior; el pipeline de firma y revisión de Apple es un proyecto en sí mismo, y preferimos lanzarlo funcionando a lanzarlo pronto.
Lanzamiento
CompartirXLinkedInFacebookRedditQuoraWhatsAppTelegramCorreo electrónico
← Todas las publicaciones