{"id":"cf0ca6b2-3aac-4fae-8841-7774cc17cd6f","shortId":"uFW9DP","kind":"skill","title":"hilt-di","tagline":"27 Android skills for AI agents (Claude Code, Codex, Cursor). Fixes Supabase auth, Hilt errors, design inconsistency, kapt→ksp, missing UiState states. Reduced my token bills 5×. FitGenZ AI shipped in 18 days.","description":"# Hilt Dependency Injection\r\n\r\n10 rules that fix what AI agents consistently get wrong with Hilt.\r\n\r\n## Setup — required before any injection works\r\n\r\n```kotlin\r\n// MyApplication.kt — REQUIRED, must be declared in AndroidManifest\r\n@HiltAndroidApp\r\nclass MyApplication : Application()\r\n\r\n// AndroidManifest.xml\r\n<application android:name=\".MyApplication\" ...>\r\n```\r\n\r\n```kotlin\r\n// build.gradle.kts — use KSP, never kapt\r\nplugins {\r\n    alias(libs.plugins.ksp)\r\n    alias(libs.plugins.hilt)\r\n}\r\ndependencies {\r\n    implementation(libs.hilt.android)\r\n    ksp(libs.hilt.compiler)                          // ← ksp, not kapt\r\n    implementation(libs.androidx.hilt.navigation.compose)\r\n}\r\n```\r\n\r\n## Rule 1: @Binds over @Provides for interface binding\r\n\r\n```kotlin\r\n// ✅ @Binds — zero runtime overhead, compiler-verified\r\n@Module\r\n@InstallIn(SingletonComponent::class)\r\nabstract class RepositoryModule {\r\n    @Binds\r\n    @Singleton\r\n    abstract fun bindItemRepository(impl: ItemRepositoryImpl): ItemRepository\r\n\r\n    @Binds\r\n    @Singleton\r\n    abstract fun bindUserRepository(impl: UserRepositoryImpl): UserRepository\r\n}\r\n\r\n// ❌ @Provides for interface — works but is less efficient\r\n@Module\r\n@InstallIn(SingletonComponent::class)\r\nobject RepositoryModule {\r\n    @Provides\r\n    @Singleton\r\n    fun provideItemRepository(impl: ItemRepositoryImpl): ItemRepository = impl\r\n}\r\n```\r\n\r\n## Rule 2: @Provides for third-party / constructed objects\r\n\r\n```kotlin\r\n// ✅ @Provides when you control construction\r\n@Module\r\n@InstallIn(SingletonComponent::class)\r\nobject NetworkModule {\r\n    @Provides\r\n    @Singleton\r\n    fun provideOkHttpClient(): OkHttpClient =\r\n        OkHttpClient.Builder()\r\n            .addInterceptor(HttpLoggingInterceptor().apply { level = Level.BODY })\r\n            .connectTimeout(30, TimeUnit.SECONDS)\r\n            .build()\r\n\r\n    @Provides\r\n    @Singleton\r\n    fun provideRetrofit(okHttpClient: OkHttpClient): Retrofit =\r\n        Retrofit.Builder()\r\n            .baseUrl(BuildConfig.BASE_URL)\r\n            .client(okHttpClient)\r\n            .addConverterFactory(Json.asConverterFactory(\"application/json\".toMediaType()))\r\n            .build()\r\n\r\n    @Provides\r\n    @Singleton\r\n    fun provideItemApiService(retrofit: Retrofit): ItemApiService =\r\n        retrofit.create(ItemApiService::class.java)\r\n}\r\n```\r\n\r\n## Rule 3: Scope correctly\r\n\r\n```kotlin\r\n// @Singleton — one instance for app lifetime\r\n@Binds @Singleton abstract fun bindRepo(impl: RepoImpl): Repo\r\n\r\n// @ActivityRetainedScoped — survives rotation, dies with activity backstack\r\n// (default for @HiltViewModel — don't re-declare it)\r\n\r\n// @ViewModelScoped — tied to ViewModel lifetime\r\n@Binds @ViewModelScoped abstract fun bindSomeHelper(impl: SomeHelperImpl): SomeHelper\r\n\r\n// @ActivityScoped — tied to Activity lifetime (rare)\r\n@Binds @ActivityScoped abstract fun bindNavigator(impl: NavigatorImpl): Navigator\r\n\r\n// ❌ Injecting @Singleton into @ViewModelScoped — scope violation, Dagger error\r\n```\r\n\r\n## Rule 4: @HiltViewModel — correct pattern\r\n\r\n```kotlin\r\n// ✅ ViewModel injection\r\n@HiltViewModel\r\nclass HomeViewModel @Inject constructor(\r\n    private val getItems: GetItemsUseCase,\r\n    private val savedStateHandle: SavedStateHandle  // free with Hilt\r\n) : ViewModel() { ... }\r\n\r\n// ✅ In Composable — always hiltViewModel(), never viewModel()\r\n@Composable\r\nfun HomeScreen(viewModel: HomeViewModel = hiltViewModel()) { ... }\r\n\r\n// ✅ Scoped to NavGraph entry\r\n@Composable\r\nfun CheckoutFlow(navController: NavController) {\r\n    val parentEntry = remember(navController) {\r\n        navController.getBackStackEntry(CheckoutRoute)\r\n    }\r\n    val viewModel: CheckoutViewModel = hiltViewModel(parentEntry)\r\n}\r\n```\r\n\r\n## Rule 5: @AndroidEntryPoint on all Android classes that inject\r\n\r\n```kotlin\r\n// ✅ Required on: Activity, Fragment, View, Service, BroadcastReceiver\r\n@AndroidEntryPoint\r\nclass MainActivity : ComponentActivity() {\r\n    @Inject lateinit var analyticsTracker: AnalyticsTracker\r\n}\r\n\r\n@AndroidEntryPoint\r\nclass NotificationService : Service() {\r\n    @Inject lateinit var notificationHandler: NotificationHandler\r\n}\r\n\r\n// ❌ Forgetting @AndroidEntryPoint — results in lateinit not initialized crash\r\nclass MainActivity : ComponentActivity() {\r\n    @Inject lateinit var analyticsTracker: AnalyticsTracker  // CRASH: not injected\r\n}\r\n```\r\n\r\n## Rule 6: @EntryPoint for non-Hilt classes\r\n\r\n```kotlin\r\n// ✅ Access Hilt graph from classes Hilt doesn't manage\r\n@EntryPoint\r\n@InstallIn(SingletonComponent::class)\r\ninterface AnalyticsEntryPoint {\r\n    fun analyticsTracker(): AnalyticsTracker\r\n}\r\n\r\n// Usage in a ContentProvider or non-Hilt class\r\nval entryPoint = EntryPointAccessors.fromApplication(\r\n    context.applicationContext,\r\n    AnalyticsEntryPoint::class.java\r\n)\r\nval tracker = entryPoint.analyticsTracker()\r\n```\r\n\r\n## Rule 7: @AssistedInject for runtime parameters\r\n\r\n```kotlin\r\n// ✅ When ViewModel needs a runtime value (e.g., item ID from nav args)\r\n@HiltViewModel(assistedFactory = DetailViewModel.Factory::class)\r\nclass DetailViewModel @AssistedInject constructor(\r\n    @Assisted val itemId: String,\r\n    private val getItem: GetItemUseCase\r\n) : ViewModel() {\r\n\r\n    @AssistedFactory\r\n    interface Factory {\r\n        fun create(itemId: String): DetailViewModel\r\n    }\r\n}\r\n\r\n// In Composable\r\n@Composable\r\nfun DetailScreen(itemId: String) {\r\n    val viewModel: DetailViewModel = hiltViewModel<DetailViewModel, DetailViewModel.Factory>(\r\n        creationCallback = { factory -> factory.create(itemId) }\r\n    )\r\n}\r\n```\r\n\r\n## Rule 8: Qualifier for multiple bindings of same type\r\n\r\n```kotlin\r\n// ✅ @Qualifier to distinguish between two instances of same type\r\n@Qualifier\r\n@Retention(AnnotationRetention.BINARY)\r\nannotation class IoDispatcher\r\n\r\n@Qualifier\r\n@Retention(AnnotationRetention.BINARY)\r\nannotation class MainDispatcher\r\n\r\n@Module\r\n@InstallIn(SingletonComponent::class)\r\nobject DispatchersModule {\r\n    @Provides @IoDispatcher\r\n    fun provideIoDispatcher(): CoroutineDispatcher = Dispatchers.IO\r\n\r\n    @Provides @MainDispatcher\r\n    fun provideMainDispatcher(): CoroutineDispatcher = Dispatchers.Main\r\n}\r\n\r\n// Usage\r\n@HiltViewModel\r\nclass MyViewModel @Inject constructor(\r\n    @IoDispatcher private val ioDispatcher: CoroutineDispatcher\r\n) : ViewModel()\r\n```\r\n\r\n## Rule 9: Testing with Hilt\r\n\r\n```kotlin\r\n// ✅ @HiltAndroidTest for integration tests\r\n@HiltAndroidTest\r\nclass HomeViewModelTest {\r\n    @get:Rule val hiltRule = HiltAndroidRule(this)\r\n\r\n    @Inject lateinit var getItems: GetItemsUseCase\r\n\r\n    @Before\r\n    fun setUp() { hiltRule.inject() }\r\n\r\n    @Test\r\n    fun `loads items successfully`() = runTest {\r\n        val viewModel = HomeViewModel(getItems)\r\n        viewModel.uiState.test {\r\n            assertEquals(HomeUiState.Loading, awaitItem())\r\n            assertTrue(awaitItem() is HomeUiState.Success)\r\n        }\r\n    }\r\n}\r\n\r\n// ✅ Replace production bindings in tests\r\n@TestInstallIn(components = [SingletonComponent::class], replaces = [RepositoryModule::class])\r\n@Module\r\nabstract class FakeRepositoryModule {\r\n    @Binds @Singleton\r\n    abstract fun bindItemRepository(impl: FakeItemRepository): ItemRepository\r\n}\r\n```\r\n\r\n## Rule 10: Common Dagger compile errors and fixes\r\n\r\n| Error | Cause | Fix |\r\n|---|---|---|\r\n| `[Dagger/MissingBinding]` | Missing @Provides / @Binds | Add module with binding |\r\n| `[Dagger/IncompatiblyScopedBindings]` | Singleton injected into narrower scope | Match scopes or remove scope annotation |\r\n| `abstract @Provides` | @Provides in abstract class | Use `object` for @Provides, `abstract class` for @Binds |\r\n| `@Binds methods must have only one parameter` | Wrong @Binds signature | `abstract fun bind(impl: Impl): Interface` |\r\n| `lateinit var not initialized` | Missing @AndroidEntryPoint | Add @AndroidEntryPoint to class |\r\n| `Cannot be provided without @Inject or @Provides` | Interface binding missing | Add @Binds module |\r\n\r\n## Deep-dive references\r\n\r\n- `references/hilt-testing.md` — full test setup with @TestInstallIn and fake modules\r\n- `references/hilt-workmanager.md` — @HiltWorker and WorkManager integration","tags":["hilt","android","agent","skills","piyushverma0","agent-skills","ai-agent","antigravity","claude-code","codex","cursor","gemini-cli"],"capabilities":["skill","source-piyushverma0","skill-hilt-di","topic-agent-skills","topic-ai-agent","topic-android","topic-antigravity","topic-claude-code","topic-codex","topic-cursor","topic-gemini-cli","topic-hilt","topic-jetpack-compose","topic-kotlin","topic-material3"],"categories":["android-agent-skills"],"synonyms":[],"warnings":[],"endpointUrl":"https://skills.sh/piyushverma0/android-agent-skills/hilt-di","protocol":"skill","transport":"skills-sh","auth":{"type":"none","details":{"cli":"npx skills add piyushverma0/android-agent-skills","source_repo":"https://github.com/piyushverma0/android-agent-skills","install_from":"skills.sh"}},"qualityScore":"0.454","qualityRationale":"deterministic score 0.45 from registry signals: · indexed on github topic:agent-skills · 8 github stars · SKILL.md body (8,075 chars)","verified":false,"liveness":"unknown","lastLivenessCheck":null,"agentReviews":{"count":0,"score_avg":null,"cost_usd_avg":null,"success_rate":null,"latency_p50_ms":null,"narrative_summary":null,"summary_updated_at":null},"enrichmentModel":"deterministic:skill-github:v1","enrichmentVersion":1,"enrichedAt":"2026-05-18T19:09:09.949Z","embedding":null,"createdAt":"2026-05-18T13:14:49.599Z","updatedAt":"2026-05-18T19:09:09.949Z","lastSeenAt":"2026-05-18T19:09:09.949Z","tsv":"'1':93 '10':40,636 '18':35 '2':154 '27':4 '3':218 '30':186 '4':288 '5':30,345 '6':399 '7':444 '8':505 '9':566 'abstract':112,117,125,230,259,273,624,629,666,670,676,690 'access':407 'activ':241,268,356 'activityretainedscop':236 'activityscop':265,272 'add':650,702,716 'addconverterfactori':202 'addinterceptor':180 'agent':9,46 'ai':8,32,45 'alia':78,80 'alway':314 'analyticsentrypoint':421,438 'analyticstrack':368,369,393,394,423,424 'android':5,349 'androidentrypoint':346,361,370,380,701,703 'androidmanifest':65 'androidmanifest.xml':70 'annot':526,532,665 'annotationretention.binary':525,531 'app':226 'appli':182 'applic':69 'application/json':204 'arg':461 'assertequ':604 'asserttru':607 'assist':470 'assistedfactori':463,479 'assistedinject':445,468 'auth':16 'awaititem':606,608 'backstack':242 'baseurl':197 'bill':29 'bind':94,99,101,115,123,228,257,271,509,613,627,649,653,679,680,688,692,714,717 'binditemrepositori':119,631 'bindnavig':275 'bindrepo':232 'bindsomehelp':261 'binduserrepositori':127 'broadcastreceiv':360 'build':188,206 'build.gradle.kts':72 'buildconfig.base':198 'cannot':706 'caus':644 'checkoutflow':330 'checkoutrout':338 'checkoutviewmodel':341 'class':67,111,113,142,171,296,350,362,371,387,405,411,419,433,465,466,527,533,538,555,576,619,622,625,671,677,705 'class.java':216,439 'claud':10 'client':200 'code':11 'codex':12 'common':637 'compil':106,639 'compiler-verifi':105 'compon':617 'componentact':364,389 'compos':313,318,328,488,489 'connecttimeout':185 'consist':47 'construct':160,167 'constructor':299,469,558 'contentprovid':428 'context.applicationcontext':437 'control':166 'coroutinedispatch':545,551,563 'correct':220,290 'crash':386,395 'creat':483 'creationcallback':500 'cursor':13 'dagger':285,638 'dagger/incompatiblyscopedbindings':654 'dagger/missingbinding':646 'day':36 'declar':63,250 'deep':720 'deep-div':719 'default':243 'depend':38,82 'design':19 'detailscreen':491 'detailviewmodel':467,486,496,498 'detailviewmodel.factory':464,499 'di':3 'die':239 'dispatchers.io':546 'dispatchers.main':552 'dispatchersmodul':540 'distinguish':516 'dive':721 'doesn':413 'e.g':456 'effici':138 'entri':327 'entrypoint':400,416,435 'entrypoint.analyticstracker':442 'entrypointaccessors.fromapplication':436 'error':18,286,640,643 'factori':481,501 'factory.create':502 'fake':730 'fakeitemrepositori':633 'fakerepositorymodul':626 'fitgenz':31 'fix':14,43,642,645 'forget':379 'fragment':357 'free':308 'full':724 'fun':118,126,147,176,191,209,231,260,274,319,329,422,482,490,543,549,590,594,630,691 'get':48,578 'getitem':302,476,587,602 'getitemsusecas':303,588 'getitemusecas':477 'graph':409 'hilt':2,17,37,51,310,404,408,412,432,569 'hilt-di':1 'hiltandroidapp':66 'hiltandroidrul':582 'hiltandroidtest':571,575 'hiltrul':581 'hiltrule.inject':592 'hiltviewmodel':245,289,295,315,323,342,462,497,554 'hiltwork':733 'homescreen':320 'homeuistate.loading':605 'homeuistate.success':610 'homeviewmodel':297,322,601 'homeviewmodeltest':577 'httplogginginterceptor':181 'id':458 'impl':120,128,149,152,233,262,276,632,693,694 'implement':83,90 'inconsist':20 'initi':385,699 'inject':39,56,279,294,298,352,365,374,390,397,557,584,656,710 'installin':109,140,169,417,536 'instanc':224,519 'integr':573,736 'interfac':98,133,420,480,695,713 'iodispatch':528,542,559,562 'item':457,596 'itemapiservic':213,215 'itemid':472,484,492,503 'itemrepositori':122,151,634 'itemrepositoryimpl':121,150 'json.asconverterfactory':203 'kapt':21,76,89 'kotlin':58,71,100,162,221,292,353,406,449,513,570 'ksp':22,74,85,87 'lateinit':366,375,383,391,585,696 'less':137 'level':183 'level.body':184 'libs.androidx.hilt.navigation.compose':91 'libs.hilt.android':84 'libs.hilt.compiler':86 'libs.plugins.hilt':81 'libs.plugins.ksp':79 'lifetim':227,256,269 'load':595 'mainact':363,388 'maindispatch':534,548 'manag':415 'match':660 'method':681 'miss':23,647,700,715 'modul':108,139,168,535,623,651,718,731 'multipl':508 'must':61,682 'myapplic':68 'myapplication.kt':59 'myviewmodel':556 'narrow':658 'nav':460 'navcontrol':331,332,336 'navcontroller.getbackstackentry':337 'navgraph':326 'navig':278 'navigatorimpl':277 'need':452 'networkmodul':173 'never':75,316 'non':403,431 'non-hilt':402,430 'notificationhandl':377,378 'notificationservic':372 'object':143,161,172,539,673 'okhttpclient':178,193,194,201 'okhttpclient.builder':179 'one':223,685 'overhead':104 'paramet':448,686 'parententri':334,343 'parti':159 'pattern':291 'plugin':77 'privat':300,304,474,560 'product':612 'provid':96,131,145,155,163,174,189,207,541,547,648,667,668,675,708,712 'provideiodispatch':544 'provideitemapiservic':210 'provideitemrepositori':148 'providemaindispatch':550 'provideokhttpcli':177 'provideretrofit':192 'qualifi':506,514,523,529 'rare':270 're':249 're-declar':248 'reduc':26 'refer':722 'references/hilt-testing.md':723 'references/hilt-workmanager.md':732 'rememb':335 'remov':663 'replac':611,620 'repo':235 'repoimpl':234 'repositorymodul':114,144,621 'requir':53,60,354 'result':381 'retent':524,530 'retrofit':195,211,212 'retrofit.builder':196 'retrofit.create':214 'rotat':238 'rule':41,92,153,217,287,344,398,443,504,565,579,635 'runtest':598 'runtim':103,447,454 'savedstatehandl':306,307 'scope':219,283,324,659,661,664 'servic':359,373 'setup':52,591,726 'ship':33 'signatur':689 'singleton':116,124,146,175,190,208,222,229,280,628,655 'singletoncompon':110,141,170,418,537,618 'skill':6 'skill-hilt-di' 'somehelp':264 'somehelperimpl':263 'source-piyushverma0' 'state':25 'string':473,485,493 'success':597 'supabas':15 'surviv':237 'test':567,574,593,615,725 'testinstallin':616,728 'third':158 'third-parti':157 'tie':253,266 'timeunit.seconds':187 'token':28 'tomediatyp':205 'topic-agent-skills' 'topic-ai-agent' 'topic-android' 'topic-antigravity' 'topic-claude-code' 'topic-codex' 'topic-cursor' 'topic-gemini-cli' 'topic-hilt' 'topic-jetpack-compose' 'topic-kotlin' 'topic-material3' 'tracker':441 'two':518 'type':512,522 'uistat':24 'url':199 'usag':425,553 'use':73,672 'userrepositori':130 'userrepositoryimpl':129 'val':301,305,333,339,434,440,471,475,494,561,580,599 'valu':455 'var':367,376,392,586,697 'verifi':107 'view':358 'viewmodel':255,293,311,317,321,340,451,478,495,564,600 'viewmodel.uistate.test':603 'viewmodelscop':252,258,282 'violat':284 'without':709 'work':57,134 'workmanag':735 'wrong':49,687 'zero':102","prices":[{"id":"8a32b4bb-0276-4c0d-9b70-305ff5823916","listingId":"cf0ca6b2-3aac-4fae-8841-7774cc17cd6f","amountUsd":"0","unit":"free","nativeCurrency":null,"nativeAmount":null,"chain":null,"payTo":null,"paymentMethod":"skill-free","isPrimary":true,"details":{"org":"piyushverma0","category":"android-agent-skills","install_from":"skills.sh"},"createdAt":"2026-05-18T13:14:49.599Z"}],"sources":[{"listingId":"cf0ca6b2-3aac-4fae-8841-7774cc17cd6f","source":"github","sourceId":"piyushverma0/android-agent-skills/hilt-di","sourceUrl":"https://github.com/piyushverma0/android-agent-skills/tree/main/skills/hilt-di","isPrimary":false,"firstSeenAt":"2026-05-18T13:14:49.599Z","lastSeenAt":"2026-05-18T19:09:09.949Z"}],"details":{"listingId":"cf0ca6b2-3aac-4fae-8841-7774cc17cd6f","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"piyushverma0","slug":"hilt-di","github":{"repo":"piyushverma0/android-agent-skills","stars":8,"topics":["agent-skills","ai-agent","android","antigravity","claude-code","codex","cursor","gemini-cli","hilt","jetpack-compose","kotlin","material3","open-source","skills","supabase"],"license":"mit","html_url":"https://github.com/piyushverma0/android-agent-skills","pushed_at":"2026-04-27T09:15:31Z","description":"27 Android skills for AI agents (Claude Code, Codex, Cursor). Fixes Supabase auth, Hilt errors, design inconsistency, kapt→ksp, missing UiState states. Reduced my token bills 5×. FitGenZ AI shipped in 18 days.","skill_md_sha":"5b8e326411b074adfbffcdc76018f9243905e5a6","skill_md_path":"skills/hilt-di/SKILL.md","default_branch":"main","skill_tree_url":"https://github.com/piyushverma0/android-agent-skills/tree/main/skills/hilt-di"},"layout":"multi","source":"github","category":"android-agent-skills","frontmatter":{},"skills_sh_url":"https://skills.sh/piyushverma0/android-agent-skills/hilt-di"},"updatedAt":"2026-05-18T19:09:09.949Z"}}