Reputation: 95
I want to create a signing activity in my app using Firebase phone authentication. The authentication has three phases:
PhoneAuthProvider.verifyPhoneNumber(options)
PhoneAuthProvider.getCredential(verificationId!!, code)
auth.signInWithCredential(credential)
I want to use Coroutines to handle the signing process. I know how to handle code verification using await()
, which wrap the Task
returned by auth.signInWithCredential(credential)
.
My problem is with PhoneAuthProvider.verifyPhoneNumber(options)
function which is a void
function. I must use callbacks to handle this method.
val options = PhoneAuthOptions.newBuilder(auth)
.setPhoneNumber(phoneNumber)
.setTimeout(60L, TimeUnit.SECONDS)
.setCallbacks(callback)
.build()
PhoneAuthProvider.verifyPhoneNumber(options)
where callbacks
is:
callbacks = object : PhoneAuthProvider.OnVerificationStateChangedCallbacks(){
override fun onVerificationCompleted(credential: PhoneAuthCredential) {
Timber.i("onVerificationCompleted:$credential")
signInWithPhoneAuthCredential(credential)
}
override fun onVerificationFailed(e: FirebaseException) {
Timber.i(e,"onVerificationFailed")
}
override fun onCodeSent(verificationId: String, token: PhoneAuthProvider.ForceResendingToken) {
Timber.i("onCodeSent:$verificationId")
storedVerificationId = verificationId
resendToken = token
}
}
The question is: is there a way to use await()
with verifyPhoneNumber
function?
Otherwise, how can I use coroutines with callbacks to block the function until callbacks triggered?
Upvotes: 0
Views: 908
Reputation: 846
You can use the below solution for this problem
First of all, you must define an interface FcmService
interface FcmService {
suspend fun initFcmToken() : Flow<String>
}
for next section define FcmServiceImpl
class FcmServiceImpl : FcmService {
override suspend fun initFcmToken(): Flow<String> =
callbackFlow {
FirebaseMessaging.getInstance().token.addOnCompleteListener(
OnCompleteListener { task ->
if (!task.isSuccessful) {
Logger.error(
"Device Manager" +
"Fetching FCM registration token failed" +
task.exception
)
return@OnCompleteListener
}
if (task.isComplete) {
trySend(task.result)
}
})
awaitClose { cancel() }
}
}
and then for use
fcmService.initFcmToken().collectLatest {
// your token is here
}
I hope help
Upvotes: 0
Reputation: 196
You can use suspendCoroutine to wrap Firebase callback into a coroutine suspend function like this:
sealed class PhoneAuthResult {
data class VerificationCompleted(val credentials: PhoneAuthCredential) : PhoneAuthResult()
data class CodeSent(val verificationId: String, val token: PhoneAuthProvider.ForceResendingToken)
: PhoneAuthResult()
}
private suspend fun performPhoneAuth(
phoneNumber: String,
firebaseAuth: FirebaseAuth): PhoneAuthResult =
suspendCoroutine { cont ->
val callback = object : PhoneAuthProvider.OnVerificationStateChangedCallbacks() {
override fun onVerificationCompleted(credential: PhoneAuthCredential) {
Timber.i("onVerificationCompleted:$credential")
cont.resume(
PhoneAuthResult.VerificationCompleted(credential)
)
}
override fun onVerificationFailed(e: FirebaseException) {
Timber.i(e, "onVerificationFailed")
cont.resumeWithException(e)
}
override fun onCodeSent(verificationId: String, token: PhoneAuthProvider.ForceResendingToken) {
Timber.i("onCodeSent:$verificationId")
cont.resume(
PhoneAuthResult.CodeSent(verificationId, token)
)
}
}
val options = PhoneAuthOptions.newBuilder(firebaseAuth)
.setPhoneNumber(phoneNumber)
.setTimeout(60L, TimeUnit.SECONDS)
.setCallbacks(callback)
.build()
PhoneAuthProvider.verifyPhoneNumber(options)
}
Upvotes: 6