{"id":16775213,"url":"https://github.com/arunkumar9t2/compass","last_synced_at":"2025-10-14T05:10:09.436Z","repository":{"id":46565774,"uuid":"405335855","full_name":"arunkumar9t2/compass","owner":"arunkumar9t2","description":"🧭 Collection of Kotlin APIs/tools to make using Realm Mobile database easier","archived":false,"fork":false,"pushed_at":"2022-02-17T15:41:51.000Z","size":3604,"stargazers_count":18,"open_issues_count":2,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-03-24T17:52:43.882Z","etag":null,"topics":["android","jetpack","jetpack-paging","mongodb-realm","realm","realm-java","realm-mobile-database"],"latest_commit_sha":null,"homepage":"https://arunkumar9t2.github.io/compass","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/arunkumar9t2.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null}},"created_at":"2021-09-11T09:20:57.000Z","updated_at":"2024-01-16T11:14:56.000Z","dependencies_parsed_at":"2022-09-26T22:01:46.632Z","dependency_job_id":null,"html_url":"https://github.com/arunkumar9t2/compass","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":"arunkumar9t2/android-template","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arunkumar9t2%2Fcompass","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arunkumar9t2%2Fcompass/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arunkumar9t2%2Fcompass/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arunkumar9t2%2Fcompass/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/arunkumar9t2","download_url":"https://codeload.github.com/arunkumar9t2/compass/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248288347,"owners_count":21078903,"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","jetpack","jetpack-paging","mongodb-realm","realm","realm-java","realm-mobile-database"],"created_at":"2024-10-13T06:51:23.107Z","updated_at":"2025-10-14T05:10:04.414Z","avatar_url":"https://github.com/arunkumar9t2.png","language":"Kotlin","readme":"# Compass\n\n\u003cp align=\"center\"\u003e\n\u003cb\u003eKotlin API and tools to make working with Realm easier\u003c/b\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e \n\u003ca href=\"https://github.com/arunkumar9t2/compass/actions/workflows/ci.yml\"\u003e\u003cimg src=\"https://img.shields.io/github/workflow/status/arunkumar9t2/compass/CI?logo=GitHub\u0026style=flat-square\"/\u003e\u003c/a\u003e\n\u003ca href=\"https://arunkumar9t2.github.io/compass/\"\u003e\u003cimg src=\"https://img.shields.io/badge/Website-%20-lightgrey.svg?color=0F8842\u0026colorA=0F8842\u0026style=flat-square\u0026logo=github\"/\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n## Components\n\n`Compass` is designed to make working with [Realm](https://realm.io) easier through collection of Kotlin types and extensions that handle `Realm`'s lifecycle and threading model effectively. It has two major components\n\n* `compass` - The core Kotlin API with set of extensions for common patterns around `Realm`'s lifecycle and threading.\n* `compass-paging` - Provides extensions to integrate with [Jetpack Paging 3](https://developer.android.com/topic/libraries/architecture/paging/v3-overview).\n* _more coming soon..™_\n\n## Getting Started\n\n`Compass` is available as android library artifacts on `mavenCentral`. In root `build.gradle`:\n\n```groovy\nallprojects {\n  repositories {\n    mavenCentral()\n  }\n}\n```\n\nOr in `dependenciesResolutionManagement` in `settings.gradle`:\n\n```groovy\ndependencyResolutionManagement {\n  repositories {\n    mavenCentral()\n  }\n}\n```\nThen in your modules:\n\n```groovy\n\ndependencies {\n    implementation \"dev.arunkumar.compass:compass:1.0.0\"\n    // Paging integration\n    implementation \"dev.arunkumar.compass:compass-paging:1.0.0\"\n}\n```\n## Setup\n\nCompass assumes `Realm.init(this)` and `Realm.setDefaultConfiguration(config)` is called already and acquires a default instance of `Realm` using `Realm.getDefaultInstance()` where needed.\n\n## Features\n\n### Query construction\n\nUse [RealmQuery](https://arunkumar9t2.github.io/compass/compass/dev.arunkumar.compass/-realm-query.html) construction function to build `RealmQuery` instances. Through use of lambdas, `RealmQuery{}` overcomes threading limitations by deferring invocation to usage site rather than call site.\n\n```kotlin\nval personQueryBuilder =  RealmQuery { where\u003cPerson\u003e().sort(Person.NAME) }\n```\nExtensions like [getAll()](https://arunkumar9t2.github.io/compass/compass/dev.arunkumar.compass/get-all.html) is provided on `RealmQueryBuilder` that takes advantage of this pattern.\n\n### Threading\n\n`Realm`'s live [updating object](https://docs.mongodb.com/realm/sdk/android/fundamentals/live-queries/#auto-refresh) model mandates few threading rules. Those rules are:\n\n1. `Realm`s can be accessed only from the thread they were originally created\n2. `Realm`s can be observed only from threads which have Android's [Looper](https://developer.android.com/reference/android/os/Looper) [prepared](https://developer.android.com/reference/android/os/Looper#prepare()) on them.\n3. Managed `RealmResults` can't be passed around the threads.\n\n`Compass` tries to make it easier to work with Realm by providing safe defaults.\n\n#### RealmExecutor/RealmDispatcher\n\n`RealmExecutor` and `RealmDispatcher` are provided which internally prepares Android Looper by using `HandlerThread`s. The following is valid:\n\n```kotlin\nwithContext(RealmDispatcher()) {\n    Realm { realm -\u003e // Acquire default Realm with `Realm {}`\n        val persons = realm.where\u003cPerson\u003e().findAll()\n\n        val realmChangeListener = RealmChangeListener\u003cRealmResults\u003cPerson\u003e\u003e {\n            println(\"Change listener called\")\n        }\n        persons.addChangeListener(realmChangeListener) // Safe to add\n\n        // Make a transaction\n        realm.transact { // this: Realm\n            copyToRealm(Person())\n        }\n        \n        delay(500)  // Wait till change listener is triggered\n    } // Acquired Realm automatically closed\n}\n```\nNote that `RealmDispatcher` should be closed when no longer used to release resources. For automatic lifecycle handling via [Flow](https://kotlinlang.org/docs/flow.html), see below.\n\n#### Streams via Flow\n\n`Compass` provides extensions for easy conversions of queries to `Flow` and confirms to basic threading expectations of a `Flow`\n* Returned objects can be passed to different threads.\n* Handles `Realm` lifecycle until `Flow` collection is stopped.\n\n```kotlin\nval personsFlow = RealmQuery { where\u003cPerson\u003e() }.asFlow()\n```\nInternally `asFlow` creates a dedicated `RealmDispatcher` to run the queries and observe changes. The created dispatcher is automatically closed and recreated when collection stops/restarted. By default, all `RealmResults` objects are copied using `Realm.copyFromRealm`.\n\n##### Read subset of data.\n\nCopying large objects from Realm can be expensive in terms of memory, to read only subset of results to memory use `asFlow()` overload that takes a [transform](https://arunkumar9t2.github.io/compass/compass/dev.arunkumar.compass/index.html#-1272439111/Classlikes/1670650899) function.\n\n```kotlin\ndata class PersonName(val name: String)\n\nval personNames = RealmQuery { where\u003cPerson\u003e() }.asFlow { PersonName(it.name) }\n```\n\n#### Paging\n\n`Compass` provides extensions on `RealmQueryBuilder` to enable paging support. For example:\n\n```kotlin\nval pagedPersons = RealmQuery { where\u003cPerson\u003e() }.asPagingItems()\n```\n\n`asPagingItems` internally manages a `Realm` instance, run queries using `RealmDispatcher` and cleans up resources when `Flow` collection is stopped.\n\nFor reading only subset of objects into memory, use the `asPagingItems()` overload with a `transform` function:\n\n```kotlin\nval pagedPersonNames = RealmQuery { where\u003cPerson\u003e() }.asPagingItems { it.name }\n```\n\n##### ViewModel\n\nFor integration with ViewModel\n\n\n```kotlin\nclass MyViewModel: ViewModel() {\n\n    val results = RealmQuery { where\u003cTask\u003e() }.asPagingItems().cachedIn(viewModelScope)\n}\n```\n\nThe `Flow` returned by `asPagingItems()` can be safely used for [transformations](https://developer.android.com/topic/libraries/architecture/paging/v3-transform#transform-data-stream), [separators](https://developer.android.com/topic/libraries/architecture/paging/v3-transform#handle-separators-ui) and [caching](https://developer.android.com/topic/libraries/architecture/paging/v3-transform#avoid-duplicate). Although supported, for [converting to UI model](https://developer.android.com/topic/libraries/architecture/paging/v3-transform#convert-ui-model) prefer using `asPagingItems { /* convert */ }` as it is more efficient.\n\n##### Compose\n\nThe `Flow\u003cPagingData\u003cT\u003e\u003e` produced by `asPagingItems()` can be consumed by `Compose` with `collectAsLazyPagingItems()` from `paging-compose`:\n\n```kotlin\nval items = tasks.collectAsLazyPagingItems()\n  LazyColumn(\n    modifier = modifier.padding(contentPadding),\n  ) {\n    items(\n      items = items,\n      key = { task -\u003e task.id.toString() }\n    ) { task -\u003e taskContent(task) }\n  }\n```\n\n## FAQ\n\n#### Why not Realm Freeze?\n\n[Frozen](https://docs.mongodb.com/realm/sdk/android/advanced-guides/threading/#frozen-objects) Realm objects is the official way to safely move objects around threads. However it still says connected to underlying `Realm` and poses risk around threading.\n\n\u003e Frozen objects remain valid for as long as the realm that spawned them stays open. Avoid closing realms that contain frozen objects until all threads are done working with those frozen objects.\n\n`Compass`'s [transform](https://arunkumar9t2.github.io/compass/compass/dev.arunkumar.compass/index.html#-1272439111/Classlikes/1670650899) API supports both creating detached objects from `Realm` and reading subset of `Realm` object into memory.\n\nFor detailed comparison, see [here](https://arunkumar.dev/introducing-compass-effective-paging-with-realm-and-jetpack-paging-3/#comparison-to-official-apis).\n\n## Resources\n\n* [Introducing Compass: Effective Paging with Realm and Jetpack Paging 3](https://arunkumar.dev/introducing-compass-effective-paging-with-realm-and-jetpack-paging-3/)","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Farunkumar9t2%2Fcompass","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Farunkumar9t2%2Fcompass","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Farunkumar9t2%2Fcompass/lists"}