🚀 Stop Wiring Your Android App Manually! Use Hilt + KSP Like a Pro Simplifying Android Dependency Injection (DI) with Hilt & KSP
In today’s Android development world, we expect our apps to be sleek, scalable, and maintainable. But here's the deal — wiring everything manually feels like building IKEA furniture without a manual (and half the screws missing). That’s where Dependency Injection (DI) steps in — and even better, Hilt + KSP takes that DI to a whole new level.
Let’s build smarter Android apps, not harder. And to make it real, we’ll imagine a modern Dictionary App built with Jetpack Compose, powered by Dagger Hilt (for DI) and KSP (for performance).
🧩 What Is Dependency Injection, Really?
Let’s say you’re running a coffee shop. Every time a customer asks for coffee, you go out to buy beans, milk, sugar, and a cup. Sounds exhausting, right?
Now imagine you’ve got a setup: your ingredients are stocked, and when someone places an order, your system just knows what to do.
That’s Dependency Injection — it preps and delivers your dependencies (objects like Retrofit, ViewModel, Repository) so your app can focus on doing what it does best.
💉 What’s Hilt, and Why Should You Care?
Hilt is Google’s recommended tool for managing DI in Android apps. It builds on top of Dagger (a powerful but complex tool) and simplifies your life — no manual dependency graphs, no chaos.
Hilt takes care of:
When and how to create instances of your classes
Scoping those instances to lifecycles (Activity, ViewModel, etc.)
Making your architecture cleaner, safer, and easier to test
✅ Why Hilt Rocks:
- Zero boilerplate setup
- Jetpack Compose-friendly
- Built-in support for Android classes
- Compile-time safety
- Google-maintained and production-ready
⚡ Why Replace KAPT with KSP?
KAPT is like a hardworking but slow assistant who takes too many coffee breaks.
KSP (Kotlin Symbol Processing), on the other hand, is the young genius — faster, cleaner, and built specifically for Kotlin.
KSP > KAPT Because:
- 🚀 2x faster builds (seriously)
- 🧠Less IDE lag and random Gradle crashes
- 💯 Better error messages
- 🛠️ Built for modern Kotlin projects
If you're still using KAPT, you're missing out on smoother development.
More about👉👉👉 Jetpack Compose Libraries?
How to bootstrap an existing app to use Hilt and KSP?
📱 Our Sample App: Dictionary with Jetpack Compose
We’re building a Jetpack Compose Dictionary App that uses:
- 🔎 Word search
- 📘 Definitions, examples, parts of speech
- 🧠Offline cache using Room
- ⭐ Bookmark favorites
- 🗣️ Text-to-Speech
- 🌙 Dark mode support
- ✅ Dependency Injection via Hilt
- ⚡ Optimized with KSP
Now let’s see how we plug in Hilt + KSP to power it.
🛠Step-by-Step: Setting Up Hilt with KSP
1️⃣ Add Plugins to build.gradle (Project-level)
id ("com.google.dagger.hilt.android") version "2.56.2" apply false
id("com.google.devtools.ksp") version "2.0.21-1.0.27" apply false
2️⃣ In build.gradle (App-level):
// 1️⃣ Apply Hilt plugin first
id("com.google.dagger.hilt.android")
// 2️⃣ Then apply KSPid("com.google.devtools.ksp")implementation ("androidx.compose.material:material-icons-extended:1.7.8")
// Room Database (Using KSP)
implementation("androidx.room:room-runtime:2.7.1")
implementation("androidx.room:room-ktx:2.7.1")
implementation("androidx.navigation:navigation-compose:2.9.0")
// Hilt Dependency Injection
implementation("com.google.dagger:hilt-android:2.56.2")
ksp("com.google.dagger:hilt-android-compiler:2.56.2")
implementation ("androidx.hilt:hilt-navigation-compose:1.2.0")
// Coil (Image)
implementation("io.coil-kt:coil-compose:2.6.0")
// Retrofit + Gson
implementation("com.squareup.retrofit2:retrofit:2.9.0")
implementation("com.squareup.retrofit2:converter-gson:2.9.0")
🧠No kapt needed — we use ksp for blazing-fast builds!
3️⃣ Create Your Hilt App Class
@HiltAndroidApp
class DictionaryApp : Application()
This tells Hilt to bootstrap itself when your app starts.
4️⃣ Annotate Your Entry Points
@AndroidEntryPoint
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
DictionaryAppTheme {
/* Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
Greeting(
name = "Android",
modifier = Modifier.padding(innerPadding)
)
}*/
DictionaryScreen()
}
}
}
}
Use @AndroidEntryPoint on any Activity, Fragment, or Service that needs injected dependencies.
5️⃣ Inject into ViewModel
@HiltViewModel
class DictionaryViewModel @Inject constructor(
private val repository: DictionaryRepository
) : ViewModel() {
var meanings = mutableStateOf<List<Definition>>(emptyList())
var isLoading = mutableStateOf(false)
var error = mutableStateOf<String?>(null)
fun search(word: String) {
viewModelScope.launch {
isLoading.value = true
error.value = null
try {
meanings.value = repository.getMeaning(word)
} catch (e: Exception) {
error.value = e.localizedMessage ?: "An error occurred"
} finally {
isLoading.value = false
}
}
}
}
6️⃣ Inject into Repository
class DictionaryRepository @Inject constructor(
private val api: DictionaryApi
) {
suspend fun getMeaning(word: String): List<Definition> =
api.getDefinition(word)
}
7️⃣ Provide Dependencies via Module
@Module
@InstallIn(SingletonComponent::class)
object NetworkModule {
@Provides
@Singleton
fun provideRetrofit(): Retrofit =
Retrofit.Builder()
.baseUrl("https://api.dictionaryapi.dev/")
.addConverterFactory(GsonConverterFactory.create())
.build()
@Provides
@Singleton
fun provideDictionaryApi(retrofit: Retrofit): DictionaryApi =
retrofit.create(DictionaryApi::class.java)
}
// === NetworkModule.kt ===
interface DictionaryApi {
@GET("api/v2/entries/en/{word}")
suspend fun getDefinition(@Path("word") word: String): List<Definition>
}
8. DictionaryScreen
package com.codingbihar.dictionaryapp
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.systemBarsPadding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.Button
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
@Composable
fun DictionaryScreen(viewModel: DictionaryViewModel = hiltViewModel()) {
var word by remember { mutableStateOf("") }
Column(modifier = Modifier.padding(16.dp).systemBarsPadding()) {
OutlinedTextField(
value = word,
onValueChange = { word = it },
label = { Text("Enter word") },
modifier = Modifier.fillMaxWidth()
)
Spacer(modifier = Modifier.height(8.dp))
Button(onClick = { viewModel.search(word) }) {
Text("Search")
}
Spacer(modifier = Modifier.height(16.dp))
when {
viewModel.isLoading.value -> CircularProgressIndicator()
viewModel.error.value != null -> Text("Error: ${viewModel.error.value}")
else -> LazyColumn {
items(viewModel.meanings.value) { definition ->
MeaningItem(definition)
}
}
}
HorizontalDivider(modifier = Modifier.padding(vertical = 8.dp))
}
}
@Composable
fun MeaningItem(definition: Definition) {
Column(modifier = Modifier.padding(8.dp)) {
Text("Word: ${definition.word}", style = MaterialTheme.typography.headlineSmall)
definition.meanings.forEach { meaning ->
val color = when (meaning.partOfSpeech.lowercase()) {
"noun" -> Color(0xFFE3F2FD)
"verb" -> Color(0xFFE8F5E9)
"adjective" -> Color(0xFFFFF3E0)
else -> Color(0xFFF3E5F5)
}
Column(
modifier = Modifier
.fillMaxWidth()
.background(color)
.padding(8.dp)
) {
Text("Part of Speech: ${meaning.partOfSpeech}", color = Color.Red ,style = MaterialTheme.typography.titleMedium)
meaning.definitions.forEach { def ->
Text("• ${def.definition}", style = MaterialTheme.typography.bodyMedium)
def.example?.let {
Text(" Example: $it", style = MaterialTheme.typography.bodySmall)
}
}
}
}
}
}