'Need help to convert this chunk of Google Sign in and registration code from fragment to viewModel for fragment so I can follow the MVVM

I need to convert the below chunk of codes to my viewModel so that I can follow the MVVM. I did a lot of googling and see some so; solutions in StackOverflow which never worked for me and also not for Kotlin especially.

here is the fragment code with all the google authentication code in it:

class LoginFragment : Fragment() {

private var _binding: FragmentLoginBinding? = null
private val binding get() = _binding!!
private val scope = CoroutineScope(CoroutineName("loginFragmentScope"))
private val viewModel by viewModels<LoginViewModel>()

private lateinit var googleSignInClient: GoogleSignInClient

companion object {
    const val TAG = "LoginFragment"
    const val RC_SIGN_IN = 9139;
}

override fun onCreateView(
    inflater: LayoutInflater, container: ViewGroup?,
    savedInstanceState: Bundle?
): View? {

    _binding = FragmentLoginBinding.inflate(inflater, container, false)
    return binding.root
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)

    binding.btnContinueWithGoogle.setOnClickListener {
        UiUtils.showProgress(requireContext())
        requestGoogleSignIn()
        signIn()
        UiUtils.hideProgress()
    }

}

private fun requestGoogleSignIn() {
    val gso = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
        .requestIdToken(getString(R.string.default_web_client_id))
        .requestEmail()
        .build()

    googleSignInClient = GoogleSignIn.getClient(requireActivity(), gso)
}

private fun signIn() {
    val signInIntent = googleSignInClient.signInIntent
    startActivityForResult(signInIntent, RC_SIGN_IN)
}

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)
    UiUtils.showProgress(requireContext())

    if (requestCode == RC_SIGN_IN) {
        val task = GoogleSignIn.getSignedInAccountFromIntent(data)
        try {

            val account = task.getResult(ApiException::class.java)!!
            Log.d(TAG, "firebaseAuthWithGoogle:" + account.id)
            firebaseAuthWithGoogle(account.idToken!!)
        } catch (e: ApiException) {

            Log.w(TAG, "Google sign in failed", e)
            UiUtils.hideProgress()
            UiUtils.showErrorSnackBar(requireView(), "Google sign in failed", 0)

        }
    }
}

private fun firebaseAuthWithGoogle(idToken: String) {
    val credential = GoogleAuthProvider.getCredential(idToken, null)
    FirestoreAuth().auth.signInWithCredential(credential)
        .addOnCompleteListener(requireActivity()) { task ->
            if (task.isSuccessful) {

                Log.d(TAG, "signInWithCredential:success")
                FirestoreAuth().getUserDetails { user ->
                    FirestoreAuth().registerUserWithEmailAndPassword(user.email, "islamic123") {
                        if (it) {
                            updateUi(FirestoreAuth().auth.currentUser)
                        }
                        UiUtils.hideProgress()
                    }
                    DataStoreUtil(requireActivity()).storeInitialData(
                        user.userId,
                        user.firstName
                    )
                    updateUi(FirestoreAuth().auth.currentUser)
                }

            } else {

                Log.w(TAG, "signInWithCredential:failure", task.exception)
                UiUtils.hideProgress()
            }
        }
}

private fun updateUi(user: FirebaseUser? = null) {
    if (user == null) {
        Log.i(TAG, "User is Null!! Failed!")
        return
    }
    startActivity(Intent(activity, TestActivity::class.java))
}


Solution 1:[1]

I guess it makes way more sense to have your logic like this:

View: onBtnClick(viewmodel.signIn())

Viewmodel: signIn() { FirebaseConnectorClass.doSignIn() }

FirebaseConnectorClass {

firebaseAuthWithGoogle()

}

EDIT: To break it down very simple, with MVVM you want to divide your UI from the backend repositories.
The main goal is not to freeze the UI while doing some heavy lifting in the backend.
Therefore you have your view (UI) trigger an event in the viewModel. The viewModel then asks the backend to do the given task. When the backend is finished, it informs the viewModel and the viewModel updates the view (UI). This way your UI stays responsive all the time.

Checkout the links of Alex Mamo above to get a better understanding.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1