Firebase: when to call removeObserverWithHandle in swift

泄露秘密 提交于 2019-11-28 17:38:09

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.

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];

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.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!