¿Por qué Kotlin no es solo para Android?
\n\nSi todavía piensa que Kotlin es simplemente un "Java mejorado" para Android, se está perdiendo lo principal. En un mundo donde todos quieren escribir un solo código y ejecutarlo en todas partes, Kotlin Multiplatform (KMP) se está convirtiendo en un verdadero "asesino" de frameworks como Flutter y React Native. Pero a diferencia de ellos, Kotlin no le obliga a renunciar a la UI nativa ni a aprender JavaScript.
\n\nImagine: escribe la lógica de negocio, las solicitudes de red y el trabajo con la base de datos una sola vez en Kotlin. Luego usa ese código en Android, iOS, en la web e incluso en el servidor (Ktor). ¿Suena a ciencia ficción? Bienvenido a la realidad del desarrollo multiplataforma con Kotlin.
\n\nEn este artículo no solo hablaremos de teoría. Mostraremos ejemplos de código reales, compararemos KMP con sus competidores y le daremos un plan paso a paso para empezar hoy mismo.
\n\n¿Qué es Kotlin Multiplatform (KMP) y para qué sirve?
\n\nKMP no es un framework. Es una tecnología de compilación integrada en el lenguaje Kotlin. Permite compilar código común (common code) para diferentes plataformas:
\n\n- \n
- Android — bytecode de JVM (como de costumbre). \n
- iOS — binario nativo (a través de LLVM). \n
- JavaScript/Web — código JS para el navegador. \n
- Native (Linux, Windows, macOS) — a través de Kotlin/Native. \n
La característica principal: escribe expect/actual — declara la API esperada en el código común, y la implementa por separado para cada plataforma. Todo lo demás es común.
\n\nEjemplo 1: Módulo común para solicitudes de red
\n\nEscribamos un cliente HTTP simple que funcione en Android e iOS. Para ello usaremos Ktor, un cliente escrito en Kotlin.
\n\nPaso 1: Estructura del proyecto (Gradle)
\n\nEn el archivo build.gradle.kts de su módulo común:
plugins {\n kotlin("multiplatform")\n id("io.ktor.plugin") version "2.3.7"\n}\n\nkotlin {\n androidTarget()\n iosX64()\n iosArm64()\n iosSimulatorArm64()\n\n sourceSets {\n val commonMain by getting {\n dependencies {\n implementation("io.ktor:ktor-client-core:2.3.7")\n implementation("io.ktor:ktor-client-content-negotiation:2.3.7")\n implementation("io.ktor:ktor-serialization-kotlinx-json:2.3.7")\n }\n }\n val androidMain by getting {\n dependencies {\n implementation("io.ktor:ktor-client-android:2.3.7")\n }\n }\n val iosMain by creating {\n dependsOn(commonMain)\n dependencies {\n implementation("io.ktor:ktor-client-darwin:2.3.7")\n }\n }\n }\n}\n\nPaso 2: Código común (commonMain)
\n\nCreemos una data class y un cliente API:
\n\n// commonMain/kotlin/com/example/User.kt\nimport kotlinx.serialization.Serializable\n\n@Serializable\ndata class User(\n val id: Int,\n val name: String,\n val email: String\n)\n\n// commonMain/kotlin/com/example/ApiClient.kt\nimport io.ktor.client.*\nimport io.ktor.client.call.*\nimport io.ktor.client.plugins.contentnegotiation.*\nimport io.ktor.client.request.*\nimport io.ktor.serialization.kotlinx.json.*\n\nclass ApiClient {\n private val client = HttpClient {\n install(ContentNegotiation) {\n json()\n }\n }\n\n suspend fun fetchUser(id: Int): User {\n return client.get("https://jsonplaceholder.typicode.com/users/$id").body()\n }\n}\n\n¿Lo ve? Sin ifs, sin comprobaciones de plataforma. Solo Kotlin puro.
\n\nEjemplo 2: Expect/Actual para trabajar con SharedPreferences
\n\nSupongamos que necesitamos guardar un token. En Android — SharedPreferences, en iOS — UserDefaults. Mostramos la magia de KMP:
\n\nCódigo común (commonMain):
\n\n// commonMai