2017-08-28 14 views
-1

我有2个选项卡一个的TabBar控制器:塔巴含有ClassA和塔布含有ClassB的。我将数据发送到tabA/ClassA中的Firebase数据库,然后观察tabB/ClassB中的数据库,并将其检索到并添加到tableView中。在tableView的单元格内,我显示了当前在数据库中的运动鞋的数量。斯威夫特的iOS荧光灯,如何刷新的TableView外火力地堡观察.childAdded过滤掉重复值?

我知道.observeSingleEvent(.value) VS .observe(.childAdded)之间的差异。我需要实时更新,因为当数据在塔巴得到发送,如果我切换到塔布,我想看到新的数据会添加到的tableView一次塔巴/ ClassA的完成。

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

在ClassA中,调用Firebase的回调函数内部我有通知帖子来运行ClassB中的pullDataFromFirebase()函数。

我遇到的问题是,如果我在塔布,而新的数据更新,它完成时,显示的数据的单元格具有计数和计数揭去。我对它进行了调试,包含数据的sneakerModels数组有时会复制并重复新添加的数据。

例如,如果我在B类和有2个双球鞋在数据库中,pullDataFromFirebase() FUNC将运行,并且的tableView细胞会显示“您有2双运动鞋”

发生了什么是,如果我切换到塔巴/ ClassA的,然后加1双球鞋,而它的更新,我切换到塔布/ ClassB的,细胞仍然会说:“你有2个双运动鞋”,但那么,一旦更新了电池会说:“你有5双运动鞋“和5个细胞会出现?如果我切换标签并回来,它会正确显示“你有3双运动鞋”和正确数量的细胞。

这就是通知的来源。一旦我补充说,如果我经历了同样的过程,并开始使用2个运动鞋,那么细胞会说“你有两双运动鞋”,我去tabA,再加一双,开关回到tabB,仍然看到“你有两双运动鞋”。一旦数据被发送的小区将简要介绍“你有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) 
    } 
} 
} 
+0

基本上不要使用信号量来解决异步数据处理问题。是否可以选择发布通知并在通知的接收者中运行异步代码('updateChildValues')?或者在通知中传递添加的对象并更新数据源数组而不重新*拉数据。 – vadian

+0

@vadian感谢您的帮助。那不是我在做什么?该帖子以ClassA发送,接收者为ClassB。 updateChildValues是在ClassB中的pullFirebaseData函数中运行的。为什么不将信号量用于异步进程? –

+0

有一个正确的方式来异步做东西。信号量不是它。 – matt

回答

0

在我的应用程序的其他部分我使用filterDuplicates的方法,我加入作为extensionArray以过滤出重复的元素。我是从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, $0) 
      } 

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

     return results 
    } 
} 

我找不到什么特别的SO到我的情况,所以我使用的方法filterDuplicates这是非常方便的。

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

型号:

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

内塔巴/ ClassA的,没有必要使用Firebase回调中的通知但是将dateInSecs添加到字典中。

塔巴/ 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 
    } 
} 
} 

而在塔布/ ClassB的在我用filterDuplicates方法筛选出分别示出了重复的元素的pullDataFromFirebase()功能的火力地堡观察者的完成处理程序内部。

塔布/ 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{$0.dateInSecs == $1.dateInSecs} 

      self.tableView.reloadData() 
     } 
    }) 
} 

基本上filterDuplicates方法循环通过sneakerModels阵列比较每个元件到dateInSecs当它发现他们它排除了副本。然后我用结果重新初始化运动鞋模型,一切都很好。

另外请注意,ClassB的viewDidLoad中没有任何需要通知观察者,所以我删除了它。

相关问题