Kotlin-friendly extensions for Couchbase Lite Android and Java SDKs.
Proudly made by MOLO17 Srl 🚀
Couchbase Lite Kotlin is a lightweight library that adds convenient extension functions to Couchbase Lite Android and Java SDKs.
This library introduces a lightweight wrapper on top of N1QL query language, Document creation and much more. Also, it provides support for a more fluent listener API, introducing first-class support for Kotlin Coroutines and the Flow API.
Add JitPack as repository for your project:
allprojects {
repositories {
...
maven { url "https://jitpack.io" }
}
}
And then to your module build.gradle
file:
implementation "com.github.MOLO17.couchbase-lite-kotlin:kotlin:1.2.1"
For Android use
implementation "com.github.MOLO17.couchbase-lite-kotlin:android-ktx:1.2.1"
Here are the main features that Couchbase Lite Kotlin provides for boosting the integration of the Couchbase Lite SDK with Kotlin.
- QueryBuilder extensions
- Document builder DSL
- Query Flow support
- ResultSet model mapping
- Database extensions
- Replicator extensions
Syntax for building a query has gone more straight-forward thanks to the
infix
function support.
select(all()) from database where { "type" equalTo "user" }
Or just a bunch of fields:
select("name", "surname") from database where { "type" equalTo "user" }
You can even do more powerful querying:
select("name", "type")
.from(database)
.where {
("type" equalTo "user" and "name" equalTo "Damian") or
("type" equalTo "pet" and "name" like "Kitt")
}
.orderBy { "name".ascending() }
.limit(10)
For creating a new MutableDocument
ready to be saved, you can now use
a new Kotlin DSL:
val document = MutableDocument {
"name" to "Damian"
"surname" to "Giusti"
"age" to 24
"pets" to listOf("Kitty", "Kitten", "Kitto")
"type" to "user"
}
database.save(document)
Now Couchbase Lite queries become Flows. Couchbase Lite Kotlin ports the coroutines Flow stream API for observing Live Queries results.
select(all())
.from(database)
.where { "type" equalTo "user" }
.asFlow()
.collect { value: ResultSet ->
// consume ResultSet
}
As plus, you receive automatic LiveQuery cancellation when the Flow tears down.
Thanks to Map delegation, mapping a ResultSet to a Kotlin class has never been so easy.
The library provides the ResultSet.toObjects()
and Query.asObjectsFlow()
(with android version Query.asKtxObjectsFlow()
)
extensions for helping to map results given a factory lambda.
Such factory lambda accepts a Map<String, Any?>
and returns an instance
of a certain type. Those requirements fits perfectly with a Map-delegated
class.
class User(map: Map<String, Any?>) {
val name: String by map
val surname: String by map
val age: Int by map
}
val users: List<User> = query.execute().toObjects(::User)
class User(map: Map<String, Any?>) {
val name: String by map
val surname: String by map
val age: Int by map
}
val users: Flow<List<User>> = query.asObjectsFlow(::User)
As seen with the Query extensions, the Database has also been powered up.
You can now observe the DatabaseChange
and DocumentChangeEvents
using
the Kotlin Flow API.
val changes: Flow<DatabaseChange> = database.changesFlow()
val documentChanges: Flow<DocumentChange> = database.documentChangesFlow(docId)
Also, a new useful syntax has been introduced when performing batch operations:
database.doInBatch {
save(document)
delete(otherDocument)
}
The Database
is used as receiver in the specified lambda.
Replicator syntax has been boosted with the Kotlin Flow API. You can observe events using the following Flows:
val changesFlow: Flow<ReplicatorChange> = replicator.changesFlow()
val replicationFlow: Flow<DocumentReplication> = replicator.documentReplicationFlow()
In addition, for Android users, you can now bind the Replicator
start()
and stop()
methods to be performed automatically when your
Lifecycle-enabled
component gets resumed or paused.
// Binds the Replicator to the Application lifecycle.
replicator.bindToLifecycle(ProcessLifecycleOwner.get().lifecycle)
// Binds the Replicator to the Activity/Fragment lifecycle.
{
// inside an Activity or a Fragment ...
replicator.bindToLifecycle(lifecycle)
}
That's it! Replicator will be automatically started when your component
passes the ON_RESUME
state, and it will be stopped when the component
passes the ON_PAUSED
state. As you may imagine, no further action will
be made after the ON_DESTROY
state.