O Kotlin Multiplatform é o futuro do desenvolvimento multiplataforma? Dicas de como começar
Publicados: 2021-01-29Atualmente, podemos observar uma tendência no desenvolvimento mobile de lançar aplicativos mais rapidamente. Houve muitas tentativas de reduzir o tempo de desenvolvimento compartilhando partes de código comuns entre diferentes plataformas, como Android e iOS. Algumas soluções já ganharam popularidade, enquanto outras ainda estão em desenvolvimento. Hoje, gostaria de discutir uma das abordagens mais recentes do segundo grupo – Kotlin Multiplatform Mobile (KMM para abreviar).
O que é Kotlin Multiplatform Mobile?
O KMM é um SDK que visa principalmente compartilhar a lógica de negócios entre plataformas – a parte que na maioria dos casos precisa ser a mesma de qualquer maneira. Isso é conseguido graças a um conjunto de vários compiladores para um módulo compartilhado. Por exemplo, o destino Android usa uma variante Kotlin/JVM e, para iOS, há uma variante Kotlin/Native. Um módulo compartilhado pode ser adicionado a projetos de aplicativos nativos típicos e os desenvolvedores responsáveis pela interface do usuário podem se concentrar em fornecer a melhor experiência para os usuários em um ambiente familiar a eles – Android Studio para Android e Xcode para iOS.
Kotlin Multiplataforma vs Flutter
Atualmente, uma das soluções mais populares para o desenvolvimento de aplicativos multiplataforma é o Flutter. Ele é focado na regra “escrever um aplicativo e executá-lo em todos os lugares” – que funciona, mas apenas para aplicativos simples. Em cenários de casos reais, os desenvolvedores geralmente precisam escrever código nativo para cada plataforma de qualquer maneira para preencher as lacunas , por exemplo, quando algum plug-in está ausente. Com essa abordagem, o aplicativo tem a mesma aparência em diferentes plataformas, o que às vezes é desejável, mas, em alguns casos, pode quebrar diretrizes específicas de design.
Pronto para criar seu próprio aplicativo?
Escolha FlutuaçãoEmbora possam parecer semelhantes, o Kotlin Multiplatform não é uma solução multiplataforma – ele não tenta reinventar a roda. Os desenvolvedores ainda podem usar ferramentas que conhecem e gostam. Ele apenas simplifica o processo de reutilização de partes de código que anteriormente deveriam ter sido escritas várias vezes, como fazer solicitações de rede, armazenar dados e outras lógicas de negócios.
Prós e contras do Kotlin Multiplatform
Prós do KMM :
- O aplicativo desenvolvido é 100% nativo para cada plataforma – é fácil de integrar com o código usado atualmente e bibliotecas de terceiros
- Fácil de usar – quase todos os desenvolvedores Android já usam Kotlin, então há muito pouco conhecimento adicional necessário para eles começarem
- A interface do usuário pode ser dividida para cada plataforma de destino - o aplicativo parecerá consistente com qualquer ecossistema
- A lógica compartilhada permite que os desenvolvedores adicionem novos recursos e corrijam bugs em ambos os sistemas operacionais ao mesmo tempo
Contras do KMM :
- Muitos componentes ainda estão no estágio Alfa/Beta e potencialmente podem ser instáveis ou mudar no futuro
Quais empresas usam o KMM?
De acordo com o site oficial, as empresas estão cada vez mais se interessando por essa tecnologia e a lista está cada vez mais longa. Entre eles, existem marcas conhecidas como Autodesk, VMWare, Netflix ou Yandex.
Como começar a usar o Kotlin Multiplataforma?
O melhor lugar para mergulhar em informações detalhadas é o guia oficial, mas neste artigo, gostaria de mostrar um exemplo bastante simples, mas mais interessante do que apenas um “Hello World”, que seria buscar e exibir aplicativos o último quadrinho de Randall Munroe (licenciado sob CC BY-NC 2.5) com o título da API xkcd.com.
Características a serem cobertas:
- Configuração do projeto
- Rede no módulo compartilhado
- Interface simples para Android e iOS
Observação: eu queria que este exemplo fosse fácil de ler para desenvolvedores de Android e iOS, então, em alguns lugares, omiti intencionalmente algumas boas práticas específicas da plataforma apenas para deixar claro o que está acontecendo
Configuração do projeto
Primeiro, certifique-se de ter as versões mais recentes do Android Studio e do Xcode instaladas, pois ambos serão necessários para a construção deste projeto. Em seguida, no Android Studio, instale o plug-in KMM. Este plugin simplifica muitas coisas – para criar um novo projeto, basta clicar em Create New Project e selecionar KMM Application.
Após a criação do projeto, navegue até o arquivo build.gradle.kts
no diretório compartilhado . Aqui você deve especificar todas as dependências necessárias. Neste exemplo, usaremos ktor para a camada de rede, kotlinx.serialization
para analisar respostas json do back-end e corrotinas kotlin para fazer tudo de forma assíncrona.
Para simplificar, abaixo forneço listagens mostrando todas as dependências que devem ser adicionadas às já presentes. Ao adicionar dependências, basta sincronizar o projeto (aparecerá um prompt). Primeiro, adicione o plug-in de serialização à seção de plug-ins.
plug-ins { kotlin("plugin.serialization") versão "1.4.0" }
Em seguida, adicione dependências.
sourceSets { val commonMain obtendo { dependências { implementação("org.jetbrains.kotlinx:kotlinx-serialization-json:1.0.0") implementação("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9-native-mt-2") implementação("io.ktor:ktor-client-core:1.4.1") implementação("io.ktor:ktor-client-json:1.4.1") implementação("io.ktor:ktor-client-serialization:1.4.1") } } val androidMain obtendo { dependências { implementação("io.ktor:ktor-client-android:1.4.1") } } val iosMain obtendo { dependências { implementação("io.ktor:ktor-client-ios:1.4.1") } } }
Vale ressaltar que no momento em que este artigo está sendo escrito, existem alguns problemas com a versão estável da biblioteca coroutines no iOS – por isso a versão utilizada possui o sufixo native-mt-2 (que significa multithreading nativo). Você pode verificar o status atual deste problema aqui.
Rede no módulo compartilhado
Primeiro, precisamos de uma classe que represente a resposta – esses campos estão presentes no json retornado pelo backend.
import kotlinx.serialization.Serializable @Serializável classe de dados XkcdResponse( val img: String, val título: String, val dia: Int, val mês: Int, val ano: Int, )
Em seguida, precisamos criar uma classe representando a API com um cliente HTTP . Caso não tenhamos fornecido todos os campos presentes no json, podemos usar a propriedade ignoreUnknownKeys
para que o serializador possa ignorar os ausentes. Este exemplo tem apenas um endpoint representado por uma função suspensa. Esse modificador informa ao compilador que essa função é assíncrona. Vou descrevê-lo mais com código específico da plataforma.
importar io.ktor.client.* importe io.ktor.client.features.json.* importe io.ktor.client.features.json.serializer.* importar io.ktor.client.request.* class XkcdApi { private val baseUrl = "https://xkcd.com" private val httpCliente = HttpClient() { install(JsonFeature) { serializador = KotlinxSerializer( kotlinx.serialization.json.Json { ignoreUnknownKeys = true } ) } } suspender diversão fetchLatestComic() = httpClient.get<XkcdResponse>("$baseUrl/info.0.json") }
Quando nossa camada de rede estiver pronta, podemos passar para a camada de domínio e criar uma classe representando o modelo local de dados. Neste exemplo, pulei mais alguns campos e deixei apenas o título do quadrinho e a URL da imagem.
classe de dados ComicModel( val imageUrl: String, título do valor: String )
A última parte dessa camada é criar um caso de uso que acionará uma solicitação de rede e mapeará a resposta para o modelo local.

