Swift iOS-如何在Firebase Observer外部重新加载TableView .childAdded以过滤掉重复值?

时间:2017-08-28 17:04:32

标签: ios swift uitableview firebase semaphore

我有一个带有2个标签的tabBar控制器:tabA包含ClassA,tabB包含ClassB。我在tabA / ClassA中将数据发送到Firebase数据库,然后在tabB / ClassB中观察数据库,在那里我检索它并将其添加到tableView。在tableView的单元格中,我显示了数据库中当前的运动鞋数量。

我知道.observeSingleEvent( .value).observe( .childAdded)之间的区别。我需要实时更新,因为当数据在tabA中发送时,如果我切换到tabB,我想在tabA / ClassA完成后看到新数据被添加到tableView。

在ClassB中,我在viewWillAppear中有我的观察者。我把它放在pullDataFromFirebase()函数中,每次出现视图时函数都会运行。我还有Notification观察器,它监听要在tabA / ClassA中发送的数据,以便它更新tableView。通知事件再次运行pullDataFromFirebase()

在ClassA中,在对Firebase的调用回调中,我有Notification帖子在ClassB中运行pullDataFromFirebase()函数。

我遇到的问题是,当新数据正在更新时我是否在tabB中,当它完成时,显示数据的单元格有一个计数并且计数被丢弃。我对它进行了调试,并且保存数据的sneakerModels数组有时会重复复制并使新添加的数据重复三次。

例如,如果我在B级并且数据库中有2对运动鞋,pullDataFromFirebase() func将运行,而tableView单元格将显示“你有2双运动鞋”

发生的事情是,如果我切换到tabA / ClassA,然后添加了一双运动鞋,当它更新时我切换到tabB / ClassB,单元格仍然会说“你有2双运动鞋”但是一旦更新细胞会说“你有5双运动鞋”,会出现5个细胞?如果我切换标签然后回来,它会正确显示“你有3双运动鞋”和正确数量的单元格。

这就是通知的来源。一旦我补充说,如果我经历了相同的过程,并开始使用2个运动鞋,单元格会说“你有2双运动鞋”,我去tabA,添加另一对,切换回来到tabB仍然看到“你有2双运动鞋”。一旦数据被发送,细胞将简要显示“你有5对运动鞋”并显示5个细胞,然后它将正确更新为“你有3对运动鞋”和正确数量的细胞(我没有必要切换标签)。

通知似乎有效,但有一个短暂不正确的时刻。

我做了一些研究,我发现的最多的是一些帖子,说我需要使用信号量,但显然来自几位留下评论的人,他们说信号量并不意味着异步使用。 我不得不更新我的问题以排除信号量参考。

现在我正在pullDataFromFirebase()的完成处理程序中运行tableView.reloadData()。

如果在完成后将tableView重新加载到观察者之外以防止重复值?

型号:

class SneakerModel{
    var sneakerName:String?
}

塔布/ ClassB的:

ClassB: UIViewController, UITableViewDataSource, UITableViewDelegate{

var sneakerModels[SneakerModel]

override func viewDidLoad() {
    super.viewDidLoad()

    NotificationCenter.default.addObserver(self, selector: #selector(pullDataFromFirebase), name: NSNotification.Name(rawValue: "pullFirebaseData"), object: nil)
}

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

    pullDataFromFirebase()
}

func pullDataFromFirebase(){

    sneakerRef?.observe( .childAdded, with: {
        (snapshot) in

        if let dict = snapshot.value as? [String:Any]{
            let sneakerName = dict["sneakerName"] as? String

            let sneakerModel = SneakerModel()
            sneakerModel.sneakerName = sneakerName

            self.sneakerModels.append(sneakerModel)

            //firebase runs on main queue
            self.tableView.reloadData()
        }
    })
}

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return sneakerModels.count
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "SneakerCell", for: indexPath) as! SneakerCell

    let name = sneakerModels[indePath.row]
    //I do something else with the sneakerName and how pairs of each I have

    cell.sneakerCount = "You have \(sneakerModels.count) pairs of sneakers"

    return cell
}

}
}

塔巴/ ClassA的:

ClassA : UIViewController{

@IBAction fileprivate func postTapped(_ sender: UIButton) {

    dict = [String:Any]()
    dict.updateValue("Adidas", forKey: "sneakerName")

    sneakerRef.?.updateChildValues(dict, withCompletionBlock: {
                (error, ref) in

        //1. show alert everything was successful

        //2. post notification to ClassB to update tableView
        NotificationCenter.default.post(name: Notification.Name(rawValue: "pullFirebaseData"), object: nil)
    }
}
}

1 个答案:

答案 0 :(得分:0)

在我的应用的其他部分,我使用def func(): a = 4 print(a) # this prints nothing unless you call func() 方法作为filterDuplicates添加到extension来过滤掉重复的元素。我是从filter array duplicates得到的:

Array

我无法在我的情况下找到任何特别的东西所以我使用了filterDuplicates方法非常方便。

在我的原始代码中,我有一个日期属性,我应该将其添加到问题中。我在这里添加它的任何方式和date属性是我需要在filterDuplicates方法中使用来解决我的问题:

型号:

extension Array {

    func filterDuplicates(_ includeElement: @escaping (_ lhs:Element, _ rhs:Element) -> Bool) -> [Element]{
        var results = [Element]()

        forEach { (element) in

            let existingElements = results.filter {
                return includeElement(element, $0)
            }

            if existingElements.count == 0 {
                results.append(element)
            }
        }

        return results
    }
}

在tabA / ClassA内部,无需使用Firebase回调中的Notification,但是将dateInSecs添加到dict。

塔巴/ ClassA的:

class SneakerModel{
    var sneakerName:String?
    var dateInSecs: NSNumber?
}

ClassA : UIViewController{ @IBAction fileprivate func postTapped(_ sender: UIButton) { //you must add this or whichever date formatter your using let dateInSecs:NSNumber? = Date().timeIntervalSince1970 as NSNumber? dict = [String:Any]() dict.updateValue("Adidas", forKey: "sneakerName") dict.updateValue(dateInSecs!, forKey: "dateInSecs")//you must add this sneakerRef.?.updateChildValues(dict, withCompletionBlock: { (error, ref) in // 1. show alert everything was successful // 2. no need to use the Notification so I removed it } } } 函数中Firebase观察者的完成处理程序内的tabB / ClassB中,我使用了filterDuplicates方法来过滤掉显示的重复元素。

塔布/ ClassB的:

pullDataFromFirebase()

基本上,filterDuplicates方法循环遍历sneakerModels数组,将每个元素与dateInSecs进行比较,当它找到它们时,它会排除副本。然后我用结果重新初始化sneakerModels,一切都很顺利。

另请注意,ClassB的viewDidLoad中没有任何Notification观察者的需要,所以我将其删除了。