How to handle timeout in queries with Firebase

12,168

Solution 1

you can manage yourself a timer controller that after x seconds remove the listener to you firebase reference. It's very simple, just one line of code in android for example.

You can see the code for the web (Detaching Callbacks section): https://www.firebase.com/docs/web/guide/retrieving-data.html

or for android (Detaching Callbacks section): https://www.firebase.com/docs/android/guide/retrieving-data.html#section-detaching

same section for IOS ;)

Solution 2

As per today there is no timeout concept on those listeners. One option is to manage the timeout yourself.

This is how I do it when I also want to display a progress dialog while loading the content.

    private void showProgressDialog(boolean show, long time) {
    try {
        if (progressDialog != null) {
            if (show) {
                progressDialog.setMessage("Cargando...");
                progressDialog.show();
                new Handler().postDelayed(new Runnable() {
                    public void run() {
                        if(progressDialog!=null && progressDialog.isShowing()) {
                            progressDialog.dismiss();
                            Toast.makeText(ActPreguntas.this, "Couldn't connect, please try again later.", Toast.LENGTH_LONG).show();
                        }
                    }
                }, time);
            } else {
                progressDialog.dismiss();
            }
        }
    }catch(IllegalArgumentException e){
    }catch(Exception e){
    }
}

So when you make a request to Firebase you call showProgressDialog(true,5000) and after 5 seconds if the dialog stills there is because it could not connect and you then do what you have to as per the timeout.

On the callback of the Firebase listener you do this showProgressDialog(false,0)

Hope it helps.

Solution 3

Here's my solution for the Firebase iOS SDK, this may be helpful for others:

extension DatabaseReference {

    func observe(_ eventType: DataEventType, timeout: TimeInterval, with block: @escaping (DataSnapshot?) -> Void) -> UInt {

        var handle: UInt!

        let timer = Timer.scheduledTimer(withTimeInterval: timeout, repeats: false) { (_) in
            self.removeObserver(withHandle: handle)
            block(nil)
        }

        handle = observe(eventType) { (snapshot) in
            timer.invalidate()
            block(snapshot)
        }

        return handle
    }

}

Usage:

database.child("users").observe(.value, timeout: 30) { (snapshot) in

    guard let snapshot = snapshot else {
        // Timeout!
        return
    }

    // We got data within the timeout, so do something with snapshot.value
}
Share:
12,168
magicwerk
Author by

magicwerk

Updated on June 11, 2022

Comments

  • magicwerk
    magicwerk about 2 years

    I noticed that if I execute a query in Firebase and the database server is not reachable, the callback waits just forever (or until the server is reachable again).

    Where this behavior is quite natural for the asynchronous approach used, it would nevertheless be useful to have an easy way to specify a timeout so you could inform the user about the status.

    Is there such an option and I just missed it - or it really missing? Or how would you solve this problem?

  • Amit
    Amit almost 8 years
    observeSingleEventOfType, with this we don't get a handle and hence won't be able to detach the callback.
  • justintime
    justintime almost 6 years
    Isn't it better to remove the listener on completing five seconds? Alternatively, we can bake in the timeout feature into the request itself instead of tying it to a UI component.
  • Jonathan Ellis
    Jonathan Ellis almost 4 years
    The documentation states: "This setting does not affect the FirebaseDatabase and FirestoreClient APIs." There also seems to be no equivalent option for iOS.