¿Es Kotlin Multiplatform el futuro del desarrollo multiplataforma? Consejos sobre cómo empezar
Publicado: 2021-01-29Hoy en día, podemos observar una tendencia en el desarrollo móvil para lanzar aplicaciones más rápido. Hubo muchos intentos de reducir el tiempo de desarrollo compartiendo partes de código comunes entre diferentes plataformas como Android e iOS. Algunas soluciones ya han ganado popularidad, mientras que otras aún están en desarrollo. Hoy, me gustaría hablar sobre uno de los enfoques más nuevos del segundo grupo: Kotlin Multiplatform Mobile (KMM para abreviar).
¿Qué es Kotlin Multiplataforma móvil?
KMM es un SDK que tiene como objetivo principal compartir la lógica comercial entre plataformas , la parte que en la mayoría de los casos tiene que ser la misma de todos modos. Esto se logra gracias a un conjunto de múltiples compiladores para un módulo compartido. Por ejemplo, el objetivo de Android usa una variante de Kotlin/JVM, y para iOS hay una de Kotlin/Native. Luego, se puede agregar un módulo compartido a los proyectos típicos de aplicaciones nativas y los desarrolladores responsables de la interfaz de usuario pueden concentrarse en brindar la mejor experiencia para los usuarios en un entorno familiar para ellos: Android Studio para Android y Xcode para iOS.
Kotlin Multiplataforma vs Flutter
Actualmente, una de las soluciones más populares para el desarrollo de aplicaciones multiplataforma es Flutter. Se centra en la regla "escribe una aplicación y ejecútala en todas partes", que funciona, pero solo para aplicaciones simples. En escenarios de casos reales, los desarrolladores a menudo tienen que escribir código nativo para cada plataforma de todos modos para llenar los vacíos , por ejemplo, cuando falta algún complemento. Con este enfoque, la aplicación se ve igual en diferentes plataformas, lo que a veces es deseable, pero en algunos casos puede infringir pautas de diseño específicas.
¿Estás listo para crear tu propia aplicación?
Elija aleteoSi bien pueden parecer similares, Kotlin Multiplatform no es una solución multiplataforma, no intenta reinventar la rueda. Los desarrolladores aún pueden usar las herramientas que conocen y les gustan. Simplemente simplifica el proceso de reutilización de partes del código que anteriormente deberían haberse escrito varias veces, como realizar solicitudes de red, almacenar datos y otra lógica empresarial.
Pros y contras de Kotlin Multiplataforma
Ventajas de KMM :
- La aplicación desarrollada es 100% nativa para cada plataforma : es fácil de integrar con el código utilizado actualmente y las bibliotecas de terceros.
- Fácil de usar : casi todos los desarrolladores de Android ya usan Kotlin, por lo que se requiere muy poco conocimiento adicional para comenzar.
- La interfaz de usuario se puede dividir para cada plataforma de destino : la aplicación se sentirá consistente con cualquier ecosistema dado
- La lógica compartida permite a los desarrolladores agregar nuevas funciones y corregir errores en ambos sistemas operativos al mismo tiempo
Contras de KMM :
- Muchos componentes aún se encuentran en la etapa Alfa/Beta y pueden ser potencialmente inestables o cambiar en el futuro.
¿Qué empresas utilizan KMM?
Según el sitio oficial, las empresas están cada vez más interesadas en esta tecnología y la lista es cada vez más larga. Entre ellos, hay marcas tan conocidas como Autodesk, VMWare, Netflix o Yandex.
¿Cómo empezar con Kotlin Multiplatform?
El mejor lugar para bucear en busca de información detallada es la guía oficial, pero en este artículo, me gustaría mostrar un ejemplo que es bastante simple, pero más interesante que solo un "Hola mundo", que sería la búsqueda y visualización de aplicaciones. el último cómic de Randall Munroe (con licencia CC BY-NC 2.5) con su título de la API de xkcd.com.
Características a cubrir:
- configuración del proyecto
- Redes en el módulo compartido
- Interfaz de usuario simple para Android e iOS
Nota: Quería que esta muestra fuera tan fácil de leer para los desarrolladores de Android e iOS, por lo que en algunos lugares omití intencionalmente algunas buenas prácticas específicas de la plataforma solo para dejar en claro lo que está sucediendo.
configuración del proyecto
Primero, asegúrese de tener instaladas las últimas versiones de Android Studio y Xcode, ya que ambas serán necesarias para la compilación de este proyecto. Luego, en Android Studio, instale el complemento KMM. Este complemento simplifica muchas cosas: para crear un nuevo proyecto, simplemente haga clic en Crear nuevo proyecto y seleccione Aplicación KMM.
Después de crear el proyecto, navegue hasta el archivo build.gradle.kts
en el directorio compartido . Aquí debe especificar todas las dependencias requeridas. En este ejemplo, usaremos ktor para la capa de red, kotlinx.serialization
para analizar las respuestas json del backend y kotlin coroutines para hacerlo todo de forma asíncrona.
Para simplificar, a continuación proporciono listados que muestran todas las dependencias que deben agregarse a las que ya están presentes. Cuando agregue dependencias, simplemente sincronice el proyecto (aparecerá un aviso). Primero, agregue el complemento de serialización a la sección de complementos.
complementos { kotlin("complemento.serialización") versión "1.4.0" }
Luego agrega dependencias.
conjuntos de fuentes { val commonMain obteniendo { dependencias { implementación("org.jetbrains.kotlinx:kotlinx-serialization-json:1.0.0") implementación("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9-native-mt-2") implementación("io.ktor:ktor-client-core:1.4.1") implementación("io.ktor:ktor-client-json:1.4.1") implementación("io.ktor:ktor-client-serialization:1.4.1") } } val androidMain obteniendo { dependencias { implementación("io.ktor:ktor-client-android:1.4.1") } } val iosMain obteniendo { dependencias { implementación("io.ktor:ktor-client-ios:1.4.1") } } }
Vale la pena mencionar que en el momento en que se escribe este artículo, hay algunos problemas con la versión estable de la biblioteca de rutinas en iOS; es por eso que la versión utilizada tiene el sufijo native-mt-2 (que significa subprocesos múltiples nativos). Puede consultar el estado actual de este problema aquí.
Redes en el módulo compartido
Primero, necesitamos una clase que represente la respuesta : esos campos están presentes en json devuelto por el backend.
importar kotlinx.serialization.Serializable @Serializable clase de datos XkcdResponse( val img: Cadena, título de valor: Cadena, val día: Int, val mes: Int, val año: Int, )
A continuación, debemos crear una clase que represente la API con un cliente HTTP . En caso de que no proporcionemos todos los campos presentes en json, podemos usar la propiedad ignoreUnknownKeys
para que el serializador pueda ignorar los que faltan. Este ejemplo tiene solo un punto final representado por una función suspendida. Este modificador le dice al compilador que esta función es asíncrona. Lo describiré más con el código específico de la plataforma.
importar io.ktor.cliente.* importar io.ktor.client.features.json.* importar io.ktor.client.features.json.serializer.* importar io.ktor.client.request.* clase XkcdApi { valor privado baseUrl = "https://xkcd.com" valor privado httpClient = HttpClient() { instalar (JsonFeature) { serializador = KotlinxSerializer( kotlinx.serialización.json.Json { ignoreUnknownKeys = verdadero } ) } } suspender diversión fetchLatestComic() = httpClient.get<XkcdResponse>("$baseUrl/info.0.json") }
Cuando nuestra capa de red esté lista, podemos pasar a la capa de dominio y crear una clase que represente el modelo local de datos. En este ejemplo, omití algunos campos más y dejé solo el título del cómic y la URL de la imagen.
clase de datos ComicModel( val imageUrl: Cadena, título de valor: Cadena )
La última parte de esta capa es crear un caso de uso que activará una solicitud de red y luego asignará la respuesta al modelo local.

