Can I somehow transport a `Deferred` out form a ca...
# coroutines
r
Can I somehow transport a
Deferred
out form a callback? got this pattern:
Copy code
kotlin
        val contract = ActivityResultContracts.StartActivityForResult()
        suspendCoroutine { cont ->
            registerForActivityResult(contract, contract.createIntent(this, client.signInIntent)) {
              <createing a Deferred here>
          }
        }
Can I somehow pull the
Deferred
directly into the
suspendCoroutine
, or do I have to do all the plumbing manually?
s
Can you elaborate on what you're trying to do here?
r
Full code:
Copy code
val client = GoogleSignIn.getClient(this, GoogleSignInOptions.DEFAULT_SIGN_IN)
        val contract = ActivityResultContracts.StartActivityForResult()
        suspendCoroutine { cont ->
            registerForActivityResult(contract, contract.createIntent(this, client.signInIntent)) {
                val account = GoogleSignIn.getSignedInAccountFromIntent(it.data)
                val def = account.asDeferred()
            }
        }
The
account
is a
Task
, which I can convert to a
Deferred
to avoid the plumbing from
Task
to coroutines, but since I also have the
registerForActivityResult
callback, I was wondering if I can reuse the plumbing.
s
Okay, I see, so in effect we have a callback inside a callback 👍
r
Yup. The inner callback is already in a coroutine, so I wondered if I can avoid plumbing ^^
s
A Task lets you register your own callbacks, so I think the best option would be to add a completion listener to the task that will resume the continuation. In other words, skip the
asDeferred
and just connect
account
directly to
cont
.
r
Also gotta collect cancellation and exceptions, right?
s
sure 👍 -- use
suspendCancellableCoroutine
in place of
suspendCoroutine
so you can hook up all those extra callbacks
r
Copy code
suspendCoroutine { cont ->
            registerForActivityResult(contract, contract.createIntent(this, client.signInIntent)) { activityResult ->
                val account = GoogleSignIn.getSignedInAccountFromIntent(activityResult.data)
                account.addOnCanceledListener { cont.resumeWithException(CancellationException()) }
                account.addOnCompleteListener { cont.resume(it) }
                account.addOnFailureListener { cont.resumeWithException(it) }
            }
        }
Aye
s
I mentioned
suspendCancellableCoroutine
but I think that is mostly relevant if you want cancellation to flow the other way, i.e. have the
Task
be cancelled when you cancel the suspended coroutine
I think your code looks good 👍
j
@Sam I think it's still important to have. If the
account
task supports cancellation, it should be done with
suspendCancellableCoroutine
.
r
Yup, fixed it
a
Isn't this enough?
Copy code
val contract = ActivityResultContracts.StartActivityForResult()
return coroutineScope.async {
    val result = suspendCoroutine { cont ->
        registerForActivityResult(contract, contract.createIntent(this, client.signInIntent)) {
            cont.resume(it)
        }
    }
    GoogleSignIn.getSignedInAccountFromIntent(result.data).await()
}
j
why the
coroutineScope.async
here?
a
Because the OP wants a
Deffered
.
r
Nah, not really. Implementation detail.
j
Given where the conversation went, I don't think deferred was a goal in itself
r
But yeah, await outside is nice. Thanks.
j
Awaiting outside would allow you to generalize the wrapping of
registerForActivityResult
into a suspend function. By the way, don't you need a
launch
call on the
ActivityResultLauncher
returned by
registerForActivityResult
?
r
Ah yea, too much Haskell, there you get a type error if you forgot that 😭