Skip to content

Instantly share code, notes, and snippets.

@michaelbukachi
Last active May 24, 2022 04:31
Show Gist options
  • Select an option

  • Save michaelbukachi/0c7f18bdeaf4bdb12b5527304d6cdbc0 to your computer and use it in GitHub Desktop.

Select an option

Save michaelbukachi/0c7f18bdeaf4bdb12b5527304d6cdbc0 to your computer and use it in GitHub Desktop.
Realm Coroutines
import io.realm.*
import io.realm.kotlin.where
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
private suspend fun <T: RealmObject, S: RealmQuery<T>> findAllAwait(query: S): RealmResults<T> = suspendCancellableCoroutine { continuation ->
val listener = RealmChangeListener<RealmResults<T>> { t -> continuation.resume(t) }
query.findAllAsync().addChangeListener(listener)
}
private suspend fun <T: RealmObject, S: RealmQuery<T>> findFirstAwait(query: S): T? = suspendCancellableCoroutine { continuation ->
val listener = RealmChangeListener { t: T? -> continuation.resume(t) }
query.findFirstAsync().addChangeListener(listener)
}
private suspend fun executeAsync(realm: Realm, block: (Realm) -> Unit): Unit = suspendCancellableCoroutine { continuation ->
realm.executeTransactionAsync({ block(it) }, { continuation.resume(Unit) }, { continuation.resumeWithException(it) })
}
suspend fun <S: RealmObject> RealmQuery<S>.await() = findAllAwait(this)
suspend fun <S: RealmObject> RealmQuery<S>.awaitFirst() = findFirstAwait(this)
suspend fun Realm.transactAwait(block: (Realm) -> Unit) = executeAsync(this, block)
class TestObject(val name: String? = "") : RealmObject()
fun test() {
GlobalScope.launch {
val realm = Realm.getDefaultInstance()
val result = realm.where<TestObject>().awaitFirst()
val results = realm.where<TestObject>().await()
realm.transactAwait(Realm.Transaction {
val testObject = TestObject(name = "Some Test")
it.copyToRealm(testObject)
})
}
}
@michaelbukachi
Copy link
Copy Markdown
Author

@rayel0915 I've corrected the snippet.

@kibotu
Copy link
Copy Markdown

kibotu commented Feb 14, 2020

even though it works perfectly fine, i found that copyToRealm is super slow, it takes up to 1500ms to copy 400 realm objects each time :/

also i feel like later on in a medium sized project realm is really slow mainly because of the need to copy entire realm objects

realm 7+ supports freeze() which deals a bit better with switching threads, do you think this solution can be applied?

i've played a round with it a bit but freezing still requires lazy loading of properties later on which is really slow if you have large realm objects, too :/

@michaelbukachi
Copy link
Copy Markdown
Author

@kibotu First time hearing about freeze(). I haven't visited their site in a while. Let me experiment with it and see what can be done with it.

@kibotu
Copy link
Copy Markdown

kibotu commented Mar 3, 2020

wouldn't it be better to also remove the listener when there is an exception? i'm not super strong with all coroutine conventions / features yet,
is there reason why it is not necessary?

another thing is that i believe it would be better to use inline functionality here as well

suspend inline fun <reified T : RealmObject, reified S : RealmQuery<T>> findAllAwait(query: S): RealmResults<T> = suspendCancellableCoroutine { continuation ->
    val listener = RealmChangeListener<RealmResults<T>> { t -> continuation.resume(t) }
    val results = query.findAllAsync()
    results.addChangeListener(listener)
    continuation.invokeOnCancellation {
        results.removeChangeListener(listener)
    }
}

suspend inline fun <reified T : RealmObject, reified S : RealmQuery<T>> findFirstAwait(query: S): T? = suspendCancellableCoroutine { continuation ->
    val listener = RealmChangeListener { t: T? -> continuation.resume(t) }
    val results = query.findFirstAsync()
    results.addChangeListener(listener)
    continuation.invokeOnCancellation {
        results.removeChangeListener(listener)
    }
}

suspend fun Realm.transactAwait(block: (Realm) -> Unit): Unit = suspendCancellableCoroutine { continuation ->
    executeTransactionAsync({ block(it) }, { continuation.resume(Unit) }, { continuation.resumeWithException(it) })
}

suspend inline fun <reified S : RealmObject> RealmQuery<S>.await() = findAllAwait(this)

suspend inline fun <reified S : RealmObject> RealmQuery<S>.awaitFirst() = findFirstAwait(this)

@michaelbukachi
Copy link
Copy Markdown
Author

Hi @kibotu. Thanks for pointing that out. It's important do cleanup after cancellation. Also, I created a library for this at https://github.com/michaelbukachi/realm-koroutines. Would nice if you could raise an issue over there.

@kibotu
Copy link
Copy Markdown

kibotu commented Mar 3, 2020

cool thanks for sharing

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment