Firebase: when to call removeObserverWithHandle in swift

observesingleevent firebase swift
firebase retrieve data swift
firebase get value of child swift
retrieve data from firebase swift 4
childbyautoid firebase swift
querylimited firebase swift
updatechildvalues firebase swift
retrieve data from firebase swift 5

Documentation says you need to call observeEventType:withBlock to remove an observer if you no longer need it.

I've seen samples where it is called within ViewDidDisAppear. I also find some Obj-C code called this method within deinit, which is not ncessary in Swift.

In my simple app, however, I want data to be synced as long as I am in the app. If this is the case, do I have to call observeEventType:withBlock ever?

I checked the Chat-Swift sample code on Firebase website, and did not find observeEventType:withBlock.

Does it mean it's ok not to call observeEventType:withBlock:. if I want the observer to be on when the app is in use?

Thank you.

UPDATE

Thanks to Jay and David. I see it makes sense to observe in ViewWillAppear and remove it in ViewDidDisappear.

However, I am using observeEventType to monitor any value change to the node and would update UI if there is any. If i put it in ViewWillAppear:

 override func viewWillAppear(animated: Bool) {
    super.viewWillAppear(animated)

    ref.observeEventType(.Value, withBlock: { snap in {
      // **update UI if there is any Value change** 
    })
  }

The problem with putting it in viewWillAppear is that, it gets called every time the view appears, regardless of Value change or not. Because of this, the snapshot is downloaded and my UI gets refreshed every time I return to the view. This becomes counterproductive.

I have also tried ChildAdded/ChildRemoved, however, it only returns the last node, not the path from my ref:

For instance, if I add to ref/child1/child2/child3/value, ChildAdded would only return child3/value.

So if I have to observe Value, it seems like putting it in ViewDidLoad is better? In this way, it gets the snapshot one time when the view loaded, and would repeat whenever there is a change, but would not obtain the snapshot just because the view appears.

To build upon @Jay's excellent answer:

In a UIViewController, create a reference as a property. Initialize a reference in viewDidLoad. Observe events in viewWillAppear. Remove observers in viewDidDisappear.

class MyViewController: UIViewController {

  var ref: Firebase!

  // Called only on load, great place to initialize
  override func viewDidLoad() {
    super.viewDidLoad()
    ref = Firebase(url: "https://<YOUR-FIREBASE-APP>.firebaseio.com/updates")
  }

  // Can be called many times to go on screen
  // Syncing should only occur when on view to conserve memory
  override func viewWillAppear(animated: Bool) {
    super.viewWillAppear(animated)

    ref.observeEventType(.Value, withBlock: { snap in {
      // do something with the data 
    })
  }

  // Can be called many times to off screen
  // Remove observers on the ref to conserve memory
  override func viewDidDisappear(animated: Bool) {
    super.viewDidDisappear(animated)
    ref.removeAllObservers() 
  }

}

Per your edit:

The problem with putting it in viewWillAppear is that, it gets called every time the view appears, regardless of Value change or not. Because of this, the snapshot is downloaded and my UI gets refreshed every time I return to the view. This becomes counterproductive.

Firebase is built for speed. These are the kind of things that you leave up to the client because it has several features that handle these situations.

The Firebase client has built-in caching. Unless you're downloading a megabyte of data in viewDidAppear the update is nominal. When the observer fires on viewDidAppear it doesn't necessarily mean it's downloading the data again. The viewDidAppear function is where your observers belong.

FYI, I am a Firebase employee who works on iOS.

Firebase: when to call removeObserverWithHandle in swift , Documentation says you need to call observeEventType:withBlock to remove an observer if you no longer need it. I've seen samples where it is called within  I also find some Obj-C code called this method within deinit, which is not ncessary in Swift. In my simple app, however, I want data to be synced as long as I am in the app. If this is the case, do I have to call observeEventType:withBlock ever? I checked the Chat-Swift sample code on Firebase website, and did not find observeEventType:withBlock.

observeEventType:withBlock is what is used to observe a node.

Once the app is observing a node it will continue to observe unless your either quit the app or tell Firebase to stop observing.

To stop observing you can either use the handle that was returned when you started observing like this:

    //start observing and get a handle
FirebaseHandle handle = [ref observeEventType:FEventTypeValue withBlock:^(FDatasnapshot* snapshot) {
        // do some stuff with the snapshot data
    }];

    [ref removeObserverWithHandle:handle]; //stop observing using the handle

or like this

[ref removeAllObservers];

Read and Write Data on iOS, If there is no data, the snapshot will return false when you call exists() and nil when Calling removeObserverWithHandle or removeAllObservers on a listener​  The Cloud Functions for Firebase client SDKs let you call functions directly from a Firebase app. To call a function from your app in this way, write and deploy an HTTPS Callable function in Cloud Functions, and then add client logic to call the function from your app.

Observing and stop observing at viewWillAppear() and viewWillDisappear() would work but this fires child values added(in case of using .added/.value for observation type) again when view controller appears.

I prefer set observation at viewDidLoad() and stop observation at deinit.

Some says deinit is not called after setting observation, but the reason is when you set observing, observer closure is strongly retaining self so that deinit will be never called. You can set weak or unowned inside the closure.

Here is example.

class SomeViewController : UIViewController {

  override func viewDidLoad() {
    super.viewDidLoad()

    someRef.observe(.childAdded) { [weak self] (snapshot) in
      guard let weakSelf = self else { return }
      ...
    }
  }

  deinit {
    print("deinit called")
    someRef.removeAllObservers()
  }

}

Please don't forget to write [weak self] or deinit will be never called. Hope it helps.

Work with Lists of Data on iOS, Calling removeObserverWithHandle or removeAllObservers on a listener does not automatically remove listeners registered on its child nodes; you must also be​  Firebase: cuándo llamar a removeObserverWithHandle en swift La documentation dice que debe llamar a observeEventType:withBlock para eliminar un observador si ya no lo necesita. He visto muestras en las que se llama dentro de ViewDidDisAppear .

Firebase: when to call removeObserverWithHandle in swift, do I have to call observeEventType:withBlock ever? I checked the Chat-Swift sample code on Firebase website, and did not find observeEventType:withBlock. Firebase removing observers I do also call ref.setValue I did not add .child(userId) to my reference ref.removeObserverWithHandle(cah) } FIXED CODE:

removeObserverWithHandle does't work as expected for iOS , The method removeObserverWithHandle doesn't remove the handle as expected​, you are subscribed to the Google Groups "Firebase Google Group" group. Note the extra child call before the removeObserver call. firebase auth:import users.json --hash-algo=scrypt --rounds=8 --mem-cost=14 Send feedback Except as otherwise noted, the content of this page is licensed under the Creative Commons Attribution 4.0 License , and code samples are licensed under the Apache 2.0 License .

Best practices for the iOS UIViewController and , If this is the case, do I have to call observeEventType:withBlock ever? I checked the Chat-Swift sample code on Firebase website, and did not  Caution: New HTTP and HTTP callable functions deployed with any Firebase CLI lower than version 7.7.0 are private by default and throw HTTP 403 errors when invoked. Either explicitly make these functions public, or update your Firebase CLI before you deploy any new functions.

Comments
  • @David answer handles this as it stops observing when the view is not being shown. It refreshes the view when it is being shown but that may be what you want anyway. There are other two potential solutions: 1) ref.observeEventType actually returns a handle. Store that handle as a property. If it's nil, then handle = ref.observeEventType. If it's not nil then don't. 2) Move the observeEventType to a higher level. Assuming you init views from the App delegate, once they are set up, then observeEventType.
  • The ChildAdded Event should be explored. If you tried it and it's only returning the last node then there's a coding error. It fires once when you call it for each child node, one at a time, and then only for childAdded events thereafter. You can leverage it to initially populate the UI (it's called once for each child) and then keep the UI updated thereafter (only called when a child is added, and it only presents the new child). We rarely use Value events because once data is loaded, we only want to know about changes. Implementing ChildAdded, ChildChanged and ChildRemoved handles that.
  • Thanks Jay. After a few attempts, I decided to keep my observer in viewDidLoad, but change from observing Value to observing ChildAdded/Removed depending on my use. Thank you for your input.
  • Observers do not belong in viewDidLoad, this can lead to memory leaks. Use viewWillAppear or viewDidAppear instead.
  • Let me know if my answered work for you. If so please mark it as accepted. It's good to keep the unanswered queue clear.
  • Thanks for your comment, please see my update above.
  • Thank you! It's great to have you answer may question!
  • David, you certainly answered my question. However, my problem persists. My app is simple: I download a fairly large node when the view loads, and listens to ChildAdded to update a portion of the UI. But with observer in ViewWillAppear, ChildAdded will get called every time I return to the view and the whole UI gets updated. Not sure that's what I want. I may figure out a way to change my code. But as of now, I put observer in ViewDidLoad and it works exactly as intended. So far I have not noticed any memory warning yet. Thank you again for your help.
  • @DavidEast What about the new Firebase? Is there anything we should know? I mean you initialize the ref inside the viewDidLoad but in the new Firebase we have the ref inside the .plist
  • I think better remove the old (if initialized) observer first, in viewWillAppear, before you initialize new one. Because viewWillAppear may be called multiple times while viewDidDisappear call only once in that time.
  • Also, in answer to the OP's specific question, you must not do it in deinit, you must do it in viewWillDisappear (or viewDidDisappear).
  • @Fattie is this best practice? So calling the removeObserverWithHandle anywhere other than viewWillDisappear or viewDidDisappear is not recommended?!
  • @FamicTech - that does seems to be the case. But TBH I'm not sure if even Firebase know. But yes, correct, do it in view will/did disappear.
  • @FamicTech anywhere other than is kind of broad. There may be situations where you intentionally want to stop observing a node. For example, say you are watching feeds for users and you want to stop watching one specific feed. You can call removeObserver for that specific feed outside of viewDidDisappear. Calling it in deinit is ok but really not necessary. Calling it in viewDidDisappear is certainly fine. However... that's situational. What if you want to receive events throughout your app regardless of which view you're looking at? The answer really depends on your specific use case.