简体   繁体   English

注册用户并添加到 Firestore

[英]Sign up user and add to firestore

I have a function that signs up a user and stores them in the Firestore database.我有一个 function 注册用户并将其存储在 Firestore 数据库中。 For now, everything is happening inside the fragment but I would like to move it to the repo and connect via the view model.现在,一切都在片段内部发生,但我想将它移动到 repo 并通过视图 model 连接。

 fun signUp(){
    val username = binding.signUpUsername.editText!!.text.toString()
    val email = binding.singUpEmail.editText!!.text.toString()
    val password = binding.signUpPassword.editText!!.text.toString()

    auth.createUserWithEmailAndPassword(email, password)
        .addOnCompleteListener { task ->
            if (task.isSuccessful){
                task.result.user!!.sendEmailVerification().addOnCompleteListener{
                    val newUser = User(uid = task.result.user!!.uid, username=username)
                    lifecycleScope.launch{
                        repository.addUser(newUser)
                    }.invokeOnCompletion {
                        Log.d("sign_up", "Your registration is successful: ${task.result.user!!.uid}")
                        Toast.makeText(this, "Account has been created. Confirm your email.", Toast.LENGTH_SHORT).show()
                        auth.signOut()
                        startActivity(Intent(this, LoginMainActivity::class.java))
                        finish()
                    }
                }

            } else {
                Log.d("sign_up", task.exception.toString())
            }
    }
}
 suspend fun addUser(user: User) {
    db.collection(Constants.USERS).document(user.uid).set(user).await()
}

My question is how to invoke suspend function addUser() inside my repo.我的问题是如何在我的仓库中调用暂停 function addUser() Can I just create CoroutineScope inside my main function?我可以在我的主 function 中创建 CoroutineScope 吗? Is it acceptable?可以接受吗? I would like to do it correctly but I haven't found any solution.我想正确地做到这一点,但我还没有找到任何解决方案。

My question is how to invoke suspend function addUser() inside my repo.我的问题是如何在我的仓库中调用暂停 function addUser() 。

Since you call .await() , your addUser() function it's mandatory to be defined as a suspend function.由于您调用.await() ,因此您的addUser() function 必须定义为suspend function。 According to the official documentation :根据官方文档

Suspending functions can be used inside coroutines just like regular functions, but their additional feature is that they can, in turn, use other suspending functions to suspend the execution of a coroutine.挂起函数可以像常规函数一样在协程中使用,但它们的附加特性是它们可以反过来使用其他挂起函数来挂起协程的执行。

This basically means that a suspend function can only be called from within a coroutine body or from another suspend function.这基本上意味着暂停 function只能从协程主体内或从另一个暂停 function 调用。

Can I just create CoroutineScope inside my main function?我可以在我的主 function 中创建 CoroutineScope 吗? Is it acceptable?可以接受吗?

No. As I understand from your question, you want to use a ViewModel along with a repository class.不。我从您的问题中了解到,您希望将 ViewModel 与存储库 class 一起使用。 That being said, you have to move the signUp() function inside the repository class and leave all the staff that is related to the views inside your fragment class.话虽如此,您必须将signUp() function 移动到存储库 class 中,并将所有与视图相关的人员留在片段 class 中。 Now I think that all it remains to be done is to call the addUser() function.现在我认为剩下要做的就是调用addUser() function。

The simplest solution to solving this problem would be to define a sealed class that looks like this:解决此问题的最简单解决方案是定义一个密封的 class,如下所示:

sealed class Response<out T> {
    object Loading: Response<Nothing>()

    data class Success<out T>(
        val data: T
    ): Response<T>()

    data class Failure<out T>(
        val errorMessage: String
    ): Response<T>()
}

Then the signUp() function should also be defined as a suspending function and the most important thing is to return a Flow :然后signUp() function 也应该定义为暂停 function 最重要的是返回一个Flow

fun signUpInFirebase(email: String, password: String) = flow {
    emit(Response.Loading)
    val user = auth.createUserWithEmailAndPassword(email, password).await().user
    val newUser = User(uid = user?.uid, username=user?.displayName)
    addUser(newUser) //👈
    emit(Response.Succes(true))
}.catch { error ->
    error.message?.let { errorMessage ->
        emit(Response.Failure(errorMessage))
    }
}

Since this function is now suspendable, it can only be called from another suspend function, which is not the case, or from a coroutine body.由于这个 function 现在是可挂起的,因此只能从另一个挂起 function 调用它,而事实并非如此,或者从协程体调用。 Since the ViewModel is placed in between your views and the repository, it makes sense to launch a coroutine in the ViewModel class using a viewModelScope that exists inside the:由于 ViewModel 位于视图和存储库之间,因此使用存在于以下位置的viewModelScope在 ViewModel class 中启动协程是有意义的:

implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1"实现“androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1”

To call this method from the ViewModel class, the following lines of code are needed:要从 ViewModel class 调用此方法,需要以下代码行:

fun signUp(email: String, password: String) = liveData(Dispatchers.IO) {
    repository.signUpInFirebase(email, password).collect { response ->
        emit(response)
    }
}

And inside your fragment:在你的片段里面:

val email = binding.singUpEmail.editText!!.text.toString().trim()
val password = binding.signUpPassword.editText!!.text.toString().trim()

private fun signUp(email, password) {
    viewModel.signUp(email, password).observe(this, { response ->
        when(response) {
            is Response.Loading -> {
                //Show ProgressBar
            }
            is Response.Success -> {
                    if(response.data) {
                        Log.d("TAG", "You're successfully authenticated.")
                    }
            }
            is Response.Failure -> {
                //Dismiss ProgressBar
                Log.d("TAG", "Failed with: " + response.errorMessage))
            }
        }
    })
}

That's pretty much of it!差不多就是这样!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM