如何检测 firebase 观察 childAdded 在达到 queryLimit 时停止调用?

How can I detect firebase observe childAdded is stop calling when reach queryLimit?

现在,我对 firebase 和使用 childAdded 数据事件类型的 observe 感到很困惑。我之所以用childAdded来观察我的firebase是因为我想让我的列表页动态firebase是否有新的数据插入

我的问题是如何知道 observe 在达到 queryLimit 时停止调用? 因为我有一个指示器,我想在达到 queryLimit 时将其关闭。

My firebase structure below:

root {
  talkID1(id by auto create) {
    attribute1 ...
    attribute2 ...
    attribute3 ...
    time
  }

  talkID2(id by auto create){
    ...
    time
  }

  ... // many talk ID which auto create by firebase

}

据我所知,如果使用childAdd观察,数据会一个接一个地传递给call back。所以如果我在 firebase 中有 N 数据 ,我认为它会调用 N<=5 次 ,对吗?

My completion handler below:

func receiveIfExist(completion: @escaping (_ data: (My data type) -> Void) {
    let requestWithQuery = Database.database.reference().child("root").queryOrdered(byChild: "time")

    requestWithQuery.queryLimited(toLast: 5).observe(.childAdded, with: { (snapshot) in
        guard let value = snapshot.value as? [String: Any] else { return }

        self.getSingleTalkInfo(key: snapshot.key, value: value, completion: { (data) in
            completion(data)
        })
    })
}

我在 viewDidLoad() 中调用 receiveIfExist 这个函数。

override func viewDidLoad() {
    super.viewDidLoad()

    self.myIndicator.startAnimating() // start running indicator
    self.receiveIfExist { (data) in

        self.handleTalk(with: data) // do something with request data
        self.myIndicator.stopAnimating() // WEIRD!!!! Please look comments below

        /* 
           I think it can not be added here because this completion will call N<=5 times just what I said before. 
           I think it should detect what my queryLimit data is and check the request data is this queryLimit data or not.
           If yes then stop indicator animating, if not then keep waiting the queryLimit reach.
        */
    } 
}

如何检测观察是否达到查询限制?

如果我能检测到,那么我可以在它到达时关闭我的指示器。

谢谢!

queryLimited(toLast: 5)

表示(用更简单的话来说)请获取最后 5 个值(顺序由查询的前一部分决定)

1. 现在,由于您按时间对数据进行排序,因此将检索最后 5 次的值,因此您的观察者将被触发 5 次

2. 注意,如果你的记录少于5条,比如2条记录,那么它只会被触发两次,因为最大限制是5,而不是最小限制

3.还有一点就是说如果新增了一个child,当你重新按照时间排序的时候,新的child ] 是最后 5 个项目之一,则此观察者将再次被触发。

因此,要获得查询限制,您可以像这样对代码进行一些更改:

func receiveIfExist(completion: @escaping (data: YourType, limitCount: Int) -> Void) {
    let requestWithQuery = Database.database.reference().child("root").queryOrdered(byChild: "time")

    requestWithQuery.queryLimited(toLast: 5).observe(.childAdded, with: { (snapshot) in
        guard let value = snapshot.value as? [String: Any] else { return }

        self.getSingleTalkInfo(key: snapshot.key, value: value, completion: { (data) in
            self.index = self.index + 1
            completion(data, self.index)
        })
    })
}

然后使用上面的函数如下:

var index = 0
override func viewDidLoad() {
    super.viewDidLoad()

    self.myIndicator.startAnimating() // start running indicator
    self.receiveIfExist { (data, limitCount) in

        self.handleTalk(with: data) // do something with request data
        if limitCount == 5 {
             self.myIndicator.stopAnimating()
        }


    } 
}

更新:

既然 Kevin 提出了非常好的观点,如果我们说只有两条记录并且索引永远不会等于 5 并且 myIndicator 不会停止动画,那么上述解决方案将会失败,

