{"id":15638117,"url":"https://github.com/skydoves/firebase-android-ktx","last_synced_at":"2025-04-05T21:07:16.863Z","repository":{"id":255799027,"uuid":"851436429","full_name":"skydoves/firebase-android-ktx","owner":"skydoves","description":"🔥 Kotlin \u0026 Compose-friendly Firebase extensions designed to help you focus on your business logic.","archived":false,"fork":false,"pushed_at":"2025-02-03T08:09:34.000Z","size":158,"stargazers_count":116,"open_issues_count":0,"forks_count":9,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-03-29T20:04:19.495Z","etag":null,"topics":["android","androidx","compose","extension","firebase","jetpack","kotlin","ktx","library","skydoves"],"latest_commit_sha":null,"homepage":"","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/skydoves.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"skydoves","custom":["https://www.paypal.me/skydoves","https://www.buymeacoffee.com/skydoves"]}},"created_at":"2024-09-03T05:13:06.000Z","updated_at":"2025-02-13T22:56:39.000Z","dependencies_parsed_at":"2024-09-15T14:45:08.111Z","dependency_job_id":"aa186020-01e9-483f-ad0f-7ac204ee1f38","html_url":"https://github.com/skydoves/firebase-android-ktx","commit_stats":{"total_commits":26,"total_committers":4,"mean_commits":6.5,"dds":"0.46153846153846156","last_synced_commit":"73a58a65b4907ee3c8d07354bb9764c213d42254"},"previous_names":["skydoves/firebase-android-ktx"],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/skydoves%2Ffirebase-android-ktx","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/skydoves%2Ffirebase-android-ktx/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/skydoves%2Ffirebase-android-ktx/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/skydoves%2Ffirebase-android-ktx/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/skydoves","download_url":"https://codeload.github.com/skydoves/firebase-android-ktx/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247399876,"owners_count":20932876,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["android","androidx","compose","extension","firebase","jetpack","kotlin","ktx","library","skydoves"],"created_at":"2024-10-03T11:18:48.694Z","updated_at":"2025-04-05T21:07:16.844Z","avatar_url":"https://github.com/skydoves.png","language":"Kotlin","readme":"\u003ch1 align=\"center\"\u003eFirebase Android KTX\u003c/h1\u003e\u003c/br\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://opensource.org/licenses/Apache-2.0\"\u003e\u003cimg alt=\"License\" src=\"https://img.shields.io/badge/License-Apache%202.0-blue.svg\"/\u003e\u003c/a\u003e\n  \u003ca href=\"https://android-arsenal.com/api?level=21\"\u003e\u003cimg alt=\"API\" src=\"https://img.shields.io/badge/API-21%2B-brightgreen.svg?style=flat\"/\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/skydoves/firebase-android-ktx/actions/workflows/android.yml\"\u003e\u003cimg alt=\"Build Status\" \n  src=\"https://github.com/skydoves/firebase-android-ktx/actions/workflows/android.yml/badge.svg\"/\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/skydoves\"\u003e\u003cimg alt=\"Profile\" src=\"https://skydoves.github.io/badges/skydoves.svg\"/\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/doveletter\"\u003e\u003cimg alt=\"Profile\" src=\"https://skydoves.github.io/badges/dove-letter.svg\"/\u003e\u003c/a\u003e\n\u003c/p\u003e\u003cbr\u003e\n\n\u003cp align=\"center\"\u003e\n \u003cimg src=\"https://github.com/user-attachments/assets/87ed8c86-31e2-429e-a09e-34a559416880\"/\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e🔥 Kotlin \u0026 Compose-friendly Firebase extensions designed to help you focus on your business logic. \u003c/p\u003e\n\n- **Firebase Realtime Database KTX**: Enables observing snapshot data changes as Kotlin Flow with fully customizable serialization, making it easy to adapt the data format to your application's needs.\n- **Firebase Messaging Lifecycle KTX**: Provides a lifecycle-aware version of `FirebaseMessagingService`, allowing you to cancel coroutine tasks within the `onNewToken` method in accordance with the service's lifecycle, ensuring efficient resource management and preventing leaks.\n\n## Firebase Realtime Database KTX\n\nThe Firebase Realtime Database KTX library allows you to observe changes in the Realtime Database as a Flow, with fully customizable serialization options. This makes it easy to handle data streams while adapting the data format to fit your app's needs, ensuring seamless integration with Kotlin and Jetpack Compose.\n\n[![Maven Central](https://img.shields.io/maven-central/v/com.github.skydoves/firebase-database-ktx.svg?label=Maven%20Central)](https://search.maven.org/search?q=g:%22com.github.skydoves%22%20AND%20a:%22firebase-database-ktx%22)\n\n### Version Catalog\n\nIf you're using Version Catalog, you can configure the dependency by adding it to your `libs.versions.toml` file as follows:\n\n```toml\n[versions]\n#...\nfirebaseKtx = \"0.2.1\"\n\n[libraries]\n#...\nfirebase-database-ktx = { module = \"com.github.skydoves:firebase-database-ktx\", version.ref = \"firebaseKtx\" }\n```\n\n### Gradle\nAdd the dependency below to your **module**'s `build.gradle.kts` file:\n\n```gradle\ndependencies {\n    implementation(\"com.github.skydoves:firebase-database-ktx:$version\")\n    \n    // if you're using Version Catalog\n    implementation(libs.firebase.database.ktx)\n}\n```\n\n### The Problem\n\nThe [Firebase Realtime Database](https://firebase.google.com/docs/database) is primarily based on Java and callback listeners, making it less compatible with Coroutines and Jetpack Compose. Furthermore, since it returns snapshot values in non-JSON formats, handling objects and implementing custom serialization solutions can be challenging, as shown in the example below:\n\n```kotlin\nval listener = object : ValueEventListener {\n  override fun onDataChange(snapshot: DataSnapshot) {\n    val value = snapshot.child(\"timeline\")\n    // ..\n  }\n\n  override fun onCancelled(error: DatabaseError) {\n    // ..\n  }\n}\ndatabase.addValueEventListener(listener)\n```\n\nThe result (`value`) isn't in JSON format, so we can't directly serialize it into a target object like this:\n\n```\n{top={banner={size={width=0, height=300}, scaleType=crop, url=https://blog.icons8.com/wp-content/uploads/2020/02/city-illustration-graphic-art.jpg}, order=0}, bottom={list={layout=grid, itemSize={width=135, height=210}, items=[{scaleType=crop, title=Frozen ...\n```\n\nThey provide their own serialization algorithm internally, but it's not customizable. Additionally, you need to attach specific annotations and initialize fields with default values, as shown in the example below:\n\n```kotlin\n@IgnoreExtraProperties\ndata class Post(\n    var uid: String? = \"\",\n    var author: String? = \"\",\n    var title: String? = \"\",\n) {\n    @Exclude\n    fun toMap(): Map\u003cString, Any?\u003e {\n        return mapOf(\n            \"uid\" to uid,\n            \"author\" to author,\n            \"title\" to title,\n    }\n}\n```\n\n### 1. DatabaseReference.flow()\n\nYou can easily serialize snapshot values from the Realtime Database and observe them as a Flow by using the `DatabaseReference.flow()` extension, as shown in the example below:\n\n```kotlin\nclass MainViewModel : ViewModel() {\n\n  private val database = Firebase.database(BuildConfig.REALTIME_DATABASE_URL).reference\n\n  private val json = Json {\n    isLenient = true\n    ignoreUnknownKeys = true\n  }\n\n  val timelineUi = database.flow\u003cTimelineUi\u003e(\n    path = { dataSnapshot -\u003e\n      dataSnapshot.child(\"timeline\")\n    },\n    decodeProvider = { jsonString -\u003e\n      json.decodeFromString(jsonString)\n    },\n  ).flatMapLatest { result -\u003e\n    if (result.isSuccess) {\n      flowOf(result.getOrNull())\n    } else {\n      throw RuntimeException(\"parsing error!\")\n    }\n  }.stateIn(\n    scope = viewModelScope,\n    started = SharingStarted.WhileSubscribed(5000),\n    initialValue = null,\n  )\n}\n```\n\nNow, you can safely observe it in Jetpack Compose using `collectAsStateWithLifecycle`, as demonstrated in the code below:\n\n```kotlin\nval timelineUI by viewModel.timelineUi.collectAsStateWithLifecycle()\n```\n\n### 2. DatabaseReference.flowSingle()\n\nThis functions similarly to `DatabaseReference.flow()`, but it only emits the value once, even if the value changes dynamically. It uses `addListenerForSingleValueEvent` instead of `addValueEventListener`. So, it's suitable for the one-shot flow.\n\n### 3. DatabaseReference.flowChild()\n\nThis observes all changes in the child nodes of the Realtime Database, including additions, modifications, deletions, movements, and cancellations. The flow emits `ChildState`, which encapsulates the state changes along with the snapshot value and any errors.\n\n```kotlin\nclass MainViewModel : ViewModel() {\n  val childState = database.flowChild\u003cTimelineUi\u003e(\n    path = { dataSnapshot -\u003e\n      dataSnapshot\n    },\n    decodeProvider = { jsonString -\u003e\n      json.decodeFromString(jsonString)\n    },\n  ).stateIn(\n    scope = viewModelScope,\n    started = SharingStarted.WhileSubscribed(5000),\n    initialValue = null,\n  )\n}\n```\n\nNow, you can safely observe it in Jetpack Compose using `collectAsStateWithLifecycle`, as demonstrated in the code below:\n\n```kotlin\nval childState by viewModel.childState.collectAsStateWithLifecycle()\n\nwhen (childState) {\n  is ChildState.ChildAdded -\u003e ..\n  is ChildState.ChildChanged -\u003e ..\n  is ChildState.ChildMoved -\u003e ..\n  is ChildState.ChildRemoved -\u003e ..\n  is ChildState.ChildCanceled -\u003e ..\n  else -\u003e ..\n}\n```\n\n### Query Extensions\n\nRealtime Database provides [support for retrieving and querying data efficiently](https://firebase.google.com/docs/database/admin/retrieve-data). It allows data ordering and simple queries using functions like `orderByKey()`, `orderByValue()`, `orderByPriority()`, and `orderByChild(..)`, all of which return a `Query` instance. The following extensions enhance data retrieval by integrating seamlessly with Kotlin's Flow:\n\n- **Query.flow()**: Serializes snapshot values from the Realtime Database and observes them as a Flow, enabling real-time updates from a `Query` instance.\n- **Query.flowSingle()**: Functions similarly to `Query.flow()`, but emits the value only once, regardless of dynamic changes. It leverages `addListenerForSingleValueEvent` instead of `addValueEventListener`, making it ideal for one-time data retrieval.\n- **Query.flowSingle()**: Observes all child node changes, including additions, modifications, deletions, movements, and cancellations. It emits `ChildState`, encapsulating state changes, snapshot values, and any potential errors.\n\n## Firebase Messaging Lifecycle KTX\n\nThe Firebase Messaging Lifecycle KTX extension allows you to implement a lifecycle-aware [FirebaseMessagingService](https://firebase.google.com/docs/reference/android/com/google/firebase/messaging/FirebaseMessagingService), enabling you to manage and cancel coroutine scopes based on the service’s lifecycle. This is especially useful when you need to send a refreshed token to the server in the [onNewToken](https://firebase.google.com/docs/reference/android/com/google/firebase/messaging/FirebaseMessagingService#onNewToken(java.lang.String)) method, ensuring that the coroutine scope is properly handled and preventing potential memory leaks from continuing to run after the service is no longer need to be actived.\n\n[![Maven Central](https://img.shields.io/maven-central/v/com.github.skydoves/firebase-database-ktx.svg?label=Maven%20Central)](https://search.maven.org/search?q=g:%22com.github.skydoves%22%20AND%20a:%22firebase-database-ktx%22)\n\n### Gradle\nAdd the dependency below to your **module**'s `build.gradle.kts` file:\n\n```gradle\ndependencies {\n    implementation(\"com.github.skydoves:firebase-messaging-lifecycle-ktx:$version\")\n}\n```\n\n### LifecycleAwareFirebaseMessagingService\n\n`LifecycleAwareFirebaseMessagingService` is a lifecycle-aware version of [FirebaseMessagingService](https://firebase.google.com/docs/reference/android/com/google/firebase/messaging/FirebaseMessagingService), designed to manage tasks in alignment with the service's lifecycle. For instance, you can send a token to your backend in the `onNewToken` method using the `lifecycleOwner.lifecycleScope.launch` function. This ensures the coroutine scope is automatically canceled when the service lifecycle changes, preventing any unintended background tasks from continuing to run.\n\n```kotlin\nclass AppFirebaseMessagingService : LifecycleAwareFirebaseMessagingService() {\n  \n  override fun onNewToken(token: String) {\n    super.onNewToken(token)\n    lifecycleScope.launch {\n      // send the token to the server\n      ..\n    }\n  }\n\n  override fun onMessageReceived(message: RemoteMessage) {\n    super.onMessageReceived(message)\n    Log.d(APP_LOG_TAG, \"FCMService#onMessageRec onMessageReceived: ${message.data}\")\n  }\n\n  override fun onDestroy() {\n    super.onDestroy()\n    Log.d(APP_LOG_TAG, \"FCMService#onDestroy onDestroy\")\n  }\n}\n```\n\n## Find this repository useful? :heart:\nSupport it by joining __[stargazers](https://github.com/skydoves/firebase-android-ktx/stargazers)__ for this repository. :star: \u003cbr\u003e\nAlso, __[follow me](https://github.com/skydoves)__ on GitHub for my next creations! 🤩\n\n# License\n```xml\nDesigned and developed by 2024 skydoves (Jaewoong Eum)\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n   http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n```\n","funding_links":["https://github.com/sponsors/skydoves","https://www.paypal.me/skydoves","https://www.buymeacoffee.com/skydoves"],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fskydoves%2Ffirebase-android-ktx","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fskydoves%2Ffirebase-android-ktx","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fskydoves%2Ffirebase-android-ktx/lists"}