# Coil 3 ImageLoader with Hilt + Disk Cache

Complete pattern for wiring Coil 3.0.4+ into a Hilt Android project with disk caching and dedicated OkHttpClient.

## Build dependency

```kotlin
// build.gradle.kts
implementation("io.coil-kt.coil3:coil-compose:3.0.4")
implementation("io.coil-kt.coil3:coil-network-okhttp:3.0.4")
```

## Hilt Module

```kotlin
package com.example.app.data

import android.app.Application
import coil3.ImageLoader
import coil3.disk.DiskCache
import coil3.network.okhttp.OkHttpNetworkFetcherFactory
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import okhttp3.OkHttpClient
import okio.Path.Companion.toPath
import java.util.concurrent.TimeUnit
import javax.inject.Singleton

@Module
@InstallIn(SingletonComponent::class)
object CoilModule {

    private const val IMAGE_CONNECT_TIMEOUT = 15L
    private const val IMAGE_READ_TIMEOUT = 30L

    @Provides
    @Singleton
    fun provideImageOkHttpClient(): OkHttpClient {
        return OkHttpClient.Builder()
            .connectTimeout(IMAGE_CONNECT_TIMEOUT, TimeUnit.SECONDS)
            .readTimeout(IMAGE_READ_TIMEOUT, TimeUnit.SECONDS)
            .build()
    }

    @Provides
    @Singleton
    fun provideImageLoader(
        context: Application,
        okHttpClient: OkHttpClient,
    ): ImageLoader {
        return ImageLoader.Builder(context)
            .diskCache {
                DiskCache.Builder()
                    .directory(
                        context.cacheDir.resolve("image_cache")
                            .also { it.mkdirs() }
                            .absolutePath
                            .toPath()
                    )
                    .maxSizePercent(0.02)
                    .build()
            }
            .components {
                add(OkHttpNetworkFetcherFactory(okHttpClient))
            }
            .build()
    }
}
```

## Application class

```kotlin
package com.example.app

import android.app.Application
import coil3.ImageLoader
import coil3.SingletonImageLoader
import dagger.hilt.android.HiltAndroidApp
import javax.inject.Inject

@HiltAndroidApp
class MyApplication : Application() {

    @Inject
    lateinit var imageLoader: ImageLoader

    override fun onCreate() {
        super.onCreate()
        SingletonImageLoader.setSafe { imageLoader }
    }
}
```

## Key decisions

- **Separate OkHttpClient for images**: Don't reuse the TMDB rate-limited OkHttpClient for image loading. TMDB's image CDN (`image.tmdb.org`) is a separate service that doesn't count against the API rate limit (40 req/10s). Using the API client would cause poster loads to stall whenever API calls consume the budget.
- **`maxSizePercent(0.02)`**: 2% of the internal storage partition. On a 16GB Firestick this is ~130MB — enough for thousands of poster thumbnails without risking storage pressure.
- **`SingletonImageLoader.setSafe`**: Required by Coil 3 to register the global ImageLoader. Called in `Application.onCreate()` after Hilt injection.
- **No rate limiter, no logging interceptor**: Image requests are best-effort; rate limiting adds backpressure that slows UI renders. Logging image URLs leaks tokens (poster URLs contain API keys in some addon setups).
