Migrate your Android Build from Groovy to Version Catalog.

Harny Otuoniyo
Mobimeo Technology
Published in
5 min readAug 11, 2023

Android Gradle build system is a very essential part of the android development process and understanding how to optimize your build for better performance cannot be over emphasized.

The Gradle build has evolved in recent times with version catalogs but let’s first go back in history to understand certain limitations with past implementations of this key component.

let’s get started shall we?

In the ancient age, the first implementation which came with your android studio setup was written in groovy.

Let’s zoom in to find out the benefits and limitations with this Implementation.

  • Easy to setup: by default it was configured in your android studio so it saves you the hassle of any extra layers of setting up or configuring it
  • Tons of examples: a lot of resources for its implementation

Limitations

  • No type safety
  • Manual version management
  • Ext block usage : sometimes could be messy and hard to read and maintain.

screenshots of build.gradle written in groovy

project build.gradle file
app build.gradle file
dependency code block in app build.gradle file

Then we had a new kid in town which came to help fix some certain limitations of the groovy implementation.

buildSrc

buildSrc came with great promises and pretty swift implementations,

  • It solved the problem of type safety because it’s implementation is in Kotlin making things more homogenous to read and manage
  • There is now IDE support of code completion and easy navigation using cmd+b or cmd+click (for Mac) and ctrl+b or ctrl+click (for Windows)
  • Easy management of dependency in one place which is very useful for multi modular project setups.

But sadly it has some limitations also

  • Setup is not trivial: a lot of times you’d have to memorize the steps to setup or look for articles to memorize the configuration.
  • Loss of cache: whenever you want to update a dependency version, you lose the cache of that module and it builds from afresh again and it could take ages to build especially for multi modular huge codebases.
  • Structure of buildSrc is very bloated and not easy to migrate.
buildSrc structure

With all of the limitations listed above, you wonder if there is a way to just make life easy and some form of compromise to work with right?

Now let me introduce you to Version Catalog

Version catalog has the following benefits with its shiny implementation:

  • it retains type safety
  • No cache loss when you update versions
  • Pretty straight forward setup
  • 1 line implementations via bundle (I’ll go deeper into this when I show the setup)
  • Standards for implementations with libraries, plugins and versions
  • Auto dependency updates

But sadly it still has some few limitations which are worth mentioning and hopefully would be fixed in the newer versions of Android studio

Limitations

  • IDE support isn’t perfect: though there are linters to show the implementation is correct with a yellow color, there is still a lot of room for improvement with the IDE support and convenient ways for it’s implementation.

How do we implement this shiny new component (Version Catalog)

Enough of the explanations, let’s get our hands dirty.

Step 1:

Create a file in your root Gradle folder and name it lib.versions.toml

This file is used to declare the standards for versions, plugins, libraries and bundles.

lib.versions.toml file

Step 2:

  • Go to your settings.gradle and rename file to settings.gradle.kts
  • Open the file and make some few changes:

Change the line

include ':app'

to

include (":app")

next, you need to add this block in the top of the file(just incase it isn’t there already):

pluginManagement {
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
}

This block is necessary for your project build.gradle file to migrate from the old buildscript dependencies block to plugins block

  • Next you need to enable version catalog in the settings.gradle.kts file by adding this block:

Within dependencyResolutionManagement, add this block:

repositories {...}
versionCatalogs {
create("libs") {
from(files("gradle/lib.versions.toml"))
}
}

Your settings.gradle.kts file should look like this:

settings.gradle.kts file

If you are using gradle 7.0 or older then you need to specify

enableFeaturePreview("VERSION_CATALOGS")

On the top of the version catalog block like this

enableFeaturePreview("VERSION_CATALOGS")
versionCatalogs {
create("libs") {
from(files("gradle/lib.versions.toml"))
}
}

Also ensure that the path for lib.versions.toml is the correct path in this line from(files(“gradle/lib.versions.toml”)) else you run into some errors. 😬

Step 3:

  • Go to your project build.gradle file and make the following changes:
Medieval version [project build.gradle file]
New version [project build.gradle file]

We are including the plugins block here for the necessary plugins we need in our Project.

Project build.gradle file

alias is a way to add plugin dependency using notation coming from version catalog.

Step 4:

go to your app build.gradle and refactor to build.gradle.kts

Change all single quotes to double quotes

Change all id notation to alias as shown below:

from this:

plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
id 'kotlin-kapt'
id 'dagger.hilt.android.plugin'
}

to this:

plugins {
alias(libs.plugins.com.android.application)
alias(libs.plugins.org.jetbrains.kotlin.android)
alias(libs.plugins.com.google.dagger.hilt.android)
kotlin("kapt")
}

The full changes is shown below:

Medieval Version
New Version

For the dependencies block

This is an example of calling the dependency from version catalog

implementation(libs.hilt)

Now lets talk about bundles:

Using bundles has the perks of grouping your dependencies in version catalog and just use one line to reference them in your app build.gradle.kts file.

For example:

In your lib.versions.toml file ->

...
[bundles]
androidx = ["core-ktx", "lifecycle-runtime-ktx", "activity-compose", "navigation", "compose-bom", "material3"]

“core-ktx” is simply a library that has been defined in the file, please check the lib.versions.toml file above for further clarifications

And then you can reference all this libraries with just one line in your app build.gradle.kts file->

implementation(libs.bundles.androidx)

This makes things easy and compact to manage.

Some little things not mentioned which may be useful in your migrations are:

  • For multi modular setups you only need to call a plugin like hilt in the app module build.gradle.kts and not necessarily in every library module.
  • For dependencies without versions e.g “com.android.library”, you cannot use alias annotation to reference this dependency, but it can be referenced using the id notation like this:
id(libs.plugins.android.library.get().pluginId)

And now your migration is complete, thanks for coming to my ted talk 😁

--

--