Search code examples
androidfirebasekotlingoogle-cloud-firestoresealed-class

Firebase: How to check if document write was successful


I want to check if my database write was successful in order to show the user an error message.

My current approach doesn't work as it says "Type mismatch, required Unit found EmailStatus"

Current approach

class EmailRepositoryImpl : EmailRepository {
    private val db = Firebase.firestore

    override fun sendEmail(email: Email): EmailStatus<Nothing> {
        db.collection("emails").document().set(email).addOnCompleteListener {
            if (it.isSuccessful) return@addOnCompleteListener EmailStatus.Success<Nothing>
            if (it.isCanceled) return@addOnCompleteListener EmailStatus.Error(it.exception!!)
        }
    }
}

Status Sealed Class

sealed class EmailStatus<out T> {
    data class Success<out T>(val data: T) : EmailStatus<T>()
    data class Error(val exception: Exception) : EmailStatus<Nothing>()
}

Is it even possible to write something like this? As far as I know there is a generic firebase error type but I didn't found anything related to kotlin or android...

I appreciate every help, thank you

Edit

I've tried getting my document, but I am just getting null: (When I use the listener approach, everything works fine)

Interface

interface EmailRepository {
    suspend fun getEmail(): Flow<EmailEntity?>
}

Interface Implementation

override suspend fun getEmail(): Flow<EmailEntity?> = flow {
    val result = db.collection("emailprice").document("Email").get().await()
    emit(result.toObject<EmailEntity>())
}

ViewModel

private val emailEntity = liveData<EmailEntity?>(Dispatchers.IO) {
    emailRepository.getCalibratePrice()
}

Solution

  • Okay, this is the final solution, thanks to @Gastón Saillén and @Doug Stevenson :

    EmailRepository

    interface EmailRepository {
        fun sendEmail(email: Email): Flow<EmailStatus<Unit>>
    }
    

    EmailRepository Implementation

    class EmailRepositoryImpl @Inject constructor(
        private val db: FirebaseFirestore
    ) : EmailRepository {
    
        override fun sendEmail(email: Email)= flow<EmailStatus<Unit>> {
            db.collection("emails").add(email).await()
            emit(EmailStatus.success(Unit))
        }.catch {
            emit(EmailStatus.failed(it.message.toString()))
        }.flowOn(Dispatchers.Main)
    
    }
    

    ViewModel

    fun sendEmail(): LiveData<EmailStatus<Unit>> {
        val newEmail = createEmail()
        return emailRepository.sendEmail(newEmail).asLiveData()
    }
    

    Fragment

    btn.setOnClickListener {
                viewModel.sendEmail().observe(viewLifecycleOwner) {
                    when(it) {
                        is EmailStatus.Success -> {
                            valid = true
                            navigateTo(next, bundleNext)
                            Toast.makeText(requireContext(), "Success", Toast.LENGTH_SHORT).show()
                        }
                        is EmailStatus.Failure -> {
                            valid = false
                            Toast.makeText(requireContext(), "Failed ${it.message}", Toast.LENGTH_SHORT).show()
                        }
                    }
                }
    }
    

    The only problem I currently have is that my "faield state" does not work like it should.

    It should fail, if the user has no internet access. Currently, the write to the db never fails and Firebase just waits till the user has internet access. The problem here is that when I click multiple times, the write is executed multiple times. But I think I have to implement a bit more logic here and the above written code is fine like it currently is.