我想到的一个解决方案是:

首先我们使用 observeSingleEvent:

得到 children 计数
func getChildrenCount(completion: @escaping (_ childrenCount: Int) -> Void){
    Database.database.reference().child("root").observeSingleEvent(of:.value with: { (snapshot) in

                   completion(snapshot.children.count)

    }
}  

然后我们应用查询以获取最后 5 项:

func receiveIfExist(completion: @escaping (data: YourType, limitCount: Int) -> Void) {
        let requestWithQuery = Database.database.reference().child("root").queryOrdered(byChild: "time")

        requestWithQuery.queryLimited(toLast: queryLimit).observe(.childAdded, with: { (snapshot) in
            guard let value = snapshot.value as? [String: Any] else { return }

            self.getSingleTalkInfo(key: snapshot.key, value: value, completion: { (data) in
                self.index = self.index + 1
                completion(data, self.index)
            })
        })
    }

然后在您的代码中使用此计数,如下所示:

var index = 0
var childrenCount = 0
var queryLimit = 5
override func viewDidLoad() {
    super.viewDidLoad()

    self.myIndicator.startAnimating() // start running indicator
    self.getChildrenCount {(snapChildrenCount) in
         self.childrenCount = snapChildrenCount

         self.receiveIfExist { (data, limitCount) in

        self.handleTalk(with: data) // do something with request data
        if (self.childrenCount < self.queryLimit && limitCount == self.childrenCount) || limitCount == self.queryLimit  {
             DispatchQueue.main.async {
                   self.myIndicator.stopAnimating()
             }

        }


    } 

    }

}
func receiveIfExist(limitCount: UInt, completion: @escaping (data: MyDataType) -> Void) {
    let requestWithQuery = Database.database.reference().child("root").queryOrdered(byChild: "time")

    requestWithQuery.queryLimited(toLast: limitCount).observe(.childAdded, with: { (snapshot) in
        guard let value = snapshot.value as? [String: Any] else { return }

        self.getSingleTalkInfo(key: snapshot.key, value: value, completion: { (data) in
            completion(data)
        })
    })
}

我也是为了观察单子值才做这个功能

let requestTalks = Database.database.reference().child("root")    

func getSingleTalk(by key: String = "", at limitType: TalkLimitType, completion: @escaping (_ eachData: MyDataType) -> Void) {

    var requestSingleTalk: DatabaseQuery {
        switch limitType {
        case .first :
            return self.requestTalks.queryOrdered(byChild: "time").queryLimited(toFirst: 1)

        case .last :
            return self.requestTalks.queryOrdered(byChild: "time").queryLimited(toLast: 1)

        case .specificKey :
            return self.requestTalks.child(key)
        }
    }

    requestSingleTalk.observeSingleEvent(of: .value, with: { (snapshot) in

        if limitType == .specificKey {
            guard let value = snapshot.value as? [String: Any] else { return }

            self.getSingleTalkInfo(key: snapshot.key, value: value, completion: { (data) in
                completion(data)
            })
        } else {
            guard let snapshotValue = snapshot.value as? NSDictionary,
                  let eachTalkKey = snapshotValue.allKeys[0] as? String,
                  let eachTalkValue = snapshotValue.value(forKey: eachTalkKey) as? [String: Any] else { return }

            self.getSingleTalkInfo(key: eachTalkKey, value: eachTalkValue, completion: { (data) in
                completion(data)
            })
        }
    })
}

因此,我可以在我的 viewDidLoad() 中执行类似的操作

override func viewDidLoad() {
    super.viewDidLoad()

    self.myIndicator.startAnimating()
    self.receiveIfExist(limitCount: 5) { (eachData) in
        self.handleTalk(with: eachData)
        self.getSingleTalk(at: .last, completion: { (lastData) in
            if eachData.keyID == lastData.keyID{
                DispatchQueue.main.async {
                    self.myIndicator.stopAnimating()
                }
            }
        })
    } 
}