0

I'm trying to experiment a proper way to convert a complete task into a sealed class easy to read when performing a get request on a document (at this time and I will see later for collections request).

import com.google.android.gms.tasks.Task
import com.google.firebase.firestore.DocumentSnapshot
import com.google.firebase.firestore.FirebaseFirestoreException
import timber.log.Timber

fun <T> Task<DocumentSnapshot?>.toDocumentResult(parser: (documentSnapshotExisting: DocumentSnapshot) -> T): DocumentResult<T>?{
    val documentResult: DocumentResult<T> = if(isSuccessful){
        val documentSnapshot: DocumentSnapshot = result!!
        if(documentSnapshot.exists()){
            try {
                DocumentResult.Found(parser.invoke(documentSnapshot))
            }
            catch (e: java.lang.Exception){
                DocumentResult.ParserException<T>(documentId = documentSnapshot.id, e = e)
            }
        }else{
            DocumentResult.NotFound(documentSnapshot.id)
        }
    }else{
        DocumentResult.Error(exception!! as FirebaseFirestoreException)
    }
    documentResult.log()
    return documentResult
}


sealed class DocumentResult<T>{
    abstract fun log()

    class Found<T>(val o: T): DocumentResult<T>() {
        override fun log() {
            Timber.tag("DocumentResult").w("$o")
        }
    }

    class NotFound<T>(val documentId: String): DocumentResult<T>() {
        override fun log() {
            Timber.tag("DocumentResult").w("documentId: $documentId doesn't exist")
        }
    }

    class ParserException<T>(val documentId: String, val e: Exception): DocumentResult<T>() {
        override fun log() {
            Timber.tag("DocumentResult").e("ParserException: ${e.localizedMessage?:e.message?:"error"}, documentId: $documentId")
        }
    }

    class Error<T>(val e: FirebaseFirestoreException): DocumentResult<T>() {
        override fun log() {
            Timber.tag("DocumentResult").e("FirebaseFirestoreException - code: ${e.code.name}, ${e.localizedMessage?:e.message?:"error"}")
        }
    }
}

With this snippet, I can do this :

activity.firestore.documentAvailableLanguages().get().addOnCompleteListener { task ->
    val documentResult = task.toDocumentResult { AvailableLanguages.toObject(it) }
    when(documentResult){
        is DocumentResult.Found -> { /* My converted object */ }
        is DocumentResult.NotFound -> {  /* document not found */}
        is DocumentResult.Error-> {  /* FirebaseFirestoreException */}
        is DocumentResult.ParserException -> { /* Conversion didn't work, exception */ }
    }
}

My question is :

1) Can we reasonably ensure that Task.exception is always not null and instance of FirebaseFirestoreException when isSuccessFul is false ?

2) Are we sure that task.result is always not null when task.isSuccessful is true ?

Thanks in advance

sokarcreative
  • 536
  • 7
  • 18

1 Answers1

0

For both questions, please note that a Task is "successful" when the work represented by the task is finished as expected, with no errors. On the orter side, a Task is "complete" when the work represented by the Task is finished, regardless of its "success" or "failure". There may be or may or may be not an error, you'll have to check for that.

An already successfully completed Task returns a DocumentSnapshot which will never have the value of null. If the requested document does not exist, you'll get an empty DocumentSnapshot object not null. This also means that if you'll call exists():

documentSnapshot.exists() //Will returns false

And if are calling getData() method:

documentSnapshot.getData() //An exception will be thrown

If the Taks is not "successful", the Exception that is trown by task.getException() is an instanceof FirebaseFirestoreException. Please note that Task's getException() method:

Returns the exception that caused the Task to fail. Returns null if the Task is not yet complete, or completed successfully.

Alex Mamo
  • 130,605
  • 17
  • 163
  • 193
  • Thanks for your clarification. So in the code above, it should works in any case on a addOnCompleteListener, right ? – sokarcreative Jan 29 '19 at 09:03
  • If you need to check for the Exception, yes. – Alex Mamo Jan 29 '19 at 09:05
  • since I use addOnCompleteListener (so the task is completed) and I check that task is not successful, he should return an exception, right ? "Returns the exception that caused the Task to fail. Returns null if the Task is not yet complete, or completed successfully." – sokarcreative Jan 29 '19 at 10:51
  • The [OnCompleteListener](https://developers.google.com/android/reference/com/google/android/gms/tasks/OnCompleteListener) and [OnSuccessListener](https://developers.google.com/android/reference/com/google/android/gms/tasks/OnSuccessListener) are two different interfaces. You need to use either one or the other one. – Alex Mamo Jan 29 '19 at 11:14