{"id":"749d94f8-67b9-4c80-a012-35138c775ad1","shortId":"mXqeVb","kind":"skill","title":"firebase","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":"# Firebase for Android\r\n\r\n## Setup — always use BoM for version management\r\n\r\n```toml\r\n[versions]\r\nfirebaseBom = \"33.5.1\"\r\ngoogleServices = \"4.4.2\"\r\n```\r\n\r\n```kotlin\r\n// build.gradle.kts\r\nplugins { alias(libs.plugins.google.services) }\r\ndependencies {\r\n    implementation(platform(libs.firebase.bom))\r\n    implementation(libs.firebase.auth.ktx)\r\n    implementation(libs.firebase.firestore.ktx)\r\n    implementation(libs.firebase.storage.ktx)\r\n    implementation(libs.firebase.analytics.ktx)\r\n    implementation(libs.firebase.crashlytics.ktx)\r\n    implementation(libs.firebase.messaging.ktx)\r\n}\r\n```\r\n\r\n## Firebase Auth\r\n\r\n```kotlin\r\n// ✅ Auth repository\r\nclass AuthRepositoryImpl @Inject constructor(\r\n    private val auth: FirebaseAuth\r\n) : AuthRepository {\r\n\r\n    override val currentUser: Flow<User?> = callbackFlow {\r\n        val listener = FirebaseAuth.AuthStateListener { auth ->\r\n            trySend(auth.currentUser?.toDomain())\r\n        }\r\n        auth.addAuthStateListener(listener)\r\n        awaitClose { auth.removeAuthStateListener(listener) }\r\n    }\r\n\r\n    override suspend fun signInWithEmail(email: String, password: String): Result<User> =\r\n        runCatching {\r\n            auth.signInWithEmailAndPassword(email, password).await()\r\n                .user?.toDomain() ?: throw IllegalStateException(\"User is null after sign in\")\r\n        }\r\n\r\n    override suspend fun createAccount(email: String, password: String): Result<User> =\r\n        runCatching {\r\n            auth.createUserWithEmailAndPassword(email, password).await()\r\n                .user?.toDomain() ?: throw IllegalStateException(\"User is null after creation\")\r\n        }\r\n\r\n    override suspend fun signInWithGoogle(idToken: String): Result<User> = runCatching {\r\n        val credential = GoogleAuthProvider.getCredential(idToken, null)\r\n        auth.signInWithCredential(credential).await()\r\n            .user?.toDomain() ?: throw IllegalStateException(\"User is null\")\r\n    }\r\n\r\n    override fun signOut() = auth.signOut()\r\n\r\n    override fun isSignedIn(): Boolean = auth.currentUser != null\r\n}\r\n\r\nfun FirebaseUser.toDomain() = User(uid, email ?: \"\", displayName ?: \"\", photoUrl?.toString())\r\n```\r\n\r\n## Firestore — CRUD + real-time\r\n\r\n```kotlin\r\n// ✅ Firestore repository\r\nclass ItemFirestoreDataSource @Inject constructor(\r\n    private val firestore: FirebaseFirestore,\r\n    private val auth: FirebaseAuth\r\n) {\r\n    private val itemsCollection\r\n        get() = firestore.collection(\"users\")\r\n            .document(auth.currentUser?.uid ?: throw UnauthorizedException())\r\n            .collection(\"items\")\r\n\r\n    // Real-time stream\r\n    fun getItemsStream(): Flow<List<ItemDto>> = callbackFlow {\r\n        val listener = itemsCollection\r\n            .orderBy(\"created_at\", Query.Direction.DESCENDING)\r\n            .addSnapshotListener { snapshot, error ->\r\n                if (error != null) { close(error); return@addSnapshotListener }\r\n                val items = snapshot?.documents?.mapNotNull { doc ->\r\n                    doc.toObject(ItemDto::class.java)?.copy(id = doc.id)\r\n                } ?: emptyList()\r\n                trySend(items)\r\n            }\r\n        awaitClose { listener.remove() }\r\n    }\r\n\r\n    // One-shot get\r\n    suspend fun getItem(id: String): ItemDto? =\r\n        itemsCollection.document(id).get().await()\r\n            .toObject(ItemDto::class.java)?.copy(id = id)\r\n\r\n    // Write\r\n    suspend fun upsertItem(item: ItemDto): String {\r\n        return if (item.id.isEmpty()) {\r\n            itemsCollection.add(item).await().id\r\n        } else {\r\n            itemsCollection.document(item.id).set(item).await()\r\n            item.id\r\n        }\r\n    }\r\n\r\n    // Update specific fields\r\n    suspend fun updateFavorite(id: String, isFavorite: Boolean) {\r\n        itemsCollection.document(id).update(\r\n            \"is_favorite\", isFavorite,\r\n            \"updated_at\", FieldValue.serverTimestamp()\r\n        ).await()\r\n    }\r\n\r\n    // Delete\r\n    suspend fun deleteItem(id: String) {\r\n        itemsCollection.document(id).delete().await()\r\n    }\r\n\r\n    // Batch write — atomic multi-document update\r\n    suspend fun batchUpdate(updates: List<Pair<String, Map<String, Any>>>) {\r\n        firestore.runBatch { batch ->\r\n            updates.forEach { (id, fields) ->\r\n                batch.update(itemsCollection.document(id), fields)\r\n            }\r\n        }.await()\r\n    }\r\n\r\n    // Transaction\r\n    suspend fun transferItem(fromId: String, toUserId: String): Result<Unit> = runCatching {\r\n        firestore.runTransaction { transaction ->\r\n            val fromRef = itemsCollection.document(fromId)\r\n            val item = transaction.get(fromRef).toObject(ItemDto::class.java)\r\n                ?: throw NotFoundException()\r\n            val toRef = firestore.collection(\"users\").document(toUserId)\r\n                .collection(\"items\").document(fromId)\r\n            transaction.set(toRef, item)\r\n            transaction.delete(fromRef)\r\n        }.await()\r\n    }\r\n}\r\n```\r\n\r\n## Hilt Firebase module\r\n\r\n```kotlin\r\n@Module\r\n@InstallIn(SingletonComponent::class)\r\nobject FirebaseModule {\r\n    @Provides @Singleton\r\n    fun provideFirebaseAuth(): FirebaseAuth = Firebase.auth\r\n\r\n    @Provides @Singleton\r\n    fun provideFirestore(): FirebaseFirestore = Firebase.firestore.also {\r\n        it.firestoreSettings = firestoreSettings { isPersistenceEnabled = true }\r\n    }\r\n\r\n    @Provides @Singleton\r\n    fun provideStorage(): FirebaseStorage = Firebase.storage\r\n\r\n    @Provides @Singleton\r\n    fun provideAnalytics(@ApplicationContext context: Context): FirebaseAnalytics =\r\n        Firebase.analytics\r\n}\r\n```\r\n\r\n## FCM — Firebase Cloud Messaging\r\n\r\n```kotlin\r\n@AndroidEntryPoint\r\nclass MyFirebaseMessagingService : FirebaseMessagingService() {\r\n    @Inject lateinit var notificationHandler: NotificationHandler\r\n\r\n    override fun onMessageReceived(message: RemoteMessage) {\r\n        message.notification?.let { notification ->\r\n            notificationHandler.showNotification(\r\n                title = notification.title ?: \"\",\r\n                body = notification.body ?: \"\",\r\n                data = message.data\r\n            )\r\n        }\r\n    }\r\n\r\n    override fun onNewToken(token: String) {\r\n        // Send token to your server\r\n    }\r\n}\r\n```\r\n\r\n## Common Mistakes\r\n\r\n❌ Missing `google-services.json` in `app/` directory\r\n❌ Not using BoM — version conflicts between Firebase libraries\r\n❌ Calling Firebase on Main thread without `.await()` — use suspend + `.await()`\r\n❌ No offline persistence for Firestore — enable `isPersistenceEnabled = true`\r\n❌ Missing Firestore security rules — always configure before production\r\n❌ Storing sensitive data in Firestore without encryption","tags":["firebase","android","agent","skills","piyushverma0","agent-skills","ai-agent","antigravity","claude-code","codex","cursor","gemini-cli"],"capabilities":["skill","source-piyushverma0","skill-firebase","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/firebase","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 (6,673 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.754Z","embedding":null,"createdAt":"2026-05-18T13:14:49.358Z","updatedAt":"2026-05-18T19:09:09.754Z","lastSeenAt":"2026-05-18T19:09:09.754Z","tsv":"'18':33 '27':2 '33.5.1':48 '4.4.2':50 '5':28 'addsnapshotlisten':241,250 'agent':7 'ai':6,30 'alia':54 'alway':39,524 'android':3,37 'androidentrypoint':453 'app':492 'applicationcontext':443 'atom':341 'auth':14,73,75,83,95,210 'auth.addauthstatelistener':99 'auth.createuserwithemailandpassword':138 'auth.currentuser':97,182,219 'auth.removeauthstatelistener':102 'auth.signinwithcredential':164 'auth.signinwithemailandpassword':114 'auth.signout':177 'authrepositori':85 'authrepositoryimpl':78 'await':117,141,166,281,300,307,328,338,365,406,508,511 'awaitclos':101,266 'batch':339,357 'batch.update':361 'batchupd':348 'bill':27 'bodi':473 'bom':41,496 'boolean':181,318 'build.gradle.kts':52 'call':502 'callbackflow':91,233 'class':77,200,414,454 'class.java':259,284,388 'claud':8 'close':247 'cloud':450 'code':9 'codex':10 'collect':223,397 'common':487 'configur':525 'conflict':498 'constructor':80,203 'context':444,445 'copi':260,285 'creat':238 'createaccount':131 'creation':150 'credenti':160,165 'crud':193 'currentus':88 'cursor':11 'data':475,530 'day':34 'delet':329,337 'deleteitem':332 'depend':56 'design':17 'directori':493 'displaynam':189 'doc':256 'doc.id':262 'doc.toobject':257 'document':218,254,344,395,399 'els':302 'email':108,115,132,139,188 'emptylist':263 'enabl':517 'encrypt':534 'error':16,243,245,248 'favorit':323 'fcm':448 'field':311,360,364 'fieldvalue.servertimestamp':327 'firebas':1,35,72,408,449,500,503 'firebase.analytics':447 'firebase.auth':422 'firebase.firestore.also':428 'firebase.storage':438 'firebaseanalyt':446 'firebaseauth':84,211,421 'firebaseauth.authstatelistener':94 'firebasebom':47 'firebasefirestor':207,427 'firebasemessagingservic':456 'firebasemodul':416 'firebasestorag':437 'firebaseuser.todomain':185 'firestor':192,198,206,516,521,532 'firestore.collection':216,393 'firestore.runbatch':356 'firestore.runtransaction':376 'firestoreset':430 'fitgenz':29 'fix':12 'flow':89,231 'fromid':370,381,400 'fromref':379,385,405 'fun':106,130,153,175,179,184,229,273,290,313,331,347,368,419,425,435,441,463,478 'get':215,271,280 'getitem':274 'getitemsstream':230 'google-services.json':490 'googleauthprovider.getcredential':161 'googleservic':49 'hilt':15,407 'id':261,275,279,286,287,301,315,320,333,336,359,363 'idtoken':155,162 'illegalstateexcept':121,145,170 'implement':57,60,62,64,66,68,70 'inconsist':18 'inject':79,202,457 'installin':412 'isfavorit':317,324 'ispersistenceen':431,518 'issignedin':180 'it.firestoresettings':429 'item':224,252,265,292,299,306,383,398,403 'item.id':304,308 'item.id.isempty':297 'itemdto':258,277,283,293,387 'itemfirestoredatasourc':201 'itemscollect':214,236 'itemscollection.add':298 'itemscollection.document':278,303,319,335,362,380 'kapt':19 'kotlin':51,74,197,410,452 'ksp':20 'lateinit':458 'let':468 'librari':501 'libs.firebase.analytics.ktx':67 'libs.firebase.auth.ktx':61 'libs.firebase.bom':59 'libs.firebase.crashlytics.ktx':69 'libs.firebase.firestore.ktx':63 'libs.firebase.messaging.ktx':71 'libs.firebase.storage.ktx':65 'libs.plugins.google.services':55 'list':232,350 'listen':93,100,103,235 'listener.remove':267 'main':505 'manag':44 'map':353 'mapnotnul':255 'messag':451,465 'message.data':476 'message.notification':467 'miss':21,489,520 'mistak':488 'modul':409,411 'multi':343 'multi-docu':342 'myfirebasemessagingservic':455 'notfoundexcept':390 'notif':469 'notification.body':474 'notification.title':472 'notificationhandl':460,461 'notificationhandler.shownotification':470 'null':124,148,163,173,183,246 'object':415 'offlin':513 'one':269 'one-shot':268 'onmessagereceiv':464 'onnewtoken':479 'orderbi':237 'overrid':86,104,128,151,174,178,462,477 'pair':351 'password':110,116,134,140 'persist':514 'photourl':190 'platform':58 'plugin':53 'privat':81,204,208,212 'product':527 'provid':417,423,433,439 'provideanalyt':442 'providefirebaseauth':420 'providefirestor':426 'providestorag':436 'query.direction.descending':240 'real':195,226 'real-tim':194,225 'reduc':24 'remotemessag':466 'repositori':76,199 'result':112,136,157,374 'return':249,295 'rule':523 'runcatch':113,137,158,375 'secur':522 'send':482 'sensit':529 'server':486 'set':305 'setup':38 'ship':31 'shot':270 'sign':126 'signinwithemail':107 'signinwithgoogl':154 'signout':176 'singleton':418,424,434,440 'singletoncompon':413 'skill':4 'skill-firebase' 'snapshot':242,253 'source-piyushverma0' 'specif':310 'state':23 'store':528 'stream':228 'string':109,111,133,135,156,276,294,316,334,352,354,371,373,481 'supabas':13 'suspend':105,129,152,272,289,312,330,346,367,510 'thread':506 'throw':120,144,169,221,389 'time':196,227 'titl':471 'todomain':98,119,143,168 'token':26,480,483 'toml':45 'toobject':282,386 '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' 'toref':392,402 'tostr':191 'touserid':372,396 'transact':366,377 'transaction.delete':404 'transaction.get':384 'transaction.set':401 'transferitem':369 'true':432,519 'trysend':96,264 'uid':187,220 'uistat':22 'unauthorizedexcept':222 'updat':309,321,325,345,349 'updatefavorit':314 'updates.foreach':358 'upsertitem':291 'use':40,495,509 'user':90,118,122,142,146,167,171,186,217,394 'val':82,87,92,159,205,209,213,234,251,378,382,391 'var':459 'version':43,46,497 'without':507,533 'write':288,340","prices":[{"id":"68513aed-ac0c-4f80-b307-e2a45df8303f","listingId":"749d94f8-67b9-4c80-a012-35138c775ad1","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.358Z"}],"sources":[{"listingId":"749d94f8-67b9-4c80-a012-35138c775ad1","source":"github","sourceId":"piyushverma0/android-agent-skills/firebase","sourceUrl":"https://github.com/piyushverma0/android-agent-skills/tree/main/skills/firebase","isPrimary":false,"firstSeenAt":"2026-05-18T13:14:49.358Z","lastSeenAt":"2026-05-18T19:09:09.754Z"}],"details":{"listingId":"749d94f8-67b9-4c80-a012-35138c775ad1","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"piyushverma0","slug":"firebase","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":"442fc5a9ba120b3adc5905b9840fd465cbbb9f0b","skill_md_path":"skills/firebase/SKILL.md","default_branch":"main","skill_tree_url":"https://github.com/piyushverma0/android-agent-skills/tree/main/skills/firebase"},"layout":"multi","source":"github","category":"android-agent-skills","frontmatter":{},"skills_sh_url":"https://skills.sh/piyushverma0/android-agent-skills/firebase"},"updatedAt":"2026-05-18T19:09:09.754Z"}}