Migrando dependências para o Version Catalog em projetos Android
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.
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:
- 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);
- 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;
- Catálogos podem declarar pacotes de dependência (bundles), que são “grupos de dependências” comumente usados em conjunto;
- 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.