O Kotlin Multiplatform é o futuro do desenvolvimento multiplataforma? Dicas de como começar

Publicados: 2021-01-29

Atualmente, 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.

Ícone de serviços de desenvolvimento de plataforma cruzada

Pronto para criar seu próprio aplicativo?

Escolha Flutuação

Embora 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.

Criar um novo projeto Kotlin Multiplatform Mobile

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!

Um aplicativo Android desenvolvido com KMM

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!

Um aplicativo iOS desenvolvido com KMM

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.

Liberando o ícone do produto

Desenvolva seu próximo aplicativo com nossos especialistas

Faça uma cotação

No 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.