# Kotlin Multiplatform 2026: compartir código Android/iOS > Cómo Kotlin Multiplatform comparte la lógica de negocio entre Android e iOS en 2026: configuración de Gradle, expect/actual, red con Ktor, interfaz Compose Multiplatform y preguntas de entrevista de KMP comunes. - Published: 2026-06-26 - Updated: 2026-07-06 - Author: SharpSkill - Tags: Kotlin Multiplatform, KMP, Android, iOS, Cross-platform - Reading time: 10 min --- Kotlin Multiplatform (KMP) permite que una sola base de código Kotlin impulse la lógica de negocio de las aplicaciones Android e iOS, mientras cada plataforma conserva su interfaz totalmente nativa, su rendimiento y el acceso a su SDK. En 2026, KMP ya no es un experimento: la tecnología es estable, cuenta con el respaldo oficial de [Google](https://developer.android.com/kotlin/multiplatform) y se ejecuta en producción en Netflix, McDonald's, Forbes y Cash App. Este análisis detalla cómo funciona realmente el uso compartido de código, cómo configurar un módulo compartido y las concesiones que conviene entender antes de una entrevista de KMP. > **Qué comparte realmente KMP** > > Kotlin Multiplatform comparte lógica compilada, no un runtime. El Kotlin común se compila a bytecode de la JVM para Android y a un binario nativo (mediante LLVM) para iOS. No hay puente de JavaScript, ni webview embebida, ni capa de reflexión, así que el código compartido se ejecuta a velocidad nativa en ambas plataformas. ## Cómo Kotlin Multiplatform comparte código sin sacrificar el rendimiento nativo El mecanismo central es una jerarquía de source sets. `commonMain` contiene el Kotlin independiente de la plataforma: red, serialización, reglas de negocio y view models. Los source sets de plataforma como `androidMain` e `iosMain` contienen el código que toca las API nativas. El compilador produce un artefacto JVM a partir de `commonMain` más `androidMain` para Android, y un framework nativo a partir de `commonMain` más `iosMain` para iOS. En 2026, Kotlin Multiplatform favorece compartir lo que realmente se beneficia de una única fuente de verdad: modelos, clientes de API, caché y validación. La interfaz puede compartirse con Compose Multiplatform o dejarse totalmente nativa con SwiftUI, pantalla por pantalla. Este modelo de «compartir la lógica, elegir la interfaz» es la razón por la que los equipos adoptan KMP sin reescribir las aplicaciones existentes. Kotlin Multiplatform es una tecnología de Kotlin que compila un módulo compartido hacia varios targets (Android en la JVM, iOS como nativo, además de escritorio y web), de modo que la lógica de negocio se escribe una sola vez mientras cada plataforma conserva su interfaz nativa y el acceso completo a su SDK. Google ahora lo admite como una opción de primer nivel, y bibliotecas de Jetpack como Room, DataStore, ViewModel y Paging ofrecen artefactos KMP. Decidir qué pertenece al módulo compartido es la decisión de arquitectura más importante de un proyecto KMP. La siguiente tabla refleja cómo la mayoría de los equipos en producción trazan la línea en 2026: todo lo que está por debajo de la capa de presentación es un buen candidato para compartir, mientras que todo lo que toca las convenciones de UX de la plataforma suele quedarse nativo. | Capa | ¿Compartir en commonMain? | Motivo | |-------|---------------------|--------| | Modelos de datos y DTO | Sí | Contrato de API idéntico en ambas plataformas | | Red y caché | Sí | Una sola pila HTTP, una sola superficie de errores | | Reglas de negocio y validación | Sí | El dominio no cambia según el sistema operativo | | View models y estado | Normalmente | Compose y SwiftUI consumen ambos el estado compartido | | Renderizado de la interfaz | Opcional | Compartido con Compose o nativo por pantalla | | API de plataforma (cámara, biometría) | No | Exponer mejor mediante expect/actual | La ganancia aumenta según cuánto abarque una aplicación de la mitad inferior de esa tabla. Los productos con muchos datos e interfaces ligeras comparten lo máximo; las aplicaciones muy a medida y basadas en animaciones comparten lo mínimo. Ninguno de los dos extremos está mal, y por eso KMP se adapta a una adopción incremental en lugar de a una reescritura de todo o nada. ## Configurar el módulo compartido en Gradle Un módulo KMP declara sus targets y source sets en Kotlin DSL. El siguiente `build.gradle.kts` configura Android y las tres arquitecturas de iOS (dispositivo físico, simulador Intel, simulador Apple Silicon) y expone la salida de iOS como un framework llamado `Shared`. ```kotlin // shared/build.gradle.kts plugins { kotlin("multiplatform") kotlin("plugin.serialization") id("com.android.library") } kotlin { // Android target compiles commonMain + androidMain to JVM bytecode androidTarget() // Three iOS targets: device, Intel sim, Apple Silicon sim listOf(iosArm64(), iosX64(), iosSimulatorArm64()).forEach { target -> target.binaries.framework { baseName = "Shared" // becomes "import Shared" in Swift isStatic = true } } sourceSets { commonMain.dependencies { implementation("io.ktor:ktor-client-core:3.3.0") implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.9.0") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.2") } androidMain.dependencies { implementation("io.ktor:ktor-client-okhttp:3.3.0") } iosMain.dependencies { implementation("io.ktor:ktor-client-darwin:3.3.0") } } } ``` Cada target incorpora un motor de Ktor específico de la plataforma (`okhttp` en Android, `darwin` en iOS), mientras que el código compartido solo depende de la API común `ktor-client-core`. El conjunto completo de opciones está en la [documentación de Kotlin Multiplatform](https://kotlinlang.org/docs/multiplatform.html). ## Escribir código específico de la plataforma con expect y actual Cierta lógica no puede residir en `commonMain` porque necesita una API de plataforma. El mecanismo `expect`/`actual` declara la forma en el código común y aporta una implementación concreta por target, resuelta en tiempo de compilación y sin coste en tiempo de ejecución. ```kotlin // commonMain/kotlin/Platform.kt // commonMain declares what it needs but cannot implement here expect class Platform() { val name: String val osVersion: String } // Shared logic can now depend on Platform freely fun analyticsUserAgent(): String { val platform = Platform() return "SharpSkill/" + platform.name + " " + platform.osVersion } ``` La implementación de Android lee de la clase `Build` y la de iOS lee de `UIDevice`. Ambas cumplen el mismo contrato, por lo que `commonMain` nunca sabe con cuál está hablando. ```kotlin // androidMain/kotlin/Platform.kt import android.os.Build // actual supplies the Android-specific implementation actual class Platform actual constructor() { actual val name: String = "Android" actual val osVersion: String = Build.VERSION.RELEASE } ``` ```kotlin // iosMain/kotlin/Platform.kt import platform.UIKit.UIDevice // actual uses UIKit directly, no Objective-C bridging code required actual class Platform actual constructor() { actual val name: String = "iOS" actual val osVersion: String = UIDevice.currentDevice.systemVersion } ``` Kotlin/Native incluye enlaces tipados para todo el SDK de iOS, de modo que `UIDevice`, `NSUserDefaults` y los tipos de Foundation se pueden invocar desde Kotlin sin código de unión escrito a mano. ## Compartir la lógica de red y de serialización La red es el código más rentable de compartir porque los contratos de API son idénticos en ambas plataformas. Un único cliente de [Ktor](https://ktor.io/docs/) junto con `kotlinx.serialization` reemplaza dos implementaciones paralelas. Las funciones `suspend` se apoyan en las [corrutinas de Kotlin](/blog/android/mastering-kotlin-coroutines), que se traducen limpiamente a `async`/`await` en ambos lados. ```kotlin // commonMain/kotlin/data/InterviewRepository.kt import io.ktor.client.HttpClient import io.ktor.client.call.body import io.ktor.client.plugins.contentnegotiation.ContentNegotiation import io.ktor.client.request.get import io.ktor.serialization.kotlinx.json.json import kotlinx.serialization.Serializable // One serializable model, zero duplication across platforms @Serializable data class Question(val id: String, val prompt: String, val difficulty: String) // One HttpClient, one JSON parser, one repository for both apps class InterviewRepository { private val client = HttpClient { install(ContentNegotiation) { json() } } suspend fun fetchQuestions(tech: String): List = client.get("https://api.sharpskill.dev/questions/" + tech).body() } ``` Este repositorio, su lógica de reintentos y sus modelos de datos se escriben una sola vez. Un error corregido en el parser queda corregido en Android e iOS al mismo tiempo, y ahí es donde KMP amortiza su coste de configuración. El mismo patrón se extiende a una base de datos compartida con SQLDelight, un almacenamiento clave-valor compartido y una inyección de dependencias compartida con Koin, de modo que toda la capa de datos puede residir en `commonMain` detrás de interfaces de las que depende la interfaz de usuario. Un error habitual es intentar compartir demasiado demasiado pronto. El camino pragmático es empezar con un solo repositorio, probar el build y el pipeline de CI en ambas plataformas y luego mover las funcionalidades una a una a medida que crece la confianza. ## Construir una interfaz compartida con Compose Multiplatform Compose Multiplatform alcanzó el estado estable en iOS en mayo de 2025 (versión 1.8.0) y llegó a la 1.11.0 a mediados de 2026, con entrada de texto nativa y renderizado concurrente activado por defecto. Un `@Composable` en `commonMain` se dibuja en ambas plataformas, y los view models compartidos encajan de forma natural con una [arquitectura MVVM o MVI](/blog/android/mvvm-vs-mvi-architecture). ```kotlin // commonMain/kotlin/ui/QuestionList.kt import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.material3.Card import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import androidx.compose.foundation.layout.padding // This list renders identically on Android and iOS @Composable fun QuestionList(questions: List) { LazyColumn { items(questions) { question -> Card(modifier = Modifier.padding(8.dp)) { Text(text = question.prompt, modifier = Modifier.padding(16.dp)) } } } } ``` Compartir la interfaz es opcional y se hace pantalla por pantalla. Un equipo puede compartir toda la interfaz con [Compose Multiplatform](https://github.com/JetBrains/compose-multiplatform), mantener SwiftUI para toda la aplicación y compartir solo la lógica, o combinar ambas cosas. Las pantallas que dependen de una UX específica de la plataforma, como los widgets o los App Clips, suelen quedarse nativas. ## Consumir el código compartido desde Swift en iOS El módulo compartido se compila en un framework que Swift importa como cualquier biblioteca. Históricamente, la fricción venía de las funciones `suspend` de Kotlin y de `Flow`, que aparecían como API de callbacks incómodas. [SKIE](https://skie.touchlab.co/), de Touchlab, lo resuelve generando Swift idiomático: `suspend` se convierte en `async`/`await` nativo y `Flow` se convierte en `AsyncSequence`. ```swift // iosApp/ContentView.swift import SwiftUI import Shared // the KMP framework, baseName = "Shared" struct ContentView: View { @State private var questions: [Question] = [] private let repository = InterviewRepository() var body: some View { List(questions, id: \.id) { question in Text(question.prompt) } .task { // With SKIE, the Kotlin suspend fun is a Swift async fun questions = (try? await repository.fetchQuestions(tech: "android")) ?? [] } } } ``` El equipo de iOS escribe Swift y SwiftUI corrientes. Consume los modelos y repositorios compartidos exactamente como lo haría con un paquete Swift nativo, lo que mantiene a KMP invisible para la mayor parte del código de iOS. Dos asperezas todavía merecen mencionarse en una entrevista. Primero, los tiempos de build: la compilación de Kotlin/Native es más lenta que un build de Swift puro, aunque la caché de build de Gradle y la distribución de un XCFramework precompilado suavizan el coste diario. Segundo, la depuración a través de la frontera Swift-Kotlin está mejorando, pero aún no es tan fluida como quedarse en un solo lenguaje, otra razón por la que los equipos mantienen la superficie compartida deliberada en lugar de desbordada. ## Preguntas de entrevista sobre Kotlin Multiplatform que cabe esperar Como las preguntas de entrevista sobre KMP aparecen cada vez más en puestos de Android y móvil, conviene repasar algunas recurrentes junto con una [preparación de entrevistas de Android](/technologies/android) más amplia: - **¿En qué se diferencia expect/actual de una interfaz?** expect/actual se resuelve en tiempo de compilación para cada target, sin dispatch en tiempo de ejecución, y puede respaldar una clase, una función, una propiedad o un typealias. Una interfaz se resuelve dinámicamente en tiempo de ejecución. Se usa expect/actual para las API de plataforma e interfaces para el polimorfismo dentro del código compartido. - **¿Cómo gestiona Kotlin/Native la memoria y los hilos?** Desde Kotlin 1.7.20, el nuevo gestor de memoria eliminó el antiguo modelo de congelación de objetos, de modo que el estado mutable compartido y las corrutinas funcionan entre hilos casi igual que en la JVM. - **¿Puede una aplicación existente adoptar KMP de forma incremental?** Sí. El módulo compartido se entrega como un AAR para Android y un XCFramework para iOS, así que un repositorio o una funcionalidad puede migrar de a uno, sin reescritura. - **¿Cuándo debe la interfaz quedarse nativa en lugar de usar Compose Multiplatform?** Cuando una pantalla se apoya en una UX específica de la plataforma o cuando el equipo de iOS posee y prefiere controlar la capa de presentación. > **Cadena de herramientas en 2026** > > Apuntar a Kotlin 2.3 con el compilador K2, Ktor 3.x para la red, SQLDelight para una base de datos compartida y Koin para la inyección de dependencias. Para la interoperabilidad con iOS, agregar SKIE pronto: incorporarlo después, una vez escrita la capa Swift, resulta mucho más doloroso. ## Conclusión En 2026, Kotlin Multiplatform es una forma pragmática de recortar la lógica duplicada entre Android e iOS sin renunciar a la interfaz ni al rendimiento nativos: - Compartir la lógica que tiene una única fuente de verdad (modelos, red, validación) y elegir interfaz nativa o Compose pantalla por pantalla. - Usar `expect`/`actual` para las API de plataforma: se resuelve en tiempo de compilación, sin sobrecoste en tiempo de ejecución. - Escribir la red una sola vez con Ktor y `kotlinx.serialization` para que las correcciones lleguen a ambas plataformas a la vez. - Adoptarlo de forma incremental mediante un AAR y un XCFramework en lugar de comprometerse con una reescritura. - Agregar SKIE desde el principio para que Swift consuma `suspend` y `Flow` como `async`/`await` y `AsyncSequence` nativos. --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/es/blog/android/kotlin-multiplatform-2026