Transformando um APP Android em iOS com KMM — Um exemplo real.
No momento em que estou escrevendo esse artigo (31/12/2020) o KMM encontra-se em alpha.
Bom, eu sempre trabalhei com desenvolvimento Android Nativo e a uns 4 anos atrás decidi criar um projetinho para que eu pudesse experimentar e estudar coisas novas do Android . Há mais ou menos 1 ano eu consegui lançar a primeira versão desse projeto na Google Play. A ideia desse aplicativo é ajudar as pessoas a encontrarem partidas de futebol amadoras próximas de suas localidades. Depois de ter lançado a primeira versão, pensei: beleza, agora eu preciso lançar uma versão para iOS. Então…quais opções eu tenho para fazer isso acontecer? 🤔
Eu tinha as seguintes cartas na mesa:
1📱 — Desenvolvo um app iOS do zero?
R: Não manjo de desenvolvimento iOS. Implementar um app todo do zero em uma linguagem /plataforma que eu não tenho domínio demoraria muito tempo.
2 🗑 — Jogo meu app Android no Lixo e refaço ele utilizando uma abordagem multiplataforma como Flutter ou React Native?
R: Poderia ser uma boa opção, pois teria a chance de aprender uma nova tecnologia que, no caso do Flutter, era uma tech que estava no hype do momento. Mas fazer isso seria o mesmo que dizer que eu gastei todo meu tempo fazendo esse app pra no final eu jogar ele no lixo. Não seria justo comigo mesmo! Imagina essa situação dentro de uma empresa…
3 🤡 💸 — Contratar alguém pra desenvolver pra mim?
Sim, eu não tenho dinheiro para bancar um dev iOS :)
4💡 — Então o que nos resta? Será que existe alguma forma de compartilhar pelo menos parte do meu código Android e desenvolver apenas uma pequena parte do iOS?
R: Sim! E foi aí que eu conheci o Kotlin Multiplatform Mobile, vulgo KMM \o/.
Breve introdução ao KMM
Ao contrário do que muitos pensam o KMM não é mais uma tecnologia que veio para "disputar" com o Flutter e React Native. A palavra-chave do Kotlin Multiplatform é compartilhamento. A ideia é compartilhar código Kotlin entre as diferentes plataformas ao passo que cada plataforma é livre para trabalhar de sua própria maneira, usando os artefatos gerados pelo SDK como bibliotecas. A imagem a seguir ilustra bem isso:
Basicamente é isso. Não tem muito segredo. Para mais detalhes você encontra a documentação oficial aqui.
Antes…
A arquitetura da minha aplicação Android estava implementada seguindo a estrutura do Clean Architecture em conjunto com o MVP na camada de aprensentação. Também me apoiei em conceitos do DDD como o Domain Model e Domain Service em forma de Use Cases. Todos esses conceitos me ajudaram muito na hora de migrar o código Android para o KMM. A seguir uma ilustração dessa antiga arquitetura:
Como o aplicativo precisa fazer trabalho offline e em diversos momentos eu preciso sincronizar os dados, eu criei mais duas abstrações além do Repository: LocalDataSource e RemoteDatasource. Com isso, eu conseguia isolar a lógica de sincronização dos dados das tecnologias que eu estava usando, pois já visava trocar o Realm em algum momento.
Durante…
Bom, como nem tudo são flores eu precisei refatorar algumas partes do meu aplicativo antes de colocar ele dentro da estrutura do KMM. Mais específicamente as partes destacadas em vermelho:
Os motivos foram esses:
- Dagger2 — Não tem suporte para o KMM. Tive que criar uma espécie de service locator e injetar as depências manualmente. Talvez não seja a melhor solução, mas foi o que funcionou até agora.
- Synthetics — Não tem suporte para KMM, além de ter sido descontinuado pela equipe da JetBrains. Tive que migrar todas as minhas telas para ViewBinding (parabéns aos envolvidos) 😂
- Realm — Não tem suporte para o KMM. Substituí pelo SQDLight.
- Retrofit — Não tem suporte para o KMM. Substituí pelo Ktor.
Além dessas refatorações, eu movi todas as minhas activities e fragments do módulo Presentation para o módulo Android do KMM, já que a intenção era compartilhar o código do presenter "pra cima".
As partes destacadas em azul ilustram essas mudanças:
Depois…
Depois de preparar o terreno, foi a hora de "plugar" a aplicação na estrutura do KMM. O resultado ficou assim:
Com essa estrutura, já tinha minha aplicação Android funcionando normalmente. Logo depois comecei a criar a primeira tela do iOS. Apenas para testar se realmente estava funcionando, fiz a tela de login já utilizando o SwfitUI. Como a lógica de validação de campos estava na classe LoginPresenter, eu literalmente so precisei desenhar a tela e injetar o Presenter na View. E pronto! Fucionou bacana.
Disponibilizei parte desse código no meu Github.
Dificuldades e aprendizados
1 — No momento da escrita desse artigo (31/12/2020), o KMM encontrava-se em alpha. Portanto, tive muita dificuldade em encontar ajuda aos erros bizarros em que eu dava de cara. Agradeço ao Wellington Costa por me ajudar em alguns pontos.
2 — Tive muita dificuldade em fazer o projeto buildar por causa das versões das bibliotecas multiplataformas que estavam na versão alfa/beta.
3 — Apanhei bastante para conseguir integrar o Presenter que trabalha de uma forma mais imperativa com o SwiftUI. Tive que criar uma classe intermediária para traduzir os comandos do Presenter em estados.
4 —Tive que tirar as referencias às classes “Calendar” e “Date” (java.util.*).
Para isso, tive que substituir em todo o codigo do modulo “shared” que utilizava tais classes para utilizarem apenas a representação de datas em mili segundos em conjunto com a biblioteca Klock.
5 — Seguir as ideias do DDD e Clean aliadas com boas práticas de programação OO fez muita diferença, pois possibilitou a portabilidade do código de forma mais suave, mesmo o KMM estando ainda imaturo no momento.
Bom, é isso. Essa foi a minha experiência inicial com o Kotlin Multiplatform. Espero escrever novos artigos aqui falando dos avanços do KMM e da evolução do meu app iOS. Falem comigo pelas minhas redes sociais >< (Linkedin/Github). Abraços!