Programación de aplicaciones en entornos móviles: Android, iOS y tecnologías multiplataforma
Este tema trata la programación de aplicaciones para dispositivos móviles: los lenguajes, entornos de desarrollo (IDE), SDK y herramientas de compilación y distribución de Android e iOS, y las alternativas que permiten programar una sola vez para varias plataformas. El sistema operativo móvil en sí mismo (Android/iOS como plataforma de usuario) se estudia en otro tema; aquí se trata como el destino sobre el que se programa.
1. Tipos de aplicaciones móviles
Según la tecnología y el alcance del código compartido, una aplicación móvil puede ser:
-
Nativa. Se construye con el lenguaje y el SDK propios de una única plataforma: en Android, con Kotlin, el lenguaje de la Java programming language o C++ compilados por las herramientas del SDK de Android en un APK o un Android App Bundle; en iOS, con Swift u Objective-C sobre los frameworks de interfaz UIKit o SwiftUI, dentro de Xcode. El sistema la trata igual que a cualquier otra aplicación nativa de la plataforma: en Android, el propio sistema operativo la empaqueta y ejecuta en su propia caja de seguridad (sandbox) con un identificador de usuario de Linux exclusivo.
-
Web / Progressive Web App (PWA). Es una aplicación construida con tecnologías web estándar (HTML, CSS, JavaScript) que se comporta como una app específica de plataforma: como un sitio web, funciona en varios dispositivos desde un único código fuente, y como una app nativa, se puede instalar en el dispositivo, operar sin conexión y en segundo plano, e integrarse con el sistema. Lo que la hace instalable y la distingue de un sitio web corriente son dos piezas: el manifiesto de la aplicación web y, habitualmente, un service worker.
-
Híbrida. El código de la aplicación es una app web (HTML, CSS y JavaScript) que se ejecuta dentro de un WebView —una ventana de navegador integrada— envuelta en un contenedor nativo específico de cada plataforma; ese contenedor es lo que se distribuye en las tiendas de aplicaciones. Un plugin hace de puente entre el código web y las API nativas del dispositivo (sensores, estado de red, etc.). Apache Cordova es el ejemplo de referencia de este modelo: el WebView puede constituir toda la interfaz de la app o, en una app híbrida en sentido estricto, convivir con componentes nativos propios.
-
Multiplataforma (cross-platform). El código de la lógica de negocio —y a menudo también de la interfaz— se escribe una sola vez en un framework común (Flutter, React Native, .NET MAUI, Kotlin Multiplatform…) y se compila o traduce a artefactos nativos para cada plataforma de destino, reduciendo la duplicación de código y de equipos frente a mantener dos bases de código nativas independientes.
2. Android: lenguajes, herramientas y estructura de un proyecto
2.1. Kotlin y Java
Las apps de Android se escriben en Kotlin, en el lenguaje de programación Java, o en C++. Desde la Google I/O de 2019, el desarrollo de Android es Kotlin-first: más de la mitad de los desarrolladores profesionales de Android usan Kotlin como lenguaje principal (frente al 30 % que usa Java como principal), el 70 % de quienes tienen Kotlin como lenguaje principal dice que les hace más productivos, y las apps hechas con Kotlin tienen, según datos internos de Google, un 20 % menos de probabilidad de fallar (crash) que las que no lo usan; más del 95 % de las mil apps de Android más usadas emplean ya Kotlin. Kotlin es totalmente interoperable con Java, de modo que ambos lenguajes pueden convivir en el mismo proyecto sin tener que migrar todo el código existente.
2.2. Android Studio, el SDK y Gradle
Android Studio es el entorno de desarrollo integrado (IDE) oficial para Android, basado en el editor de código y las herramientas de IntelliJ IDEA, al que añade un sistema de compilación flexible basado en Gradle, un emulador rápido, plantillas de código, herramientas de prueba, el análisis estático lint y soporte para C++ y el NDK.
Cada proyecto de Android Studio se organiza en módulos, visibles por defecto en la vista «Android»; un módulo de app contiene, entre otras, las carpetas manifests (con el fichero AndroidManifest.xml), kotlin+java (el código fuente, incluidos los tests) y res (recursos sin código, como cadenas de texto e imágenes).
El sistema de compilación de Android es Gradle, con capacidades específicas de Android añadidas por el plugin de Gradle para Android; se ejecuta integrado en Android Studio o de forma independiente desde la línea de comandos. Los ficheros de configuración de la compilación se llaman build.gradle.kts (sintaxis Kotlin, la recomendada) o build.gradle (sintaxis Groovy); hay uno de nivel de proyecto (que declara, por ejemplo, las versiones de los plugins com.android.application, com.android.library u org.jetbrains.kotlin.android) y uno por cada módulo. El wrapper de Gradle (gradlew) es una pequeña aplicación incluida en el código fuente que descarga y ejecuta la versión de Gradle indicada en gradle/wrapper/gradle-wrapper.properties, para que la compilación sea reproducible entre máquinas. El fichero settings.gradle.kts (o settings.gradle), en la raíz del proyecto, declara los repositorios y qué módulos entran en la compilación final.
Dentro de la configuración de Gradle se definen, además:
-
Tipos de compilación (build types, como
debugyrelease, creados por defecto): por ejemplo, la compilacióndebugfirma la app con una clave de depuración automática; lareleaseno se firma salvo que se configure explícitamente una clave de firma propia. -
Variantes de producto (product flavors), para publicar versiones distintas de la misma app (por ejemplo, gratuita y de pago) reutilizando el código y los recursos comunes.
-
Variante de compilación (build variant), el cruce de un tipo de compilación con una variante de producto.
-
Reducción de código y recursos, con herramientas integradas como R8, que reducen el tamaño del APK o del AAB eliminando código y recursos no usados.
2.3. Estructura de un proyecto y AndroidManifest.xml
Todo proyecto de Android debe tener, con ese nombre exacto, un fichero AndroidManifest.xml en la raíz del conjunto de fuentes de la app. El manifiesto describe al sistema Android, a las herramientas de compilación y a Google Play información esencial de la app, y entre otras cosas debe declarar:
-
Los componentes de la app (actividades, servicios, receptores de difusión y proveedores de contenido), cada uno con sus propiedades básicas (como el nombre de su clase Kotlin o Java) y, opcionalmente, los filtros de intención que describen cómo puede iniciarse.
-
Los permisos que la app necesita para acceder a partes protegidas del sistema o de otras apps, y los permisos que exige a otras apps para acceder a su propio contenido.
-
Las características de hardware y software que requiere la app, lo que condiciona en qué dispositivos puede instalarse desde Google Play.
Cada componente se declara con un elemento XML correspondiente: <activity> para cada subclase de Activity, <service> para cada subclase de Service, <receiver> para cada subclase de BroadcastReceiver y <provider> para cada subclase de ContentProvider. Las actividades, servicios y proveedores de contenido que no se declaren en el manifiesto son invisibles para el sistema y nunca pueden ejecutarse; los receptores de difusión, en cambio, también pueden crearse dinámicamente en el código y registrarse en tiempo de ejecución.
2.4. Componentes de la aplicación
Android define cuatro tipos de componentes de app, cada uno con un propósito y un ciclo de vida distintos:
-
Activity (actividad). Es el punto de entrada para interactuar con el usuario: representa una sola pantalla con interfaz de usuario. Se implementa como subclase de la clase
Activity. -
Service (servicio). Es un punto de entrada de propósito general para mantener una app en ejecución en segundo plano, sin interfaz de usuario propia; por ejemplo, reproducir música mientras el usuario está en otra app, o descargar datos sin bloquear la interacción. Hay dos tipos: los servicios iniciados (started), que el sistema mantiene en ejecución hasta que su trabajo termina, y los servicios enlazados (bound), a los que otra app o el propio sistema se enlazan para usar la API que ofrecen. Se implementa como subclase de
Service. -
BroadcastReceiver (receptor de difusión). Permite que el sistema entregue eventos a la app fuera del flujo normal de uso, para que la app reaccione a anuncios de difusión de todo el sistema (por ejemplo, que la pantalla se ha apagado o que la batería está baja); el sistema puede entregar estas difusiones incluso a apps que no están en ejecución. No muestra interfaz de usuario, aunque puede crear una notificación en la barra de estado. Cada difusión se entrega como un objeto
Intent; se implementa como subclase deBroadcastReceiver. -
ContentProvider (proveedor de contenido). Gestiona un conjunto de datos de la app que se puede almacenar en el sistema de ficheros, en una base de datos SQLite, en la web o en cualquier otro almacenamiento persistente al que la app tenga acceso, y a través del cual otras apps pueden consultar o modificar esos datos si el proveedor lo permite. Es, desde el punto de vista del sistema, un punto de entrada a una app para publicar datos identificados mediante un esquema de URI. Se implementa como subclase de
ContentProvider.
Una peculiaridad del diseño de Android es que cualquier app puede iniciar un componente de otra app; por eso las apps de Android no tienen un único punto de entrada, no existe una función main(). Ese arranque de componentes se hace mediante un mensaje asíncrono llamado Intent: un objeto Intent activa actividades, servicios y receptores de difusión, y puede ser explícito (nombra directamente la clase del componente destino) o implícito (describe el tipo de acción a realizar —por ejemplo, ver o enviar algo— y deja que el sistema encuentre un componente capaz de realizarla). Los proveedores de contenido, en cambio, se activan mediante peticiones de un ContentResolver, no mediante intents.
2.5. Ciclo de vida de la actividad
La clase Activity ofrece seis callbacks de ciclo de vida: onCreate(), onStart(), onResume(), onPause(), onStop() y onDestroy(). En onCreate() se realiza la lógica básica de arranque de la app (por ejemplo, enlazar datos a listas); tras onCreate(), el sistema invoca onStart() y onResume() en rápida sucesión, y la actividad entra en el estado Resumed (en primer plano, con el foco de interacción del usuario). Cuando otra actividad pasa a primer plano, el sistema invoca onPause() —la ejecución de onPause() es muy breve y no ofrece necesariamente tiempo suficiente para operaciones de guardado— y, si la actividad deja de ser visible por completo, invoca onStop(), el lugar adecuado para liberar o ajustar recursos no necesarios mientras la actividad no es visible. Si el usuario vuelve a la actividad, el sistema vuelve a llamar a onResume() (si solo había pasado por onPause()) sin pasar por onCreate().
2.6. Interfaz de usuario: vistas XML y Jetpack Compose
Android ofrece dos formas de construir la interfaz de usuario. El modelo clásico, basado en vistas, define la jerarquía de widgets normalmente a partir de un fichero de diseño XML inflado en tiempo de ejecución; cada widget mantiene su propio estado interno y expone métodos getter y setter (por ejemplo, button.setText(String) o container.addChild(View)) con los que el código de la app lo actualiza manualmente recorriendo el árbol con findViewById().
Jetpack Compose es el conjunto de herramientas moderno y recomendado por Android para construir UI nativa, con un modelo declarativo: en lugar de mutar vistas con estado, se describen funciones composable (anotadas con @Composable) que reciben datos y emiten elementos de interfaz, sin devolver nada y sin exponer getters ni setters. Cuando el estado de la app cambia, la app vuelve a llamar a las mismas funciones composable con los nuevos datos, y Compose decide de forma inteligente qué partes de la pantalla necesita redibujar; este proceso se llama recomposición. Como las funciones composable están escritas en Kotlin y no en XML, pueden usar if, bucles y funciones auxiliares con la misma flexibilidad que cualquier otro código Kotlin. Compose es compatible con Material Design 3 y con el código basado en vistas existente, de modo que ambos modelos se pueden combinar en la misma app.
2.7. Permisos
Android clasifica los permisos según el riesgo de la acción o el dato al que dan acceso, y cada permiso tiene asignado un nivel de protección:
-
Permisos de instalación (install-time): se conceden automáticamente al instalar la app. Incluyen los permisos normales (nivel de protección
normal, para datos y acciones que apenas afectan a la privacidad del usuario o a otras apps) y los permisos de firma (signature), que el sistema solo concede si la app está firmada con el mismo certificado que la app o el sistema operativo que define el permiso. -
Permisos en tiempo de ejecución (runtime), también llamados permisos peligrosos (nivel
dangerous): dan acceso a datos o acciones restringidas con un efecto más sustancial sobre el sistema, como la ubicación, los contactos, la cámara o el micrófono; la app debe solicitarlos en tiempo de ejecución y el sistema muestra al usuario un cuadro de diálogo de permiso para que los conceda. -
Permisos especiales (nivel
appop): solo la plataforma y los fabricantes (OEM) pueden definirlos, para proteger acciones especialmente potentes, como dibujar sobre otras apps.
2.8. Empaquetado (APK/AAB) y distribución en Google Play
Las herramientas del SDK de Android compilan el código junto con los datos y los recursos en un APK (Android package, un fichero .apk con todo lo necesario para instalar y ejecutar la app en un dispositivo) o en un Android App Bundle (.aab), un formato de publicación que incluye todo el código y los recursos compilados de la app pero que no se puede instalar directamente: difiere la generación y la firma del APK hasta que Google Play lo recibe. A partir del AAB, Google Play genera y sirve APK optimizados para cada configuración de dispositivo, de modo que cada usuario solo descarga el código y los recursos que su dispositivo necesita. Desde agosto de 2021, todas las apps nuevas deben publicarse con Android App Bundle, y desde junio de 2023 esa obligación se extendió también a las apps nuevas y existentes de TV. El tamaño total comprimido de las descargas necesarias para instalar la app (el APK base más los APK de configuración) no puede superar los 4 GB.
Para publicar una app en Google Play Console, el desarrollador crea una versión (release) en una de cuatro pistas: pruebas abiertas (disponibles a cualquier usuario que se une desde la ficha de la app en Play), pruebas cerradas (un número limitado de probadores elegidos), pruebas internas (hasta 100 probadores elegidos) o producción (disponible para todos los usuarios de Google Play en los países elegidos).
3. iOS: lenguajes, herramientas y ciclo de vida de la aplicación
3.1. Swift y Objective-C
Swift es el lenguaje de programación de propósito general con el que se construyen las apps de Apple hoy: rápido, seguro por diseño y expresivo, con tipado por inferencia, optionals para representar la ausencia de un valor sin recurrir a punteros nulos sin control, cierres (closures), genéricos y manejo de errores incorporado al lenguaje. Swift se publicó como proyecto de código abierto el 3 de diciembre de 2015, bajo licencia Apache 2.0 con una excepción para la biblioteca en tiempo de ejecución, y hoy se puede usar no solo en las plataformas de Apple (iOS, macOS, watchOS y tvOS) sino también en Linux y en Windows.
Objective-C es el lenguaje con el que tradicionalmente se escribía el software para iOS y macOS: un superconjunto del lenguaje C que añade capacidades orientadas a objetos y un runtime dinámico, con sintaxis propia para definir clases y métodos y con gestión automática de memoria por conteo de referencias (Automatic Reference Counting, ARC). Swift y Objective-C pueden convivir en el mismo proyecto de Xcode, y en las plataformas de Apple el propio runtime de Objective-C sigue siendo necesario para acceder a frameworks como UIKit o AppKit.
3.2. Xcode y el SDK de iOS
Xcode es el conjunto de herramientas con el que se construyen las apps para las plataformas de Apple: gestiona todo el flujo de trabajo, desde crear la app hasta probarla, optimizarla y enviarla al App Store. Incluye un editor de código con autocompletado, control de versiones y un depurador, además de herramientas para perfilar el rendimiento (Instruments), inspeccionar la accesibilidad (Accessibility Inspector) y ejecutar la app en dispositivos simulados o físicos.
Cada versión de Xcode incluye los SDK de las plataformas de Apple (iOS, iPadOS, macOS, tvOS, watchOS, visionOS) para esa versión del sistema. Apple exige, además, que las apps que se suban a App Store Connect se compilen con una versión mínima de Xcode y del SDK: desde el 28 de abril de 2026, las apps deben compilarse con Xcode 26 o posterior usando un SDK para iOS 26, iPadOS 26, tvOS 26, visionOS 26 o watchOS 26 (o versiones posteriores).
3.3. Interfaz de usuario: UIKit y SwiftUI
UIKit es el framework que, desde los inicios de iOS, proporciona la arquitectura de ventanas y vistas para construir la interfaz, la infraestructura de gestión de eventos táctiles y de otro tipo de entrada, y el bucle de ejecución principal de la app. Al igual que el modelo de vistas de Android, es imperativo: el código actúa sobre objetos de vista que mantienen su propio estado.
SwiftUI es el framework declarativo con el que se declara la interfaz de usuario y el comportamiento de la app en cualquier plataforma de Apple a partir del protocolo App y de vistas propias que conforman el protocolo View, componibles con pilas, listas y modificadores. Las vistas de SwiftUI se pueden integrar con objetos de UIKit (y de AppKit en macOS), de modo que ambos frameworks se pueden combinar en la misma app, igual que ocurre en Android entre las vistas XML y Jetpack Compose.
3.4. Ciclo de vida de la aplicación e Info.plist
El estado actual de una app de iOS determina qué puede y qué no puede hacer en cada momento: una app en primer plano tiene prioridad sobre los recursos del sistema, mientras que una app en segundo plano debe hacer el mínimo trabajo posible. UIKit notifica los cambios de estado llamando a métodos del objeto delegado adecuado: desde iOS 13, mediante objetos UISceneDelegate (cada escena, scene, representa una instancia de la interfaz de la app en ejecución, y puede estar en un estado distinto —en primer plano, en segundo plano o suspendida— que las demás escenas de la misma app); en iOS 12 y anteriores, todos los eventos de ciclo de vida se entregan al objeto UIApplicationDelegate, que gestiona todas las ventanas de la app. Además de los eventos de ciclo de vida, la app debe responder a otros eventos del sistema, como los avisos de memoria baja, los cambios de disponibilidad de los datos protegidos cuando el usuario bloquea o desbloquea el dispositivo, o la apertura de URL.
El archivo Info.plist (la información property list, con ese nombre exacto, sensible a mayúsculas) es el conjunto de pares clave-valor que especifica cómo interpreta el sistema el paquete (bundle) de la app: algunas claves caracterizan al propio paquete, y otras configuran la app que representa. En un paquete de app de iOS se ubica en el directorio raíz del paquete (en macOS, dentro del directorio Contents). Normalmente es Xcode quien crea y mantiene este fichero; para acceder a sus valores desde el código se usa la clase Bundle —por ejemplo, su propiedad bundleIdentifier contiene el valor asociado a la clave CFBundleIdentifier—.
3.5. Permisos
Antes de que una app pueda acceder a un recurso protegido —la interfaz Bluetooth, la ubicación, la cámara, el micrófono o las fotos del usuario, entre otros—, el sistema pide permiso al usuario en nombre de la app, y el cuadro de diálogo que muestra incluye una cadena de descripción de uso (usage description string o purpose string) que la propia app proporciona explicando por qué necesita ese acceso. Esa cadena se introduce como valor de la clave correspondiente del Info.plist —por ejemplo, NSCameraUsageDescription para la cámara, NSMicrophoneUsageDescription para el micrófono o NSBluetoothAlwaysUsageDescription para Bluetooth— y debe solicitarse justo en el momento en que la app necesita el dato, nunca por adelantado ni «porque podría ser útil más adelante».
3.6. Firma y aprovisionamiento
Salvo en macOS, las plataformas de Apple no ejecutan código de terceros arbitrario: toda ejecución debe estar autorizada por Apple mediante un perfil de aprovisionamiento (provisioning profile), que ata entre sí cinco criterios: quién puede firmar código, qué apps puede firmar, dónde pueden ejecutarse, cuándo pueden ejecutarse y qué permisos especiales (entitlements) pueden tener. Los perfiles se crean desde el sitio web de Apple Developer, de forma directa o a través de Xcode o de la App Store Connect API, y llevan una firma criptográfica que el dispositivo comprueba antes de ejecutar la app. Cuando una app se envía al App Store, la propia tienda vuelve a firmar la app como parte de la distribución, tras comprobar que está firmada y aprovisionada correctamente; por eso la app final, ya distribuida desde el App Store, no necesita llevar su propio perfil de aprovisionamiento.
3.7. TestFlight y distribución en el App Store
Para distribuir una app, primero se crea un archivo (archive) —una compilación de la app, con información de depuración, que Xcode guarda en un paquete— desde el menú Producto > Archivar. Desde el organizador de archivos de Xcode se elige un método de distribución: entre las opciones predefinidas están TestFlight & App Store (firma de código automática y subida con símbolos, para distribuir por TestFlight y enviar al App Store), TestFlight Internal Only (restringe el acceso al propio equipo, evitando que una compilación de desarrollo llegue al App Store) o Release Testing; también existen métodos de distribución personalizados, como Ad Hoc (a un número limitado de dispositivos registrados) o Enterprise (a los miembros de la propia organización, dentro del programa Apple Developer Enterprise). Antes de enviar una app por primera vez, hay que crear su ficha en App Store Connect. TestFlight es, así, la vía con la que un desarrollador distribuye compilaciones beta a probadores y recoge su opinión antes de publicar la versión definitiva en el App Store.
4. Entornos y tecnologías multiplataforma
4.1. Flutter y Dart
Flutter es un conjunto de herramientas de interfaz de usuario multiplataforma (cross-platform UI toolkit) diseñado para reutilizar código entre sistemas operativos —iOS, Android, web y escritorio— permitiendo además que la app interactúe directamente con los servicios de cada plataforma. Su arquitectura es una serie de capas independientes: de abajo arriba, un embebedor (embedder) específico de cada plataforma, escrito en el lenguaje propio de cada una (Java y C++ en Android; Swift y Objective-C/Objective-C++ en iOS y macOS; C++ en Windows y Linux), que coordina con el sistema operativo el acceso a servicios como las superficies de renderizado, la accesibilidad y la entrada; el motor de Flutter (Flutter engine), escrito sobre todo en C++, que implementa las primitivas de bajo nivel (renderizado de gráficos y texto, E/S de ficheros y de red, el runtime de Dart); y el framework de Flutter, escrito en Dart, organizado en capas de fundamentos, renderizado, widgets —en Flutter, todo elemento de interfaz es un widget— y los paquetes Material y Cupertino, que implementan los lenguajes de diseño de Android y de iOS respectivamente sobre esa misma capa de widgets. Durante el desarrollo, la app se ejecuta en una máquina virtual que permite el hot reload con estado de los cambios sin una recompilación completa; para el lanzamiento (release), el código Dart se compila directamente a código máquina (Intel x64 o instrucciones ARM) o, si el destino es la web, a JavaScript.
Dart es el lenguaje con el que se escribe Flutter: un lenguaje optimizado para cliente, con comprobación de tipos estática (type safe) y seguridad nula sólida (sound null safety, por la que un valor no puede ser null salvo que se declare explícitamente que puede serlo, comprobado en tiempo de compilación), con tipos inferidos y un conjunto de bibliotecas básicas que incluye, entre otras, dart:core, dart:collection, dart:convert, dart:math, dart:async (para programación asíncrona con Future y Stream), dart:io y dart:isolate (para programación concurrente mediante isolates, trabajadores independientes que no comparten memoria y se comunican por mensajes).
4.2. React Native
Con React Native, la interfaz se construye en JavaScript usando componentes de React que, en tiempo de ejecución, React Native convierte en las vistas nativas correspondientes de Android o de iOS; por eso las apps de React Native tienen el aspecto, el comportamiento y el rendimiento de cualquier otra app, al estar respaldadas por las mismas vistas nativas. Estos componentes respaldados por vistas nativas se llaman Native Components, y React Native incluye un conjunto esencial y listo para usar llamado Core Components, entre ellos <View> (equivalente a ViewGroup en Android y a UIView en iOS, un contenedor sin desplazamiento), <Text> (TextView / UITextView), <Image> (ImageView / UIImageView), <ScrollView> (ScrollView / UIScrollView) y <TextInput> (EditText / UITextField).
4.3. .NET MAUI
.NET Multi-platform App UI (.NET MAUI) es un framework multiplataforma para crear apps móviles y de escritorio nativas con C# y XAML desde una sola base de código compartida, capaz de dirigirse a Android, iOS, macOS y Windows; es la evolución de Xamarin.Forms, con sus controles de interfaz reconstruidos desde cero. .NET MAUI unifica las API de Android, iOS, macOS y Windows en una sola API, apoyada en la Base Class Library (BCL) de .NET, que a su vez se ejecuta sobre Mono (la implementación del runtime de .NET para Android, iOS y macOS) o sobre el Common Language Runtime (CLR) de .NET en Windows. En la compilación final: las apps Android compiladas con .NET MAUI pasan de C# a lenguaje intermedio (IL), que se compila just-in-time (JIT) a código nativo cuando la app se lanza; las apps iOS se compilan de forma íntegramente ahead-of-time (AOT), de C# a código nativo ARM; las apps macOS usan Mac Catalyst (la solución de Apple que lleva una app de iOS hecha con UIKit al escritorio); y las apps Windows usan la biblioteca Windows UI 3 (WinUI 3). .NET MAUI organiza todo esto en un proyecto único mediante multi-targeting, e incluye hot reload tanto de código C# (.NET hot reload) como de XAML, de modo que los cambios se reflejan en la app en ejecución sin recompilarla y sin perder el estado de navegación.
4.4. Kotlin Multiplatform
Kotlin Multiplatform (KMP) es una tecnología de código abierto de JetBrains que permite compartir código entre Android, iOS, escritorio, web y servidor conservando las ventajas del desarrollo nativo; con Compose Multiplatform, además, se puede compartir también el código de la interfaz de usuario entre esas plataformas. Para producir el código nativo que se ejecuta en iOS, KMP se apoya en Kotlin/Native, que compila a binarios nativos y accede directamente a las API de la plataforma; según la encuesta KMP del segundo trimestre de 2024, el 55 % de los usuarios declaró una colaboración mejor tras adoptar KMP y el 65 % de los equipos, una mejora de rendimiento y calidad. Empresas como Google, Duolingo, Forbes, Philips, McDonald's, Bolt, H&M, Baidu, Kuaishou y Bilibili han adoptado KMP en producción.
4.5. Progressive Web Apps (PWA)
Una Progressive Web App (PWA) es, como se ha visto en el apartado 1, una app construida con tecnología web que se puede instalar y comportarse como una app específica de plataforma. Dos piezas la hacen posible:
-
El manifiesto de la aplicación web (web app manifest): un fichero JSON que indica al navegador cómo debe aparecer y comportarse la PWA en el dispositivo, enlazado desde el HTML con
<link rel="manifest" href="manifest.json">. Para que los navegadores basados en Chromium (Chrome, Samsung Internet, Edge) promuevan su instalación, el manifiesto debe incluir, como mínimo,nameoshort_name;icons, con al menos un icono de 192 px y otro de 512 px;start_url;displayy/odisplay_override; yprefer_related_applicationsenfalseo ausente. Además, la PWA debe servirse porhttps, o porlocalhost/127.0.0.1en desarrollo local. Una PWA que cumple estos requisitos se puede distribuir también en tiendas de apps —Google Play Store, Microsoft Store o Meta Quest Store, entre otras—, con herramientas como PWABuilder que facilitan ese empaquetado. -
El service worker: un fichero JavaScript, registrado contra el origen y una ruta mediante
ServiceWorkerContainer.register(), que actúa como un servidor proxy entre la aplicación web, el navegador y la red; intercepta las peticiones de red y las puede responder con un recurso cacheado, lo que permite construir experiencias sin conexión. Su ciclo de vida tiene tres fases —descarga, instalación y activación— y se ejecuta en un contexto de trabajador (worker), sin acceso al DOM y en un hilo distinto al de la app, de forma asíncrona y no bloqueante; solo funciona en contextos seguros (HTTPS, olocalhostpara desarrollo).
5. Emuladores y simuladores, pruebas y publicación
Para probar una app sin depender de tener a mano cada modelo de dispositivo físico, Android ofrece el Android Emulator, que simula dispositivos Android en el ordenador de desarrollo mediante dispositivos virtuales de Android (AVD); viene incluido con Android Studio y ofrece flexibilidad (configuraciones predefinidas de teléfono, tableta, Wear OS, Android Automotive y Android TV, además de distintos niveles de API), alta fidelidad (llamadas y mensajes de texto simulados, ubicación del dispositivo, velocidad de red, sensores, acceso a Google Play Store) y rapidez (por ejemplo, para transferir datos). Apple, por su parte, ejecuta la app en dispositivos simulados de iOS, iPadOS, tvOS, visionOS o watchOS, o en un dispositivo físico emparejado con el Mac, elegidos desde el menú de destino de ejecución de Xcode; los simuladores se ejecutan hoy dentro de Device Hub (la denominación tradicional de esta pieza es Simulator) y, como advierte la propia documentación de Apple, no reproducen el rendimiento ni todas las funciones de un dispositivo físico, así que la verificación final conviene hacerla también sobre hardware real.
Una vez probada, cada plataforma tiene su propio circuito de distribución progresiva antes de llegar a todos los usuarios. En Google Play, una app se publica mediante versiones (releases) sobre pistas de prueba abierta, cerrada o interna (hasta 100 probadores elegidos) antes —o en paralelo— a la pista de producción, con la firma de la app gestionada por Play App Signing. En Apple, el camino equivalente es TestFlight: tras generar el archivo en Xcode, se distribuye a probadores internos o externos para recoger su opinión antes de enviar la versión definitiva a revisión y publicación en el App Store; Apple, además, exige que la app enviada a App Store Connect se haya compilado con una versión mínima concreta de Xcode y del SDK de la plataforma, que cambia con cada ciclo anual de versiones.
6. Nota: accesibilidad de las aplicaciones móviles del sector público
El Real Decreto 1112/2018, de 7 de septiembre, sobre accesibilidad de los sitios web y aplicaciones para dispositivos móviles del sector público, que incorpora al ordenamiento español la Directiva (UE) 2016/2102, define en su artículo 4.b) la «aplicación para dispositivos móviles» como «las aplicaciones informáticas diseñadas y desarrolladas para ser usadas por el público en general en dispositivos móviles, entre los que se incluyen los teléfonos inteligentes y las tabletas», aclarando expresamente que no incluyen el propio software que controla esos dispositivos (los sistemas operativos para dispositivos móviles) ni el equipo informático —justo la frontera que separa este tema (programar para el móvil) del que trata el sistema operativo móvil en sí—.
El real decreto exige que las apps móviles de las entidades obligadas (Administración General del Estado, Administraciones autonómicas, Administración Local, sector público institucional y Administración de Justicia, entre otras) sean perceptibles, operables, comprensibles y robustas (los cuatro principios de accesibilidad de su art. 4, l) a ñ)), y presume esa conformidad cuando la app cumple las normas armonizadas europeas correspondientes o, en su defecto, los requisitos pertinentes de la norma EN 301 549 V1.1.2 (2015-04). Según su disposición final quinta, todas las disposiciones relativas a aplicaciones para dispositivos móviles son de aplicación desde el 23 de junio de 2021.
Fuentes: Google, Android Developers — «Application fundamentals» (https://developer.android.com/guide/components/fundamentals); Google, Android Developers — «The activity lifecycle» (https://developer.android.com/guide/components/activities/activity-lifecycle); Google, Android Developers — «App manifest overview» (https://developer.android.com/guide/topics/manifest/manifest-intro); Google, Android Developers — «Meet Android Studio» (https://developer.android.com/studio/intro); Google, Android Developers — «Configure your build» (https://developer.android.com/build); Google, Android Developers — «Thinking in Compose» (https://developer.android.com/develop/ui/compose/mental-model); Google, Android Developers — «Jetpack Compose» (https://developer.android.com/compose); Google, Android Developers — «Permissions on Android» (https://developer.android.com/guide/topics/permissions/overview); Google, Android Developers — «About Android App Bundles» (https://developer.android.com/guide/app-bundle); Google, Play Console Help — «Prepare and roll out a release» (https://support.google.com/googleplay/android-developer/answer/9859348); Google, Android Developers — «Run apps on the Android Emulator» (https://developer.android.com/studio/run/emulator); JetBrains, Kotlin — «Kotlin for Android» (https://kotlinlang.org/docs/android-overview.html); JetBrains, Kotlin Multiplatform — «What is Kotlin Multiplatform» (https://kotlinlang.org/docs/multiplatform/kmp-overview.html, obtenida del repositorio oficial del proyecto); Apple — «UIKit» (https://developer.apple.com/documentation/uikit); Apple — «SwiftUI» (https://developer.apple.com/documentation/swiftui); Apple — «Swift» (https://developer.apple.com/documentation/swift); Apple, swift.org — «About Swift» (https://swift.org/about/); Apple — «About Objective-C» (https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/Introduction/Introduction.html); Apple — «Managing your app's life cycle» (https://developer.apple.com/documentation/uikit/managing-your-app-s-life-cycle); Apple — «Information Property List» (https://developer.apple.com/documentation/bundleresources/information-property-list); Apple — «Protected resources» (https://developer.apple.com/documentation/bundleresources/protected-resources); Apple — «Protecting the User's Privacy» (https://developer.apple.com/documentation/uikit/protecting-the-user-s-privacy); Apple — «TN3125: Inside Code Signing: Provisioning Profiles» (https://developer.apple.com/documentation/technotes/tn3125-inside-code-signing-provisioning-profiles); Apple — «Distributing your app for beta testing and releases» (https://developer.apple.com/documentation/xcode/distributing-your-app-for-beta-testing-and-releases); Apple — «Xcode» (https://developer.apple.com/documentation/xcode); Apple — «Running your app on simulated or physical devices» (https://developer.apple.com/documentation/xcode/running-your-app-on-simulated-or-physical-devices); Apple Developer — «Upcoming Requirements» (https://developer.apple.com/news/upcoming-requirements/); Google, Flutter — «Flutter architectural overview» (https://docs.flutter.dev/resources/architectural-overview); Google, Dart — «A tour of the Dart language» / portada (https://dart.dev/overview); Meta, React Native — «Core Components and Native Components» (https://reactnative.dev/docs/intro-react-native-components); Microsoft Learn — «What is .NET MAUI?» (https://learn.microsoft.com/en-us/dotnet/maui/what-is-maui); Apache Software Foundation, Apache Cordova — «Architectural overview of Cordova platform» (https://cordova.apache.org/docs/en/latest/guide/overview/index.html); MDN Web Docs — «Progressive web apps» (https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps); MDN Web Docs — «Making PWAs installable» (https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps/Guides/Making_PWAs_installable); MDN Web Docs — «Service Worker API» (https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API); BOE — Real Decreto 1112/2018, de 7 de septiembre, sobre accesibilidad de los sitios web y aplicaciones para dispositivos móviles del sector público, texto consolidado (https://www.boe.es/buscar/act.php?id=BOE-A-2018-12699). Consultadas el 11/10/2026.