DEV Community

Rodrigo Sicarelli
Rodrigo Sicarelli

Posted on • Updated on

KMP 101: Dominando os princípios dos Source Sets

No último artigo (🔗 KMP 101: Entendendo como o Kotlin compila para multiplas plataformas), aprendemos sobre o frontend, IR e backend do compilador do Kotlin.

Dessa vez, vamos entender um conceito-chave para codar em KMP: os source sets


Introdução aos source sets no KMP

Os source sets no Kotlin são essenciais para o desenvolvimento multiplataforma. Utilizando uma arquitetura hierárquica, os source sets nos permitem organizar nosso código-fonte, declarar dependências específicas para cada alvo e também nos permitem configurar opções de compilação de forma isolada para diferentes plataformas em um mesmo projeto.

Pense em um source set no KMP como uma 'pasta especial' em um projeto, onde cada pasta tem um propósito (ou plataforma) específico. Por exemplo, a pasta "comum" contém arquivos usados em todas as plataformas, enquanto pastas específicas, como "android" ou "iOS", abrigam arquivos exclusivos para essas plataformas.

O compilador do Kotlin identifica essas pastas especiais e se encarrega de compilar seu conteúdo (código-fonte), conforme as estratégias de compilação exploradas em 🔗 KMP 101: Entendendo como o Kotlin compila para multiplas plataformas.

Entendendo a função e a estrutura básica de um source set

Cada source set em um projeto multiplataforma possui um nome único e contém um conjunto de arquivos de código-fonte e recursos (arquivos, ícones, etc). Ele especifica um alvo (target) para o qual o código será compilado.

Assumindo que as configurações necessárias foram aplicadas (as quais abordaremos em artigos futuros), a estrutura de pastas abaixo orienta o compilador do Kotlin a:

  1. Inicializar e compilar os seguintes alvos: android, iOS, watchOS, tvOS, js, wasm e desktop.
  2. Compilar o código-fonte dentro do source set common para todas as plataformas, tornando os membros do arquivo Common.kt disponíveis nativamente para cada plataforma definida.
  3. Ao final da compilação, gerar arquivos específicos para cada plataforma (.class, .so, .js, .wasm), com todos os membros do Common.kt disponíveis.

Estrutura basica source set

A natureza hierárquica dos source sets

Os source sets do KMP funcionam como uma árvore genealógica.

Na base da árvore, temos os ancestrais comuns (o source set commonMain), cujas características são compartilhadas por todos na família. À medida que avançamos para os galhos, encontramos os source sets intermediários, que representam ramos da família com características únicas compartilhadas por um subconjunto de membros (por exemplo, apple ou native).

Finalmente, nas extremidades dos galhos, estão os membros individuais da família (os source sets específicos da plataforma, como iosArm64 ou iosSimulatorArm64), cada um com suas próprias características únicas.

Isso permite organizar uma hierarquia de source sets intermediários com total controle do que cada source set irá compartilhar.

Hierarquia padrão do KMP

Source sets comuns vs. específicos

No KMP, a distinção entre source sets comuns e específicos de plataforma é fundamental para entender como o código é compartilhado e gerenciado entre diferentes alvos.

Source set comum (commonMain)

O source set comum, geralmente localizados no diretório commonMain, representa a base do compartilhamento de código no Kotlin Multiplataforma. Aqui, você escreve o código Kotlin que é compartilhado entre todas as plataformas-alvo do projeto. Este código pode incluir lógica de negócios, modelos de dados, e funcionalidades agnósticas em relação à plataforma subjacente.

É importante notar que, embora este código seja compartilhado, ele não deve conter nenhuma funcionalidade ou chamada de API que seja específica a uma plataforma. O compilador Kotlin assegura isso, evitando o uso de funções ou classes específicas de plataforma no código comum, uma vez que esse código é compilado para diferentes alvos.

Source sets específicos de plataforma

