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.

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 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.
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.
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.
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.
// 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.
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
}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 junto con kotlinx.serialization reemplaza dos implementaciones paralelas. Las funciones suspend se apoyan en las corrutinas de Kotlin, que se traducen limpiamente a async/await en ambos lados.
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<Question> =
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.
¿Listo para aprobar tus entrevistas de Android?
Practica con nuestros simuladores interactivos, flashcards y tests técnicos.
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.
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<Question>) {
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, 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, de Touchlab, lo resuelve generando Swift idiomático: suspend se convierte en async/await nativo y Flow se convierte en AsyncSequence.
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 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.
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/actualpara 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.serializationpara 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
suspendyFlowcomoasync/awaityAsyncSequencenativos.
¡Empieza a practicar!
Pon a prueba tu conocimiento con nuestros simuladores de entrevista y tests técnicos.
Etiquetas
Compartir
Artículos relacionados

Kotlin 2.3 para Android: Desestructuración por Nombre, KMP y Preguntas de Entrevista 2026
Preguntas de entrevista sobre Kotlin 2.3 para desarrolladores Android en 2026. Desestructuración por nombre, KMP, parámetros de contexto, Flow y coroutines con ejemplos de código.

Kotlin Flow vs StateFlow vs SharedFlow: preguntas de entrevista de Android en 2026
Las preguntas sobre Kotlin Flow vs StateFlow vs SharedFlow que hacen los entrevistadores de Android en 2026, con respuestas claras, una tabla comparativa y código listo para producción.

Android 16 en 2026: Nuevas APIs, Modo Escritorio y Preguntas de Entrevista
Análisis profundo de Android 16 API 36: edge-to-edge obligatorio, Desktop Mode, ProgressStyle, navegación predictiva y preguntas de entrevista técnica para desarrolladores Android en 2026.