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.