clase GetLatestComicUseCase(valor privado xkcdApi: XkcdApi) { suspender ejecución divertida () = xkcdApi.fetchLatestComic () .let { ComicModel(it.img, it.title) } }
Interfaz de usuario simple para Android
Es hora de pasar al directorio androidApp
: aquí es donde se almacena la aplicación nativa de Android. Primero, debemos agregar algunas dependencias específicas de Android al otro archivo build.gradle.kts
que se encuentra aquí. Nuevamente, la lista a continuación solo muestra las dependencias que deben agregarse a las que ya están presentes. Esta aplicación utilizará la arquitectura Model-View-ViewModel (las dos primeras líneas) y Glide para cargar una imagen cómica desde la URL devuelta (las dos segundas líneas)
dependencias { implementación ("androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0") implementación ("androidx.lifecycle:lifecycle-livedata-ktx:2.2.0") implementación ("com.github.bumptech.glide:glide:4.11.0") anotaciónProcesador("com.github.bumptech.glide:compilador:4.11.0") }
De forma predeterminada, un proyecto recién creado debe contener MainActivity y su archivo de diseño activity_main.xml
. Agreguemos algunas vistas: una TextView
para el título y una ImageView para el cómic en sí.
<?versión xml="1.0" codificación="utf-8"?> <LinearLayout xmlns:andro androide: android:layout_width="match_parent" android:layout_height="match_parent" android:gravedad="centro" android:orientación="vertical"> <Vista de texto androide: android:layout_width="wrap_content" android:layout_height="wrap_content”/> <ImagenVista androide: android:layout_width="match_parent" android:layout_height="wrap_content"/> </LinearLayout>
Luego , necesitaremos alguna representación del estado de la aplicación : puede estar cargando un nuevo cómic, mostrándolo o podemos encontrar un error durante la carga.
estado de clase sellado { Objeto Cargando: Estado () éxito de clase (resultado de val: ComicModel): Estado () Error de objeto: Estado () }
Ahora agreguemos un ViewModel mínimo usando la lógica de negocios creada anteriormente . Todas las clases se pueden importar. MutableLiveData
es un campo observable: la vista observará los cambios y se actualizará en consecuencia. viewModelScope
es un ámbito de rutina relacionado con el ciclo de vida del modelo de vista: en caso de que se cierre la aplicación, cancelará automáticamente las tareas pendientes.
clase MainViewModel : ViewModel() { valor privado getLatestComicUseCase = GetLatestComicUseCase(XkcdApi()) val comic = MutableLiveData<Estado<ModeloComic>>() divertido buscar cómic() { viewModelScope.lanzamiento { comic.value = Estado.Cargando() ejecutarCapturar { getLatestComicUseCase.run() } .onSuccess { comic.value = State.Success(it) } .onFailure { comic.valor = Estado.Error() } } } }
Una última cosa: MainActivity para conectar todo.
clase MainActivity: AppCompatActivity (R.layout.activity_main) { viewModel de valor privado: MainViewModel por perezoso { ViewModelProvider(esto).get(MainViewModel::class.java) } anula la diversión onCreate (savedInstanceState: ¿Paquete?) { super.onCreate (estado de instancia guardado) viewModel.comic.observar(esto) { cuando) { es Estado.Cargando -> { findViewById<TextView>(R.id.titleLabel).text = "Cargando" } es Estado.Éxito -> { findViewById<TextView>(R.id.titleLabel).text = it.result.title Glide.with (esto) .load(es.resultado.img) .into(findViewById(R.id.image)) } es Estado.Error -> { findViewById<TextView>(R.id.titleLabel).text = "Error" } } } verModelo.fetchComic() } }
Eso es todo, ¡la aplicación de Android está lista!
Interfaz de usuario sencilla para iOS
Todo lo anterior se hizo en Android Studio, así que para esta parte cambiemos a Xcode para que sea más conveniente. Para hacer esto, simplemente abra Xcode y seleccione el directorio iosApp ; contiene un proyecto Xcode preconfigurado. De forma predeterminada, este proyecto usa SwiftUI para GUI, así que sigamos con él para simplificar.
Lo primero que debe hacer es crear una lógica básica para obtener datos cómicos. Al igual que antes, necesitamos algo para representar el estado.
enumeración Estado { carga de casos éxito del caso (ComicModel) error de caso }
A continuación, preparemos un ViewModel una vez más.
clase ViewModel: ObservableObject { let getLatestComicUseCase = GetLatestComicUseCase(xkcdApi: XkcdApi()) @Published var comic = Estado.cargando en eso() { self.comic = .cargando getLatestComicUseCase.run { fetchedComic, error en si se obtieneComic != nil { self.comic = .success(¡buscadoComic!) } más { self.comic = .error } } } }
Y, finalmente, la vista.
Nota: para simplificar, usé el componente RemoteImage de SwiftUI para mostrar la imagen, al igual que usé Glide en Android.
estructura ContentView: Ver { @ObservedObject privado (conjunto) var viewModel: ViewModel var cuerpo: algunos Ver { comicView() } -- función privada comicView() -> alguna Vista { cambiar viewModel.comic { cargando el caso: devolver AnyView(Texto("Cargando")) case .result(let comic): devuelve CualquierVista(VStack { Texto(cómic.título) Imagen remota (url: comic.img) }) caso .error: devolver AnyView(Texto("Error")) } } }
Y eso es todo, ¡la aplicación iOS también está lista!
Resumen
Finalmente, para responder a la pregunta del título: ¿Kotlin Multiplatform es el futuro del desarrollo multiplataforma? – Todo depende de las necesidades. Si desea crear una aplicación pequeña e idéntica para ambas plataformas móviles al mismo tiempo, entonces probablemente no, porque necesita tener los conocimientos necesarios sobre el desarrollo de ambas plataformas.
Desarrolle su próxima aplicación con nuestros expertos
Consigue una cotizaciónSin embargo, si ya tiene un equipo de desarrolladores de Android e iOS y desea brindar la mejor experiencia de usuario, puede reducir significativamente el tiempo de desarrollo . Al igual que en el ejemplo proporcionado, gracias a un módulo compartido, la lógica de la aplicación se implementó solo una vez y la interfaz de usuario se creó de forma completamente específica para la plataforma. Asi que, por que no intentarlo? Como puede ver, es fácil empezar.
¿Tiene curiosidad por el desarrollo multiplataforma desde una perspectiva empresarial? Consulte nuestro artículo sobre las ventajas del desarrollo multiplataforma.