Nicolaas Wagenaar
Nicolaas Wagenaar

Reputation: 170

Firebase removing observers

I have a problem removing a Firebase observer in my code. Here's a breakdown of the structure:

var ref = Firebase(url:"https://MY-APP.firebaseio.com/")
var handle = UInt?

override func viewDidLoad() {
    handle = ref.observeEventType(.ChildChanged, withBlock: {
        snapshot in

        //Do something with the data
    }
}

override func viewWillDisappear(animated: Bool) {
    if handle != nil {
        println("Removed the handle")
        ref.removeObserverWithHandle(handle!)
    }
}

Now when I leave the viewcontroller, I see that "Removed the handle" is printed, but when I return to the viewcontroller, my observer is called twice for each event. When I leave and return again, it's called three times. Etc. Why is the observer not being removed?

I do also call ref.setValue("some value") later in the code, could this have anything to do with it?

Upvotes: 8

Views: 4443

Answers (3)

jazzgil
jazzgil

Reputation: 2366

Observers must be removed on the same reference path they were put upon. And for the same number of times they were issued, or use ref.removeAllObservers() for each path.

Here's a trick I use, to keep it tidy:

var fbObserverRefs = [FIRDatabaseReference]()    // keep track of where observers defined.

...then, put observers in viewDidLoad():

fbObserverRefs.append(ref.child("user/\(uid)"))
fbObserverRefs.last!.observe(.value, with: { snap in
    // do the work...
})

...then, in viewWillDisappear(), take care of removing any issued observers:

// Only true when popped from the Nav Controller stack, ignoring pushes of 
// controllers on top.
if isBeingDismissed || isMovingFromParentViewController {
    fbObserverRefs.forEach({ $0.removeAllObservers() })
}

Upvotes: 0

Reid
Reid

Reputation: 734

Thought I was having this bug but in reality I was trying to remove observers on the wrong reference.

ORIGINAL CODE:

let ref: FIRDatabaseReference = FIRDatabase.database().reference()
var childAddedHandles: [String:FIRDatabaseHandle] = [:]

func observeFeedbackForUser(userId: String) {
    if childAddedHandles[userId] == nil { // Check if observer already exists

        // NOTE: - Error is caused because I add .child(userId) to my reference and
        //     do not when I call to remove the observer.

        childAddedHandles[userId] = ref.child(userId).observeEventType(.ChildAdded) { 
            [weak self] (snapshot: FIRDataSnapshot) in

            if let post = snapshot.value as? [String:AnyObject],
               let likes = post["likes"] as? Int where likes > 0 {             

                self?.receivedFeedback(snapshot.key, forUserId: userId)          
            }
        }
    }
}

func stopObservingUser(userId: String) {
    // THIS DOES NOT WORK

    guard let cah = childAddedHandles.removeValueForKey(userId) else {
        print("Not observing user")
        return
    }

    // Error! I did not add .child(userId) to my reference
    ref.removeObserverWithHandle(cah)
}

FIXED CODE:

func stopObservingUser(userId: String) {
    // THIS WORKS

    guard let cah = childAddedHandles.removeValueForKey(userId) else {
        print("Not observing user")
        return
    }

    // Add .child(userId) here
    ref.child(userId).removeObserverWithHandle(cah)
}

Upvotes: 2

Daniel K
Daniel K

Reputation: 53

Given it's April 2015 and the bug is still around I'd propose a workaround for the issue:

  • keep a reference of the handles (let's say in a dictionary and before initiating a new observer for the same event type check if the observer is already there.

Having the handles around has very low footprint (based on some official comments :) ) so it will not hurt that much.

Upvotes: 1

Related Questions