Home > Enterprise >  Why it says "lateinit property resendToken has not been initialized" for jetpack compose p
Why it says "lateinit property resendToken has not been initialized" for jetpack compose p

Time:06-26

I have phone authentication project in android jetpack compose, so I can do it in my code and I am success to get OTP code from firebase, but I want to use resend code again in my project, so when I click this line of code;

 .clickable { modelAuthentication.resendCode(phoneNumberOTP)}
         

it is throw an error like "lateinit property resendToken has not been initialized", I am not get it what I missed, any idea?

Phone.kt:

@Composable
fun PhoneScreen(

    navController: NavController,
    modelAuthentication: AuthenticationViewModel

    ) {
 val phoneNumberState = remember { mutableStateOf("") }


        OutlinedTextField(
            value = phoneNumberState.value,
            colors = TextFieldDefaults.textFieldColors(
                backgroundColor = white,
                focusedIndicatorColor = Grey,
                unfocusedIndicatorColor = Grey,
                focusedLabelColor = Grey,
                unfocusedLabelColor = Grey,
                cursorColor = color,
                textColor = color,

                ),
            onValueChange = { phoneNumberState.value = it },
            label = { Text(text = "Phone Number") },
            placeholder = { Text(text = "Phone Number") },
            singleLine = true,
        
   
        )
      Button(
                modifier = Modifier
                    .width(285.dp)
                    .height(55.dp)

                ,
                onClick = {
                    modelAuthentication.send(phoneNumberState.value)
                  
             
                },
                colors = ButtonDefaults.buttonColors(
                    backgroundColor = color
                ),
                shape = RoundedCornerShape(40),
               

            ) {
                Text(
                    text = "send",
                    style = TextStyle(
                        fontSize = 18.sp,
                        color = white,


                        )

                )
            }

PhoneVerify.kt

@Composable
fun PhoneVerifyScreen(
    navController: NavController,
    modelAuthentication: AuthenticationViewModel,
   
) {
    lateinit var resendToken: PhoneAuthProvider.ForceResendingToken
    val focusManager = LocalFocusManager.current
    val phoneNumberPatientOTP = remember { mutableStateOf("") }
   
    val context = LocalContext.current
    LaunchedEffect(Unit) {
        println("found activity? ${context.findActivity()}")
        val activity = context.findActivity() ?: return@LaunchedEffect
        modelAuthentication.setActivity(activity)
    }


    Column(
        Modifier.fillMaxSize(),
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {

            OutlinedTextField(
                value = phoneNumberOTP.value,
                colors = TextFieldDefaults.textFieldColors(
                    backgroundColor = white,
                    focusedIndicatorColor = Grey,
                    unfocusedIndicatorColor = Grey,
                    focusedLabelColor = Grey,
                    unfocusedLabelColor = Grey,
                    cursorColor = color,
                    textColor = color,

                    ),
                onValueChange = { phoneNumberOTP.value = it },
                label = { Text(text = "Verify code") },
                placeholder = { Text(text = "Verify code") },
                modifier = Modifier.fillMaxWidth(0.8f),

                )
        }

        Spacer(modifier = Modifier.padding(7.dp))

        Row(
            Modifier
                .width(300.dp),
            horizontalArrangement = Arrangement.Center,
            verticalAlignment = Alignment.CenterVertically,

            ) {

            Text(
                "Resend Code",
                modifier = Modifier
                    .height(20.dp)
                    .clickable {
                        
                        modelAuthentication.resendCode(phoneNumberOTP)


                    }
                ,
                textAlign = TextAlign.End,
                fontWeight = FontWeight.Medium,
                fontSize = 14.sp,
                color = color
            )
        } }

ViewModel.kt

    @HiltViewModel
    class AuthenticationViewModel @Inject constructor(
    
        private val auth: FirebaseAuth
    
        ) : ViewModel() {
    
        lateinit var otp: String
    
        var verificationOtp = ""
        var popNotification = mutableStateOf<Event<String>?>(null)
    
        private lateinit var baseBuilder: PhoneAuthOptions.Builder
    
        fun setActivity(activity: Activity) {
            baseBuilder = PhoneAuthOptions.newBuilder(auth).setActivity(activity)
        }
    
        lateinit var resendToken: PhoneAuthProvider.ForceResendingToken
    
      val mCallBack =  object : PhoneAuthProvider.OnVerificationStateChangedCallbacks() {
override fun onVerificationCompleted(p0: PhoneAuthCredential) {
    handledException(customMessage = "Verification Completed")

}

override fun onVerificationFailed(p0: FirebaseException) {
    handledException(customMessage = "Verification Failed")

}

override fun onCodeSent(otp: String, p1: PhoneAuthProvider.ForceResendingToken) {
    super.onCodeSent(otp, p1)
    verificationOtp = otp
    resendToken = p1
    handledException(customMessage = "Otp Send Successfully")
}}

fun sendVerificationCode(mobileNum: String) {
    val options = baseBuilder
        .setPhoneNumber(mobileNum)
        .setTimeout(5L, TimeUnit.SECONDS)
        .setCallbacks(mCallBack)
        .build()
    PhoneAuthProvider.verifyPhoneNumber(options)
}
    
    
        fun signInWithPhoneAuthCredential(otp: String) {
            val credential = PhoneAuthProvider.getCredential(verificationOtp, otp)
            auth.currentUser?.linkWithCredential(credential)
            FirebaseAuth.getInstance().signInWithCredential(credential)
                .addOnCompleteListener { task ->
                    if (task.isSuccessful) {
                        handledException(customMessage = "Verification Successful")
    
                    } else {
                        handledException(customMessage =  "Wrong Otp")
    
                    }
                }
        }
    
    
        
    
        private fun handledException(exception: Exception? = null, customMessage: String = "") {
            exception?.printStackTrace()
            val errorMsg = exception?.message ?: ""
            val message = if (customMessage.isEmpty()) {
                errorMsg
            } else {
          

      "$customMessage: $errorMsg"
        }
        popNotification.value = Event(message)
    }


    }

CodePudding user response:

Modify your viewmodel looks like this

1 Define the callback on the top

val mCallBack =  object : PhoneAuthProvider.OnVerificationStateChangedCallbacks() {
override fun onVerificationCompleted(p0: PhoneAuthCredential) {
    handledException(customMessage = "Verification Completed")

}

override fun onVerificationFailed(p0: FirebaseException) {
    handledException(customMessage = "Verification Failed")

}

override fun onCodeSent(otp: String, p1: PhoneAuthProvider.ForceResendingToken) {
    super.onCodeSent(otp, p1)
    verificationOtp = otp
    resendToken = p1
    handledException(customMessage = "Otp Send Successfully")
}}

2 Update the send function

fun sendVerificationCode(mobileNum: String) {
    val options = baseBuilder
        .setPhoneNumber(mobileNum)
        .setTimeout(5L, TimeUnit.SECONDS)
        .setCallbacks(mCallBack)
        .build()
    PhoneAuthProvider.verifyPhoneNumber(options)
}

3 Remove the resend function

Note: p1 parameter is the Resend token

If you want resend verification, call the same function (sendVerificationCode)

CodePudding user response:

This, in your ViewModel

lateinit var resendToken: PhoneAuthProvider.ForceResendingToken

is a variable that hasn't been initialised to a value. You're not usually allowed to do that - you have to initialise it with something - but the lateinit keyword allows you to initialise it later. You're making a promise that you'll set a value on it before something tries to read that value. The error message you're getting, that resendToken has not been initialized means you've broken that promise!

You're initialising it in one of your callbacks:

override fun onCodeSent(
    verificationId: String,
    token: PhoneAuthProvider.ForceResendingToken,
) {
    ...
    resendToken = token
}

but the problem is this is running after you've tried to use resendToken in the resendCode function, which you call when you click the button. You need a value for resendToken before you can do this:

.setForceResendingToken(resendToken)

That's just a general explanation of the problem - I haven't used the Firebase stuff you're working with, so I can't tell you the best way to fix it. But you're calling that PhoneAuthOptions builder with a token you don't have yet, which is returned by the callbacks you're also providing to that builder. To me, that implies you have to be able to do the verifyPhoneNumber call without a token, since that's the only way you get one?

So maybe make resendToken nullable instead of lateinit, and check if it's null before adding it to the builder:

var resendToken: PhoneAuthProvider.ForceResendingToken? = null

fun resendCode(mobileNum: MutableState<String>) {
    val options = PhoneAuthOptions.newBuilder(auth).apply {
        setPhoneNumber(mobileNum.toString())
        setTimeout(60L, TimeUnit.SECONDS)
        setCallbacks(callbacks)
        // only set this on the builder if we have it!
        resendToken?.let { setForceResendingToken(it) }
    }.build()
    PhoneAuthProvider.verifyPhoneNumber(options)
}

(I'm assuming the builder methods return the same Builder instance so we can just call apply on it and the code is neater.) I don't know if that actually works, but I'm just giving you ideas for stuff to try

  • Related