¿Qué estrategias de compartir código entre iOS y Android existen más allá de 'elige un framework multiplataforma'?
React Native y Flutter no son la única forma de evitar escribir todo dos veces — muchos equipos comparten un núcleo en Kotlin, Rust o C++ debajo de interfaces totalmente nativas.
APRENDE HACIENDOPon a prueba tu intuición3 pasos
¿Qué estrategias de compartir código entre iOS y Android existen más allá de 'elige un framework multiplataforma'?
Usa los ejemplos y la explicación de la lección para comprobar tu idea.
Reflexión personal. No se envía a ningún servicio de IA.#Replanteando la pregunta
Una respuesta junior a "cómo compartes código entre iOS y Android" es "React Native o Flutter". Una respuesta senior reconoce que esos frameworks resuelven un problema específico —compartir UI y lógica de negocio juntas— y que muchos equipos mantienen deliberadamente la UI totalmente nativa mientras comparten todo lo que está debajo. Esta distinción importa comercialmente: una app fintech con requisitos de cumplimiento fuertes o una app de cámara con pipelines de renderizado a medida a menudo no puede aceptar ninguna abstracción sobre la UI de la plataforma, pero aun así quiere evitar mantener dos implementaciones paralelas de la lógica de precios o de un algoritmo de procesamiento de imágenes.
#Estrategia 1: Kotlin Multiplatform (KMP) para una capa de lógica compartida
Kotlin Multiplatform compila el mismo código Kotlin a bytecode de JVM para Android y a un binario nativo vía Kotlin/Native para iOS (empaquetado como un .xcframework consumible desde Swift). La UI se mantiene 100% nativa —SwiftUI o UIKit en iOS, Jetpack Compose o Views en Android— mientras que el networking, el caché, las reglas de negocio y los modelos de datos viven en un único módulo compartido.
El patrón idiomático para todo lo que deba diferir por plataforma es expect/actual: declaras la forma una vez en código compartido y das un cuerpo específico de cada lado.
// commonMain/kotlin/data/WeatherRepository.ktclass WeatherRepository(private val httpClient: HttpClient, private val logger: PlatformLogger) {suspend fun fetchForecast(city: String): Forecast {logger.log("WeatherRepository", "Fetching forecast for $city")return httpClient.get("https://api.example.com/forecast") {parameter("city", city)}.body()}}// commonMain/kotlin/platform/PlatformLogger.ktexpect class PlatformLogger() {fun log(tag: String, message: String)}
// androidMain/kotlin/platform/PlatformLogger.ktactual class PlatformLogger actual constructor() {actual fun log(tag: String, message: String) {android.util.Log.d(tag, message)}}
// iosMain/kotlin/platform/PlatformLogger.ktactual class PlatformLogger actual constructor() {actual fun log(tag: String, message: String) {platform.Foundation.NSLog("$tag: $message")}}
Del lado de iOS, Swift consume el framework compilado como si fuera cualquier otra dependencia de Swift:
let repository = WeatherRepository(httpClient: KtorClientFactory().create(), logger: PlatformLogger())Task {do {let forecast = try await repository.fetchForecast(city: "Bogotá")await MainActor.run { self.viewModel.update(forecast) }} catch {// manejar el error}}
#Estrategia 2: un núcleo compartido en C++ o Rust
Los dominios críticos en rendimiento o cargados de algoritmos (códecs, criptografía, física, pipelines de cámara, billeteras cripto) suelen compartir un núcleo en C o C++ envuelto con JNI en Android y una capa de bridging en Objective-C++ (o un header de interoperabilidad Swift/C) en iOS. Rust se volvió popular para el mismo propósito por su seguridad de memoria sin recolector de basura, más herramientas como UniFFI, que genera bindings de Kotlin y Swift directamente desde una definición de interfaz en Rust, eliminando la mayor parte del código de conexión escrito a mano que hacía dolorosa la compartición en C++.
#Estrategia 3: contratos de API y generación de código compartidos
Incluso sin compartir un runtime compilado, los equipos reducen la duplicación generando clientes tipados desde una única fuente de verdad —una especificación OpenAPI o un esquema GraphQL— tanto en Swift como en Kotlin. Ninguna plataforma comparte código ejecutable, pero los modelos de request/response, las reglas de validación y las firmas de los endpoints se mantienen sincronizados automáticamente en lugar de copiarse a mano en dos lenguajes y desalinearse con el tiempo.
#Estrategia 4: UI dirigida por el servidor (server-driven UI)
Para pantallas tipo catálogo o promocionales que cambian con frecuencia (un carrusel de merchandising, un flujo de checkout con layouts en A/B testing), algunos equipos envían la descripción del layout —no código— desde el backend: un esquema JSON que describe qué componentes renderizar y en qué orden, interpretado por un renderer nativo delgado en cada plataforma. Esto no es compartir código en el sentido pleno, pero centraliza la parte de la UI que cambia con más frecuencia y permite que un equipo de backend publique un cambio de layout sin pasar por una actualización en la tienda de apps.
#Estrategia 5: adopción incremental tipo "brownfield"
En lugar de apostar todo o nada a un framework, algunos equipos incrustan un motor de React Native o Flutter dentro de pantallas específicas de una app nativa ya existente —una técnica conocida comúnmente como integración "brownfield". Esto permite pilotear la compartición de código multiplataforma en una superficie de bajo riesgo (por ejemplo, una pantalla de configuración o un módulo de chat de soporte) mientras el shell de navegación, los flujos centrales y todo lo que tenga requisitos fuertes específicos de plataforma se mantienen nativos. Es una cobertura de riesgo: si el módulo piloto rinde mal, el radio de impacto es una pantalla, no toda la app.