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
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.