Enquanto o código comum oferece uma grande vantagem na reutilização de código, nem tudo pode ser generalizado para todas as plataformas. É aqui que entram os source sets específicos de plataforma, como androidMain, iosMain, desktopMain, entre outros. Esses source sets contêm código específico para uma plataforma, sendo compilados apenas para seu respectivo alvo.

Por exemplo, o source set androidMain pode conter chamadas de API Android, enquanto iosMain pode utilizar APIs específicas do iOS. Isso permite que você tire proveito das características e APIs únicas de cada plataforma, mantendo simultaneamente, uma base de código comum significativa no commonMain.

Escolhendo entre comum e específico

Ao desenvolver um projeto Kotlin Multiplataforma, uma parte significativa do seu esforço será dedicada a decidir o que vai ao código comum e o que precisa ser implementado de forma específica para cada plataforma. A regra geral é maximizar o código comum, recorrendo a source sets específicos de plataforma apenas quando for necessário acessar funcionalidades ou APIs que não estão disponíveis de forma genérica.

Essa abordagem não só simplifica a manutenção do código, como também assegura a consistência em todas as plataformas, aproveitando ao máximo o potencial do Kotlin Multiplataforma.

Source set intermediário

Vamos supor que temos um projeto KMP com os source sets commonMain, androidMain e appleMain. Dentro do source set comum, temos uma interface definida chamada InterfaceComum que funciona como um contrato ao qual todas as plataformas precisam aderir.

Derivando da InterfaceComum, temos InterfaceApple e InterfaceAndroid: a InterfaceApple adiciona funcionalidades específicas para o ecossistema Apple, enquanto InterfaceAndroid faz o mesmo para dispositivos Android.

Esse design garante que, embora compartilhemos a lógica comum pela InterfaceComum, cada plataforma pode ter suas próprias extensões e funcionalidades, mantendo a separação e a especialização do código conforme necessário.

Esse conceito é chamado de intermediary source sets:

Um source set intermediário é um conjunto de source set que compila para alguns, mas não para todos os alvos do projeto.

Exemplo *source sets*

Source set de teste

Os testes no Kotlin Multiplataforma também são tratados como um source set. O que significa que cada plataforma pode ter seus próprios testes específicos se utilizando, por exemplo, o SDK nativo ou outras bibliotecas open source nativas.

O source set comum também pode (e deve!) ter seus próprios testes, porém você irá precisar utilizar outras bibliotecas KMP para a escrita multiplataforma, como, por exemplo, o 🔗 kotlin.test, 🔗 turbine ou 🔗 assertk.

Exemplo *source sets*

Gerenciando dependências nos source sets

Em projetos Kotlin Multiplataforma, a gestão eficiente de dependências nos source sets é crucial para manter a modularidade e a eficiência do código.

O KMP nos permite ter controle individual das dependências de cada source set, nos possibilitando ainda criar relações/dependências entre elas.

Dependências no source set comum

No source set comum (commonMain), as dependências incluem bibliotecas utilizáveis em todas as plataformas suportadas pelo projeto. Estas bibliotecas fornecem funcionalidades que são independentes de qualquer plataforma específica, como lógica de negócios, algoritmos ou utilitários comuns. A inclusão de uma biblioteca no source set comum significa que essa funcionalidade estará disponível para todos os alvos do projeto, promovendo a reutilização do código e a consistência entre plataformas.

Isso significa que, ao declarar uma depêndencia comum, todos os outros source sets também terão essa dependencia, que, por sua vez, é uma dependência KMP que oferece funcionalidades agnósticas de plataforma.

Dependências em source sets específicos

Contrastando com o source set comum, os source sets específicos de plataforma, como androidMain ou iosMain, focam em dependências que são relevantes apenas para uma plataforma particular. Essas dependências são utilizadas para acessar APIs, bibliotecas ou recursos que são exclusivos a uma plataforma, permitindo que os desenvolvedores aproveitem as funcionalidades nativas e otimizem a experiência do usuário em cada plataforma.