class GetLatestComicUseCase(private val xkcdApi: XkcdApi) { suspend fun run() = xkcdApi.fetchLatestComic() .let { ComicModel(it.img, it.title) } }
IU simples para Android
Hora de mover para o diretório androidApp
– é onde o aplicativo Android nativo é armazenado. Primeiro, precisamos adicionar algumas dependências específicas do Android ao outro arquivo build.gradle.kts
localizado aqui. Novamente, a listagem abaixo mostra apenas as dependências que devem ser adicionadas às já presentes. Este aplicativo usará a arquitetura Model-View-ViewModel (as duas primeiras linhas) e Glide para carregar uma imagem em quadrinhos do URL retornado (as duas segundas linhas)
dependências { implementação("androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0") implementação("androidx.lifecycle:lifecycle-livedata-ktx:2.2.0") implementação("com.github.bumptech.glide:glide:4.11.0") annotationProcessor("com.github.bumptech.glide:compiler:4.11.0") }
Por padrão, um projeto recém-criado deve conter MainActivity e seu arquivo de layout activity_main.xml
. Vamos adicionar algumas visualizações a ele – um TextView
para o título e um ImageView para o próprio quadrinho.
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:andro andróide: android:layout_width="match_parent" android:layout_height="match_parent" android:gravidade="centro" android:orientation="vertical"> <TextView andróide: android:layout_width="wrap_content" android:layout_height="wrap_content"/> <ImageView andróide: android:layout_width="match_parent" android:layout_height="wrap_content"/> </LinearLayout>
Em seguida , precisaremos de alguma representação do estado do aplicativo – ele pode estar carregando um novo quadrinho, exibindo-o ou podemos encontrar um erro durante o carregamento.
classe selada Estado { carregamento do objeto: State() class Success(val result: ComicModel): State() Erro de objeto: State() }
Agora vamos adicionar um ViewModel mínimo usando a lógica de negócios criada anteriormente . Todas as classes podem ser importadas. MutableLiveData
é um campo observável - a exibição observará as alterações nele e se atualizará de acordo. viewModelScope
é um escopo de corrotina vinculado ao ciclo de vida do viewmodel – caso o aplicativo seja fechado, ele cancelará automaticamente as tarefas pendentes.
class MainViewModel : ViewModel() { private val getLatestComicUseCase = GetLatestComicUseCase(XkcdApi()) val comic = MutableLiveData<State<ComicModel>>() divertido buscarComic() { viewModelScope.launch { comic.value = State.Loading() runCatching { getLatestComicUseCase.run() } .onSuccess { comic.value = State.Success(it) } .onFailure { comic.value = State.Error() } } } }
Uma última coisa – MainActivity para conectar tudo.
class MainActivity : AppCompatActivity(R.layout.activity_main) { valor privado viewModel: MainViewModel por lazy { ViewModelProvider(this).get(MainViewModel::class.java) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) viewModel.comic.observe(this) { Quando) { é Estado. Carregando -> { findViewById<TextView>(R.id.titleLabel).text = "Carregando" } é Estado.Sucesso -> { findViewById<TextView>(R.id.titleLabel).text = it.result.title Deslize.com(isto) .load(it.result.img) .into(findViewById(R.id.image)) } é State.Error -> { findViewById<TextView>(R.id.titleLabel).text = "Erro" } } } viewModel.fetchComic() } }
É isso aí, o aplicativo Android está pronto!
IU simples para iOS
Tudo acima foi feito no Android Studio, então para esta parte vamos mudar para o Xcode para torná-lo mais conveniente. Para fazer isso, basta abrir o Xcode e selecionar o diretório iosApp – ele contém um projeto Xcode pré-configurado. Por padrão, este projeto usa SwiftUI para GUI, então vamos mantê-lo para simplificar.
A primeira coisa a fazer é criar uma lógica básica para buscar dados de quadrinhos. Assim como antes, precisamos de algo para representar o estado.
enum Estado { carregamento de caso caso de sucesso (ComicModel) erro de caso }
Em seguida, vamos preparar um ViewModel mais uma vez
class ViewModel: ObservableObject { let getLatesteComicUseCase = GetLatestComicUseCase(xkcdApi: XkcdApi()) @Published var comic = State.loading iniciar() { self.comic = .loading getLatestComicUseCase.run { fetchedComic, erro em if fetchedComic != nil { self.comic = .success(fetchedComic!) } senão { self.comic = .error } } } }
E, por fim, a vista.
Nota: para simplificar, usei o componente RemoteImage do SwiftUI para exibir a imagem, assim como usei o Glide no Android.
struct ContentView: Visualizar { @ObservedObject private(set) var viewModel: ViewModel var body: some View { comicView() } -- private func comicView() -> some View { mudar viewModel.comic { caso .carregando: return AnyView(Text("Carregando")) case .result(deixe comic): return AnyView(VStack { Texto(comic.title) RemoteImage(url: comic.img) }) caso .erro: return AnyView(Text("Erro")) } } }
E pronto, o app para iOS também está pronto!
Resumo
Finalmente, para responder à pergunta do título – Kotlin Multiplatform é o futuro do desenvolvimento multiplataforma? – tudo depende das necessidades. Se você deseja criar um aplicativo pequeno e idêntico para as duas plataformas móveis ao mesmo tempo, provavelmente não, porque precisa ter o conhecimento necessário sobre desenvolvimento para ambas as plataformas.
Desenvolva seu próximo aplicativo com nossos especialistas
Faça uma cotaçãoNo entanto, se você já possui uma equipe de desenvolvedores Android e iOS e deseja oferecer a melhor experiência ao usuário, isso pode reduzir significativamente o tempo de desenvolvimento . Como no exemplo fornecido, graças a um módulo compartilhado, a lógica do aplicativo foi implementada apenas uma vez e a interface do usuário foi criada de maneira totalmente específica da plataforma. Então, por que não experimentá-lo? Como você pode ver, é fácil começar.
Curioso sobre o desenvolvimento multiplataforma de uma perspectiva de negócios? Confira nosso artigo sobre as vantagens do desenvolvimento multiplataforma.