Migrando dependências para o Version Catalog em projetos Android

Bernardoslailati
#LocalizaLabs
Published in
5 min readNov 21, 2023

--

Em projetos Android, dependências de bibliotecas e plugins desempenham papéis essenciais, simplificando o desenvolvimento ao realizar tarefas específicas como adicionar funcionalidades ou comportamentos ao ambiente de desenvolvimento e oferecer soluções prontas para problemas conhecidos, poupando nosso precioso tempo de programação em diversas situações.

Durante a evolução do projeto, comumente nos deparamos com o aumento significativo dessas dependências, que passam a ser então obrigatórias para a construção de nossas funcionalidades.

E visto que adicionar dependências implica no aumento de linhas de código, se não tomarmos cuidado, teremos também mais desorganização

Escolher um padrão moderno e organizado de gerenciamento dessas dependências, poderá lhe garantir, no futuro, benefícios como a diminuição do esforço gasto em manutenção e facilidade ao se adicionar novas dependências ou atualizar versões já existentes.

E o Gradle Version Catalog se mostra como uma ótima solução para esse cenário!

O que é o Gradle Version Catalog e como utilizá-lo?

Segundo a própria documentação oficial:

Um catálogo de versão é uma lista de dependências, representada através de coordenadas de dependência, que um usuário poderá escolher ao declará-las em um script de build.

Simples assim.

Como exemplo, a seguir vemos a importação de uma dependência convencional em arquivos build.gradle:

# build.gradle.kts - app
plugin {
id("com.google.dagger.hilt.android")
}

dependencies {
implementation("androidx.core:core-ktx:1.12.0")
}

Sendo convertida, ao utilizar o Version Catalog, no código abaixo:

# build.gradle.kts - app
plugin {
alias(libs.plugins.dagger.hilt)
}

dependencies {
implementation(libs.core.ktx)
}

Neste exemplo, libs apontará para o caminho do catálogo de versões do projeto e core.ktx e dagger.hilt realizam o vínculo com as dependências de uma biblioteca e um plugin, respectivamente, declaradas neste catálogo em específico.

Além da API de gerenciamento e configurações, o Gradle oferece também um arquivo padrão de catálogo, para começar o quanto antes. Ao criar o arquivo TOML nomeado libs.versions.toml na pasta gradle de compilação do projeto, um catálogo de versões será criado (versão mínima do Gradle exigida 7.0) e já disponibilizado para uso.

Exemplo de criação do arquivo libs.versions.toml em um projeto Android.

O arquivo TOML irá contemplar quatro seções principais:

  • [versions] usada para declarar as versões que poderão ser referenciadas nas dependências;
  • [libraries] utilizada para definir os atalhos de acesso às dependências de bibliotecas;
  • [plugins] usada para declarar os plugins vinculados ao projeto;
  • [bundles] criada para abranger as declarações de conjuntos de dependências.

Para se adicionar uma nova dependência de biblioteca nesse arquivo, basta seguir qualquer um dos padrões abaixo:

[versions]
common = "1.4"

[libraries]
my-lib = "com.mycompany:mylib:1.4"
my-other-lib = { module = "com.mycompany:other", version = "common" }
my-other-lib2 = { group = "com.mycompany", name = "alternate", version = "common" }
mylib-full-format = { group = "com.mycompany", name = "alternate", version = { require = "1.4" } }

[plugins]
short-notation = "some.plugin.id:1.4"
long-notation = { id = "some.plugin.id", version = "1.4" }
reference-notation = { id = "some.plugin.id", version.ref = "common" }

E como exemplo completo desse arquivo preenchido, já em um projeto Android, temos:

[versions]
agp = "8.3.0-alpha01"
kotlin = "1.8.10"
google-services = "4.4.0"
firebase-bom = "32.5.0"
compose-bom = "2023.10.01"
gson = "2.10.1"
junit = "4.13.2"

[libraries]
firebase-bom = { group = "com.google.firebase", name = "firebase-bom", version.ref = "firebase-bom" }
firebase-analytics = { group = "com.google.firebase", name = "firebase-analytics" }
firebase-auth = { group = "com.google.firebase", name = "firebase-auth-ktx" }
firebase-storage = { group = "com.google.firebase", name = "firebase-storage-ktx" }
firebase-crashlytics = { group = "com.google.firebase", name = "firebase-crashlytics-ktx" }
firebase-config = { group = "com.google.firebase", name = "firebase-config-ktx" }
firebase-database-ktx = { group = "com.google.firebase", name = "firebase-database-ktx", version.ref = "firebase-database-ktx" }

compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "compose-bom" }
compose-ui = { group = "androidx.compose.ui", name = "ui" }
compose-ui-util = { group = "androidx.compose.ui", name = "ui-util" }
compose-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" }

gson = { module = "com.google.code.gson:gson", version.ref = "gson" }

junit = { group = "junit", name = "junit", version.ref = "junit" }

[bundles]
compose = ["compose-ui", "compose-ui-util", "compose-ui-graphics"]
firebase = [
"firebase-analytics",
"firebase-auth",
"firebase-storage",
"firebase-crashlytics",
"firebase-config",
"firebase-database-ktx"
]

[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
google-services = { id = "com.google.gms.google-services", version.ref = "google-services" }
versions = { id = "com.github.ben-manes.versions", version = "0.45.0" }

O consumo de TODAS essas dependências em um arquivo build.gradle.kts, seria então:

# build.gradle.kts - app

plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.google.services)
...
}

...

dependencies {
implementation(platform(libs.firebase.bom))
implementation(libs.bundles.firebase)

implementation(platform(libs.compose.bom))
implementation(libs.bundles.compose)

implementation(libs.gson)

testImplementation(libs.junit)
...
}

Bem simples e organizado, não é mesmo?!

Principais Vantagens

O catálogo de versões oferece várias vantagens em relação à declaração das dependências diretamente nos scripts de build, sendo algumas delas:

  1. Para cada catálogo, o Gradle gera caminhos de acesso de tipo seguro, para que você possa adicionar facilmente dependências com preenchimento automático na IDE Android Studio (a partir da versão Flamingo);
  2. Cada catálogo é visível para todos os projetos de uma compilação. É um local central para se declarar uma versão de dependência e garantir que a alteração nessa versão será refletida em todos os subprojetos;
  3. Catálogos podem declarar pacotes de dependência (bundles), que são “grupos de dependências” comumente usados em con​​junto;
  4. Os catálogos podem separar o grupo e o nome de uma dependência de sua versão real e, em vez disso, usar referências de versão, tornando possível compartilhar uma declaração de versão entre várias dependências.

Adicionar uma dependência usando a notação libs.algumaLibQualquer funciona exatamente como se você tivesse codificado o grupo, o artefato e a versão diretamente em seus arquivos de build.

Conclusão

Um catálogo de versões lhe ajudará a centralizar suas dependências e será apenas, como o próprio nome diz, um inventário completo entregue a seu projeto. Oficialmente, o Gradle recomenda sua utilização para que sejam gerados acessadores de tipagem segura para as dependências, introduzidas notações abreviadas e realizado o compartilhamento de suas coordenadas entre outros diferentes projetos. A utilização de um catálogo não terá qualquer tipo de consequência para os usuários e implicará exclusivamente no aumento da robustez, escalabilidade, organização e segurança do processo de gerenciamento de suas dependências.

--

--