Exemplo *source sets*

Convenções adotadas pela comunidade

O KMP é extremamente flexível, nos possibilitando nomear e manipular nossos source sets como preferirmos.

Porém, no decorrer dos anos, a comunidade foi adotando algumas convenções, e o próprio KMP foi se adequando ao redor dessas convenções, oferecendo algumas facilidades na configuração do projeto. Vamos explorar as principais delas

1: Nomes utilizando "camelCase"

A comunidade geralmente adota a nomenclatura cammelCase para a definição dos source sets.

2: Sufixo "main"

O diretório main em projetos que utilizam linguagens da JVM, como Java e Kotlin, é tradicionalmente usado para armazenar o código-fonte principal da aplicação. Este diretório é parte de uma estrutura de pastas convencional, onde main geralmente contém os pacotes e classes que implementam a lógica principal do programa.

Em projetos KMP, essa tradição foi levada adiante e se utiliza o main como sufixo para declarar nossos source sets: commonMain, androidMain, nativeMain, desktopMain, etc.

3: Código compartilhado usando o commonMain

O código compartilhado geralmente reside em um source set chamado commonMain. Não é comum, mas alguns projetos também adotam a nomenclatura sharedMain.

4: Utilizando os "Source set conventions"

Como aprendemos, o próprio KMP foi se ajustando ao redor dessas definições da comunidade.

A partir do Kotlin 1.9.20, o plugin Gradle do KMP oferece um modelo de hierarquia padrão, que contém source sets intermediários predefinidos para casos de uso comuns. Esse modelo é automaticamente configurado com base nos alvos especificados no projeto.

Dentro do KPM Gradle Plugin, temos uma classe chamada 🔗 KotlinMultiplatformSourceSetConventions que reduz e muito a tarefa tediosa de definir e controlar os source sets:

Source Set Plataforma
androidMain Android
androidNativeMain Android
androidNativeTest Android
appleMain Apple
appleTest Apple
commonMain Comum
commonTest Comum
iosMain iOS
iosTest iOS
jsMain JavaScript
jsTest JavaScript
jvmMain JVM
jvmTest JVM
linuxMain Linux
linuxTest Linux
macosMain macOS
macosTest macOS
mingwMain Windows
mingwTest Windows
nativeMain Nativo
nativeTest Nativo
tvosMain tvOS
tvosTest tvOS
wasmJsMain WebAssembly
wasmJsTest WebAssembly
wasmWasiMain WebAssembly
wasmWasiTest WebAssembly
watchosMain watchOS
watchosTest watchOS

Conclusão

Neste artigo, exploramos o conceito vital de source sets no KMP, desvendando como eles facilitam a organização do código, a declaração de dependências específicas para cada plataforma e a configuração de opções de compilação de forma isolada. Compreendemos a distinção entre source sets comuns e específicos, a importância dos source sets intermediários, e como gerenciar eficientemente as dependências para manter a modularidade e eficiência do código.

A flexibilidade e o poder do KMP nos permitem criar aplicações robustas e eficientes, maximizando a reutilização do código e mantendo a consistência em todas as plataformas. A adoção das convenções da comunidade e a compreensão profunda da estrutura de source sets são essenciais para qualquer dev que busca aproveitar ao máximo o potencial do Kotlin Multiplataforma.

No nosso próximo artigo, mergulharemos no Plugin do KMP para Gradle, explorando como ele nos ajuda a configurar e gerenciar nossos projetos multiplataforma de maneira eficiente.

Até a próxima!


🤖 Artigo foi escrito com o auxílio do ChatGPT 4, utilizando o plugin Web.

As fontes e o conteúdo são revisados para garantir a relevância das informações fornecidas, assim como as fontes utilizadas em cada prompt.

No entanto, caso encontre alguma informação incorreta ou acredite que algum crédito está faltando, por favor, entre em contato!


Referencias

Top comments (0)