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

Swift iOS -How To Reload TableView Outside Of Firebase Observer .childAdded to Filter Out Duplicate Values?

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

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

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

在 ClassA 中,在调用 Firebase 的回调中,我有通知 post 到 运行 ClassB 中的 pullDataFromFirebase() 函数.

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

例如,如果我在 Class B 并且数据库中有 2 双运动鞋,pullDataFromFirebase() 函数将 运行,tableView 单元格将显示 "You have 2 pairs of sneakers"

发生的事情是,如果我切换到 tabA/ClassA,然后添加了 1 双运动鞋,在更新时我切换到 tabB/ClassB,单元格仍然会显示 "You have 2 pairs of sneakers",但随后一旦它更新单元格会说 "You have 5 pairs of sneakers" 并且会出现 5 个单元格?如果我切换标签并返回,它会正确显示 "You have 3 pairs of sneakers" 和正确的单元格数量。

这就是通知出现的地方。一旦我添加了如果我经历相同的过程并从 2 款运动鞋开始,单元格会显示 "You have 2 pairs of sneakers",我转到 tabA,添加另一双,切换回tabB,仍然看到 "You have 2 pairs of sneakers"。发送数据后,单元格将短暂显示 "You have 5 pairs of sneakers" 并显示 5 个单元格,然后它会正确更新为 "You have 3 pairs of sneakers" 和正确数量的单元格(我不必切换选项卡)。

通知似乎有效,但有一个短暂的错误时刻。

我做了一些研究,我能找到的最多的是一些 post 说我需要使用信号量,但显然来自几个在下面留下评论的人,他们说信号量不应该被使用异步地。 我必须更新我的问题以排除信号量引用。

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

完成后如何在观察者外部重新加载 tableView 以防止重复值?

型号:

class SneakerModel{
    var sneakerName:String?
}

tabB/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
}

}
}

tabA/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)
    }
}
}

在我的应用程序的其他部分,我使用 filterDuplicates 方法,我将其作为 extension 添加到 Array 以过滤掉重复元素。我从 filter array duplicates:

得到的
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, [=10=])
            }

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

        return results
    }
}

我找不到任何关于我的情况的特别之处,所以我使用了非常方便的 filterDuplicates 方法。

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

型号:

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

在 tabA/ClassA 中,无需在 Firebase 回调中使用通知,但将 dateInSecs 添加到字典中。

tabA/ClassA:

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
    }
}
}

并且在 tabB/ClassB Firebase 观察器的完成处理程序中的 pullDataFromFirebase() 函数中,我使用 filterDuplicates 方法过滤掉出现的重复元素。

tabB/ClassB:

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)

            // use the filterDuplicates method here
            self.sneakerModels = self.sneakerModels.filterDuplicates{[=13=].dateInSecs == .dateInSecs}

            self.tableView.reloadData()
        }
    })
}

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

另请注意,ClassB 的 viewDidLoad 中不需要通知观察器,因此我将其删除。