Blog Infos
Author
Published
Topics
Published
Topics

As projects start to be more complex and modularized, keeping all the dependencies in sync and updating them becomes a hard and annoying task. To solve this, we have a few alternatives and a new one was recently launched by Gradle to make our lives easier.

Gradle introduced in version 7.0 a new feature called Version Catalog. It represents a list of type-safe dependencies to be used across the projects. It is also very flexible and takes advantage of what Gradle currently has to offer. In this article, we will take a look at why using, how to apply it in an Android project and some points that are important to know before diving in.

Why Version Catalog?

The Version Catalog is a very flexible solution that takes advantage of existing Gradle features and allows us to do even more than the alternatives. One example is the ability to create bundles which allow adding a single implementation() line with a set of libraries in our Gradle File.

Version Catalog files are shareable, so it’s even easier to have a standard configuration not only inside it but across multiple projects. The flexibility also supports third-party plugins to automatically update the versions for the latest ones, if we want.

Also, in addition to composite builds, it performs better in comparison with the buildSrc solution. For instance with buildSrc, when we increment a version number the build is cleaned and needs to be rebuilt. This is not the case for Version Catalog. For more details about performance, please take a look at this great article by

. For more insights, this is a great Twitter thread to follow.

Basic configuration

In order to configure the Version Catalog in our project, a set of simple steps are required.

1. Create the libs.versions.toml file

The libs.versions.toml file is the file that contains all the dependency definitions, such as versionslibrariesbundles and plugins definitions.

[versions]
# Define the dependency versions
kotlin = "1.7.10"
compose = "1.2.1"
[libraries]
# Define the libraries
compose_ui = { module = "androidx.compose.ui:ui", version.ref = "compose" }
compose_material = { module = "androidx.compose.material:material", version.ref = "compose" }
compose_tooling = { module = "androidx.compose.ui:ui-tooling", version.ref = "compose" }
compose_icons = { module = "androidx.compose.material:material-icons-extended", version.ref = "compose" }
[bundles]
# Define bundles/groups of libraries
compose = ["compose.ui", "compose.material", "compose.tooling", "compose.icons"]
[plugins]
kotlin = { id = "org.jetbrains.kotlin.android", version = "kotlin" }

It’s worth mentioning that all the dependency alias are normalized by Gradle. It means that every alias that has -_ or. will be updated to use .instead. For instance the alias compose_uicompose-ui or compose.ui will be normalized as compose.ui.

Keep in mind that it’s also possible to define the Version Catalogs in the settings.gradle(.kts), however for clarity we are showing only with the TOML file. For more information about this and other ways to define your dependencies, please take a look at the official docs.

2. Create a dedicated module for plugins

Since we are using composite builds with Version Catalog, we need a dedicated module. The module does not need a specific name, but just make sure that it won’t conflict with the existing ones in your project. In our example, let’s call it plugins.

This module needs to contain at least two files: a build.gradle(.kts) and settings.gradle(.kts). The first one is used for regular Gradle configuration and the other is used to link up our libs.versions.toml file with the Version Catalog feature.

plugins {
`kotlin-dsl`
}
repositories {
mavenCentral()
google()
}
dependencyResolutionManagement {
repositories {
mavenCentral()
}
versionCatalogs {
create("libs") {
from(files("../gradle/libs.versions.toml"))
}
}
}
3. Setup Composite Build

Now that we have our Version Catalog set, we need to set up the composite build to link this configuration with our project. In order to achieve that, we need to open our project’s settings.gradle file and include a few lines:

pluginManagement {
includeBuild("plugins")
repositories {
gradlePluginPortal()
google()
mavenCentral()
}
}

The code above sets the plugin management to not only look for dependencies in the remote repositories but also search for them in our plugins module.

If your project is using a Gradle version below 7.2, you will need to add a new feature preview activation on the settings.gradle file.

enableFeaturePreview("VERSION_CATALOGS")
4. Use the Version Catalog

Finally, after this setup, we are able to use the dependencies in our Gradle files.

dependencies {
// Adds a single dependency
implementation libs.kotlin
// Add a group of dependencies
implementation libs.bundles.compose
// Tests dependencies works in the same way
testImplementation libs.junit
androidTestImplementation libs.espresso.core
}
Good to know

In addition to the points already mentioned about Version Catalog, there are a couple of things that are good to know.

Automatically update versions

One of the advantages of the Version Catalog is the ability to use tools to automatically update them. For projects on GitHub, RenovateBot is a great tool to integrate into your pipeline. This bot reads your libs.versions.toml and automatically creates Pull Requests to update your dependencies.

