2017-08-28 5 views
-1

私は2つのタブを持つtabBarコントローラを持っています:tabAはClassAを含み、tabBはClassBを含みます。私はtabA/ClassAのFirebaseデータベースにデータを送信し、TabB/ClassBのデータベースを調べて、それを取得してtableViewに追加します。 tableViewのセルの中には、現在データベース内にあるスニーカーの数が表示されます。スウィフトiOS - Firebase Observerの外でテーブルビューを再ロードする方法.child追加して重複値を除外しますか?

私は.observeSingleEvent(.value).observe(.childAdded)の違いを知っています。 TabAにデータが送信されている間にtabBに切り替えると、tabA/ClassAが完了したら新しいデータがtableViewに追加されたことを確認したいので、ライブアップデートが必要です。

私はviewWillAppearに私のオブザーバーを持っています。私はそれをpullDataFromFirebase()の関数の中に置き、ビューが現れるたびに関数が実行されます。また、TabViewを更新するように、tabA/ClassAで送信されるデータをリッスンする通知オブザーバもあります。通知イベントはpullDataFromFirebase()を再度実行します

ClassAでは、FireBasicの呼び出しのコールバック内に、ClassBのpullDataFromFirebase()関数を実行する通知ポストがあります。

私が実行している問題は、新しいデータが更新されている間にタブBに入っているときです。完了すると、データを表示するセルにカウントがあり、カウントがスローされます。私はそれをデバッグし、データを保持するsneakerModels配列は時々複製され、新しく追加されたデータを3倍にします。例えば

私はクラスBで午前とスニーカーの2組がデータベースに存在する場合、pullDataFromFirebase() funcが実行され、のtableViewセルが起こっていた何

「あなたはスニーカーの2ペアを持っている」と表示されます私はtabA/ClassAに切り替えて、スニーカー1ペアを追加しましたが、TabB/ClassBに切り替えると、セルはまだ "あなたは2ペアのスニーカーを持っています"と言いますが、セルを更新すると "You 5組のスニーカーを持っていて、5つの細胞が出現するでしょうか?タブを切り替えて戻ってきたら、「あなたは3ペアのスニーカーがあります」と正しい量のセルが正しく表示されます。

通知が届いた場所です。同じプロセスを経て2つのスニーカーで始めると、「あなたは2ペアのスニーカーがあります」と表示され、別のペアを追加してスイッチを追加しますtabBに戻り、「あなたは2ペアのスニーカーを持っています」と表示されます。データが送信されると、セルには「あなたに5ペアのスニーカーがあります」と表示され、5セルが表示されます。「スニーカーが3ペアあります」と正しいセル量に正しく更新されます(スイッチタブ)。

通知は機能していたようですが、その短い間違った瞬間がありました。

セマフォを使用する必要があると主張したが、セマフォが非同期的に使用されることを意図していないと答えたいくつかの投稿がありました。 セマフォの参照を除外するために私の質問を更新しなければならなかった。

今私は、pullDataFromFirebase()の完了ハンドラで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 
} 

} 
} 

タバ/に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メソッドを使用しますextensionからArrayまでは重複要素を除外します。私はfilter array duplicatesからそれを得た:私はSO私の状況に私は非常に便利だった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 
    } 
} 

私の元のコードでは、質問に追加する必要がある日付プロパティがあります。

モデル:

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

をタバ/にClassAの内部で使用する必要はありません、私はそれをここに追加していると、その日付のプロパティは、私は私の問題を解決するためにfilterDuplicatesメソッド内で使用するために必要なものである任意の方法firebaseコールバック内の通知は、dateInSecsをdictに追加します。

タバ/にClassA:私は表示された要素の重複を除外するためにfilterDuplicatesメソッドを使用pullDataFromFirebase()機能でFirebase観察者の完了ハンドラ内

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のインチ

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

      self.tableView.reloadData() 
     } 
    }) 
} 

基本的filterDuplicates方法はdateInSecsに各要素を比較sneakerModels配列をループし、それらを見つけたときには、コピーを排除します。私は結果をスニーカーモデルに再初期化し、すべてがうまくいきます。

通知ObserverがClassBのviewDidLoad内にある必要がないので、削除しました。

関連する問題