0

I'm working on the functionality for my app where I send user input to Firestore. When I add an onCompleteListener to collectionReference.add(myObj), I get a Task<DocumentReference>:

collectionReference.add(userObj).addOnCompleteListener(new OnCompleteListener<DocumentReference>() {
  @Override
  public void onComplete(@NonNull @org.jetbrains.annotations.NotNull Task<DocumentReference> task) {
    if(task.isSuccessful()) {

    }  
  }
});

However, when I add an onSuccessListener, I get a DocumentReference:

collectionReference.add(userObj).addOnSuccessListener(new OnSuccessListener<DocumentReference>() {
  @Override
  public void onSuccess(@NonNull @org.jetbrains.annotations.NotNull DocumentReference documentReference) {
  }
});

I've read this post: How do I know whether to use OnComplete or OnSuccess? but all I really got from it is that onCompleteListener's don't check if a task has failed or not, so you have to say: if(task.isSuccessful){//do stuff}

What are the differences between Task<DocumentReference> and DocumentReference? How are onSuccess and onCompleteListener's different in Firebase, and when should I use either?

Finally, considering that an onCompleteListener just requires extra work of testing whether the task is successful or not, why would I use an onCompleteListener rather than an onSuccessListener in Firebase?

Thank you!

Nagaraj Tantri
  • 5,172
  • 12
  • 54
  • 78
CodingChap
  • 1,088
  • 2
  • 10
  • 23
  • I think all your doubts are part of the answers to that question you referred. For instance, when to use OnCompleteLister over OnSuccessListener is answered by [this](https://stackoverflow.com/a/49438419/308565) and for what are the differences of `Task` and just `Df` is basically for on complete -`this puts the success and failure code close together`, you need to evaluate the status of a task on its success or failure. Not sure if there are more doubts if it's not giving out any specific info. – Nagaraj Tantri May 10 '21 at 11:43

2 Answers2

2

Personally I would say using onCompleteListener is better than using onSuccessListener, there is no guarantee that the task will be successful and using the onCompleteListener will allow you to handle errors, whereas onSuccessListener will not be called at all and you won't know why.

The differences between Task<DocumentReference> and DocumentReference is that the Task version is a Firebase component that represents an asynchronous task. When you call collectionReference.add(userObj) you get a Task<DocumentReference> that is incomplete, and by adding a onCompleteListener, the Task<DocumentReference> you receive in the callback will be the completed task. That's why, checking for Task.isComplete() will be unnecessary inside onCompleteListener, and you can simply check Task.isSuccessful() instead, and if it's not successful, it probably contains the exception that you can get with Task.getException().

If the task is successful, Task.getResult() will point you to the DocumentReference that you want to use. The reference you acquire will be the same if you add onSuccessListener to the task itself.

And, regarding to why onCompleteListener is better to me: Handling errors is one of the biggest requirements in order to display a proper message, or popup etc. to the user if something goes wrong. It wouldn't make sense if you just print the exception itself, but putting a meaningful message such as "You don't have internet connection" will provide a better user experience. Handling exceptions will not be possible on onSuccessListener since it will simply not be fired if something goes wrong, hence, using onCompleteListener will allow you to properly complete the task, with its success or failure result.

Furkan Yurdakul
  • 2,801
  • 1
  • 15
  • 37
  • Firstly, thank you for your response! However, I'm a little confused with this: "Personally I would say using onCompleteListener is better than using onSuccessListener, there is no guarantee that the task will be successful and using the onCompleteListener will allow you to handle errors, whereas onSuccessListener will not be called at all and you won't know why." Regarding the onSuccessListener portion, couldn't you add an onFailureListener which will carry out an event on the case of failure--such as the you don't have internet connection example! Thanks. – CodingChap May 10 '21 at 15:14
2

It's not that one approach is better than the other.

If you are using an OnCompleteListener, there will always be a guarantee that you'll have either a result (the data) or an Exception, but you'll have to check that. How? By calling "isSuccessful()" method on the Task object, case in which you are guaranteed to have a result object and no Exception. It's always one, or the other.

On the other hand, if you are using an OnSuccessListener, there will always be a guarantee that the result (the data) object won't be null. However, it will not get invoked if there is an error. But this doesn't mean that we cannot handle errors, because you can chain a OnFailureListener, right after that and there will always be a guarantee that the Exception object won't be null, but it will not get invoked if there is no error.

So you can use OnSuccessListener and OnFailureListener, only if you don't want to check if the Task is successful inside the OnCompleteListener.

Please also note that in case of a loss of network connectivity (there is no network connection on the user device), neither "onSuccess()", nor "onFailure()" methods that exist in the above interfaces are triggered. So Firestore SDK doesn't throw an Exception when there is no internet connection, and it makes sense since Firestore is designed to work offline. Behind the scenes, Firestore SDK tries to reconnect until the devices regain connectivity. Remember that we can consider that the work is done only when the data has been committed (or rejected) on the Firebase server. So you'll never get an Exception because you get offline.

Furthermore, there is also an OnCanceledListener which is called when the Task is canceled successfully, which means that every Task has a "cancel()" method and once this method completes successfully, this listener is invoked.

In conclusion, a Task is can be considered complete when the work represented by the Task is finished, regardless of its success or failure. There may or may not have been an error, and you have to check for that. On the other side, a Task is successful when the work is finished, as expected, with no errors.

Edit:

You can simply use a function to check if you have a network connection, by pinging Google servers:

Alex Mamo
  • 130,605
  • 17
  • 163
  • 193