Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Wrapping a Firebase Task<Void> into RxJava CompletableEmitter?

I am trying to wrap a Firestore method into CompletableEmitter, to be able to return Completable.complete() on success/complete, and Completable.error() on failure. (The Task is a Firestore operation to update a document(1 field in that document) in a collection)

This is the firestore method I am trying to wrap into a Completable which returns a Task<Void>. And here is the code without having it wrapped in a Completable:

public static Completable updateDocument(DocumentReference ref, Map<String,Object> fieldsAndValues){
    ref.update(fieldsAndValues)
                .addOnSuccessListener(aVoid -> {
                    Timber.d("SUCCESS");
                }).addOnCompleteListener(task -> {
                    Timber.d("COMPLETE");
                }).addOnFailureListener(e -> {
                    Timber.d("ERROR");
                });
        return Completable.complete();    //just for the sake of returning complete to finish the method...
}

This above code executes as is should.

Here is my attempt to wrap that code :

public static Completable updateDocument(DocumentReference ref, Map<String,Object> fieldsAndValues){
    return Completable.create(emitter -> ref.update(fieldsAndValues)
       .addOnSuccessListener(aVoid -> emitter.onComplete())
       .addOnCompleteListener(task -> emitter.onComplete())
       .addOnFailureListener(emitter::onError));
}

While the first executes with success (onSuccess called, document in Firestore updated), the wrapped code never reaches onSuccess from the Task (onSuccess never reached, document never updated in Firestore). But it does not throw any errors, The Task completes but never reaches success.

I do not understand this behavior. Any suggestions? What do I do wrong here?

like image 874
CJR Avatar asked Oct 20 '25 15:10

CJR


2 Answers

You don't necessarily have to use the callbacks for a task - I think that the api is restrictive in this sense.

you can make the task blocking and just evaluate it after execution i.e.

public static Completable updateDocument(DocumentReference ref, Map<String,Object> fieldsAndValues){
        return Completable.fromCallable(() -> {
            final Task<Void> task = ref.update(fieldsAndValues);
            Tasks.await(task);
            if(task.isSuccessful()) {
                return Completable.complete();
            } else {
                return Completable.error(new IllegalStateException("Task not successful", task.getException()));
            }
        });
    }

As your original problem, try holding a reference to the task in the closure i.e.

public static Completable updateDocument(DocumentReference ref, Map<String, Object> fieldsAndValues) {
        return Completable.create(emitter -> {
            final Task<Void> task = ref.update(fieldsAndValues)
            task.addOnSuccessListener(aVoid -> emitter.onComplete())
                    .addOnCompleteListener(task -> emitter.onComplete())
                    .addOnFailureListener(emitter::onError);
        });
    }
like image 60
Mark Avatar answered Oct 23 '25 05:10

Mark


Have you subscribed returned completable? ref.update() will be never executed unless you subscribe it

updateDocument(...)
    .subscribe(...) // <- this

Note: There is also wrong with your first code. Completables should be executed when they are subscribed. However in your first code, it is executed when you call updateDocument(). For example, if you do Completable c = updateDocument(...), it should not update document because you did not subscribe() yet but your first code it will update document.

like image 45
ytRino Avatar answered Oct 23 '25 03:10

ytRino