Error: Suspension functions can be called only within coroutine body for the code doing a remove and...
d
Error: Suspension functions can be called only within coroutine body for the code doing a remove and after a logout:
Copy code
suspend fun doLogout(token: String) {
    val userId = appService.currentUser?.id

        realm.write {

            var user = query<UserInfo>("_id = $0", userId).first().find()
            if (user != null) {
                val productIndex =
                    user.FCMToken.withIndex().findLast { it.value == token }!!.index

                user = findLatest(user)!!.also {
                    it.FCMToken.removeAt(productIndex)
                }
                copyToRealm(user)

                appService.currentUser?.logOut() // Suspension functions can be called only within coroutine body

            }
        }
}
Is there anyway to fix this without changing much code? I need to do first the db operation then logOut()
m
Copy code
suspend fun doLogout(token: String) {
    val userId = appService.currentUser?.id

        realm.write {

            var user = query<UserInfo>("_id = $0", userId).first().find()
            if (user != null) {
                val productIndex =
                    user.FCMToken.withIndex().findLast { it.value == token }!!.index

                user = findLatest(user)!!.also {
                    it.FCMToken.removeAt(productIndex)
                }
                copyToRealm(user)
            }
        }
    appService.currentUser?.logOut()
}
This should work as long as realm.write is not launching the code in another thread
d
It is
sometimes the logout happens first..
How can I avoid this?
m
This might work then
Copy code
suspend fun doLogout(token: String) {
    val userId = appService.currentUser?.id
    coroutineScope {
        realm.write {

            var user = query<UserInfo>("_id = $0", userId).first().find()
            if (user != null) {
                val productIndex =
                    user.FCMToken.withIndex().findLast { it.value == token }!!.index

                user = findLatest(user)!!.also {
                    it.FCMToken.removeAt(productIndex)
                }
                copyToRealm(user)

                launch { appService.currentUser?.logOut() }
            }
        }
    }
}
Or not, I forgot that coroutineScope wouldnt work well with realm.write if it launches another thread
d
Any solution?
m
Found a solution
Copy code
suspend fun doLogout(token: String) {
    val userId = appService.currentUser?.id
    val deferred = CompletableDeferred<Unit>()
        realm.write {

            var user = query<UserInfo>("_id = $0", userId).first().find()
            if (user != null) {
                val productIndex =
                    user.FCMToken.withIndex().findLast { it.value == token }!!.index

                user = findLatest(user)!!.also {
                    it.FCMToken.removeAt(productIndex)
                }
                copyToRealm(user)
            }
            deferred.complete(Unit)
        }
    deferred.await()
    appService.currentUser?.logOut()
}
d
Could you please explain a bit the logic?
m
CompleteableDeferred is an implementation of Deferred from async/await in Kotlin that has public methods to complete the Deferred. Calling Deferred.await() waits until the Deferred is complete and then allows execution to continue. And calling Deferred.complete with a value sets Deferred as complete, allowing execution to continue
m
I'm assuming that
ream.write
is a blocking call, in which case it should be run in a new coroutine (suspend functions should be main thread safe). So you would wrap the call to
realm.write
in a
withContext(<http://Dispatchers.IO|Dispatchers.IO> { }
And then make the call to
logOut
after the
withContext
returns. This is similar to the
CompletableDeferred
solution above, but I think more idiomatic.
If
realm.write
is launching it's own thread, then I would probably use the
suspendCoroutine
function or
suspendCancellableCoroutine
if realm gives you a way to cancel.
d
@Mitchell Syer Unfortunately logOut method still happens before the realm.write
m
Thats very unlikely unless something else is wrong. The code is setup to wait until realm.write's execution is finished. Maybe check/log if user is null or other similar issues
m
realm.write
is already suspending, so neither of our suggestions are needed
3667 Views