My pipeline has a set of unit and instrumented tests, so I’m confident that the new version is compatible and I just click the merge button. If the tests fail, I just update the PR with the API changes and it’s good to go.

Pull request to update the Coroutines version

Job Offers

Job Offers


    Sr. Software Development Engineer, Last Mile Driver Assistance Technology

    Amazon
    Berlin
    • Full Time
    apply now

    Kotlin Multiplatform Mobile Developer

    Touchlab
    Remote
    • Full Time
    apply now

OUR VIDEO RECOMMENDATION

,

Hype Driven Development: How I learned to stop worrying and love the failures

Ever wondered what it would take to combine all the experimental features of a language you love (Spoilers… It’s Kotlin!) into one big bundle? I’m a lover of new and shiny stuff, so let me…
Watch Video

Hype Driven Development: How I learned to stop worrying and love the failures

Ash Davies
Senior Android Developer
@ Snapp Mobile GmbH

Hype Driven Development: How I learned to stop worrying and love the failures

Ash Davies
Senior Android Devel ...
@ Snapp Mobile GmbH

Hype Driven Development: How I learned to stop worrying and love the failures

Ash Davies
Senior Android Developer
@ Snapp Mobile GmbH

Jobs

However, if your project does not support that plugin, a good alternative is the Version Catalog Update Plugin to directly apply to the project’s Gradle file. Both plugins are widely configurable and help us keep the project dependencies fresh.

No support for precompiled script plugins

One of the points of attention from Version Catalog is that the dependencies and versions are not visible in precompiled script plugins. In other words, the dependencies declared on libs.versions.toml will be visible on the build.gradle from all your modules, but not for custom/precompiled ones.

Let’s say that we have dozens of library modules on our project and want all of them to have the same basic dependencies. We could create a library_dependencies.gradle containing them all and just apply it to the appropriate modules.

plugins {
id("com.android.library")
}
dependencies {
// Won't be able to find this library
implementation(libs.android.appcompat)
}

Unfortunately, this new Gradle file won’t be able to find the appropriate dependency due to some limitations. One way to workaround this issue is to create an extension function to manually expose the dependency.

internal val VersionCatalog.logcat: Provider<MinimalExternalModuleDependency>
get() = getLibrary("logcat")
private fun VersionCatalog.getLibrary(library: String) = findLibrary(library).get()

Now instead of trying to directly access the Version Catalog, we manually access the libs reference and the implementation function will access our extension function and work properly.

plugins {
id("com.android.library")
}
// Manually get the reference for "libs"
val libs: VersionCatalog =
extensions.getByType<VersionCatalogsExtension>().named("libs")
dependencies {
// Use the extension function to access the dependency
implementation(libs.android.appcompat)
}

One of the drawbacks of this solution is that it’s type unsafe since we are using Strings to find the dependencies rather than generated code. For more information about this behaviour, I highly recommend this GitHub Issue with an extensive discussion about this topic.

Conclusion

The Version Catalog is a great new way to handle dependency management on our projects. It makes the maintenance of both libraries and versions easier and also allows all the flexibility that Gradle has to offer. Some Android projects already started migrating and we will start to see more and more code, examples and improvements on this amazing feature.

What’s next?

As usual, I would love to share more complex code to help you in your studies. The first Pull Request it’s on my personal project, Alkaa, which migrates from buildSrc to Version Catalog. It also adds a few JVM Convention Plugins to help avoid duplication, but this is a topic for the next article. 🙂

Also, there are two PRs from amazing projects that I used a lot as examples when I was doing the migration. The first one is ShoppingApp by

. A huge thanks to both for their amazing contributions! ❤️

The code used in this article is available on GitHub, where you can see step by step each part of the process:

The official Gradle documentation also has great examples and insights on how to improve dependency management on our projects. For more deep and informal content, this article from Cédric Champeau is great. And last but not least, the official Now In Android by Google also uses the Version Catalog.

Thanks a lot for reading my article! ❤️

Thanks to Bruno Kenji Tiba

This article was originally published on proandroiddev.com on August 25, 2022

YOU MAY BE INTERESTED IN

YOU MAY BE INTERESTED IN

blog
Compose is part of the Jetpack Library released by Android last spring. Create Android…
READ MORE
blog
The reason for writing this article is that Text composable function does not support…
READ MORE
blog
Screenshot tests are the most effective way how to test your View layer. And…
READ MORE
blog
An Android Splash Screen is the first screen that is visible to the user…
READ MORE

Leave a Reply

Your email address will not be published.

Fill out this field
Fill out this field
Please enter a valid email address.

Menu