-
-
Save michaelbukachi/0c7f18bdeaf4bdb12b5527304d6cdbc0 to your computer and use it in GitHub Desktop.
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) | |
}) | |
} | |
} |
@kibotu just use the usual try-catch
statement to catch exceptions
e.g
try {
val results = realm.where<TestObject>().await()
} catch(e: Exception) {
// handle exception
}
Since the scope the of the listener is local, It will be destroyed and garbage collected as soon as the results are disposed.
When I run this code, I encounter this
java.lang.IllegalStateException: `copyOrUpdate` can only be called inside a write transaction.
@rayel0915 I've corrected the snippet.
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 :/
@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.
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)
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.
cool thanks for sharing
when would you remove the listener?
and how do you deal with errors?