2017-01-13 40 views
0

私の主な質問は、ちらつきを取り除く方法ですが、私が正規化されていないFirebaseデータを正確かつ効率的に扱っているかどうかを知りたいだけです。私のアプローチは正しかったところにありますか?新しいFirebaseデータがTableViewセルのちらつき(Firebase/iOS/Swift)

私は、正規化されていないデータを持つfirebaseデータベースのデータを正しく表示しようとしています。私は投稿を持っていて、それぞれの投稿に関連したコメントをしています。誰かがviewcontrollerから新しいviewcontrollerにsegueingすることによって誰かが投稿のコメントセクションを開くたびに、投稿のユニークキー(postKey)を取得し、postCommentGroupに含まれるpostKeyに関連付けられたコメントのグループをスキャンします。 postCommentGroup内の各postKeyの子であるコメントのグループは、キーとしてのcommentKeyと値としての「true」であり、どのコメントがどの投稿に関連付けられているかを示します。コメントはまったく別のブランチにありますが、それはFirebaseのドキュメントによると思います。

本質的に3層のネストされたオブザーバがあります。

わかりやすくするために、私はテーブルビューでデキュー可能なセルを使ってセルをリサイクルしています。また、物を妨害しているかもしれない初歩的な遅延ロード/イメージキャッシュメカニズムもありますが、複雑なテーブルビューなので、それは問題だとは思わない。

知識が不足しているため、このサイクルを経る以外の方法でデータを表示する方法がわかりません。私はこのサイクルがちらつきを引き起こしていると思うが、データをロードする方法が他に何か分からない。私は、クエリを使用するなど、さまざまなやり方で試してみましたが、それを動作させることはできませんでした。

私は、データのクエリ方法(私が助けてくれるかもしれないと思う)を上手く取ろうとしましたが、Swiftの構文とFirebaseのアップデートを更新しました。これまでの例を少し難しくしています。

最近の FirebaseのサイトやGithubのいずれかのFirebaseのドキュメントでは、非正規化されたデータをやや複雑な方法で適切に使用した例はありません。誰でもSwift 3.0とFirebase(最新バージョン - 旧バージョンではない)を使用して非正規化データを扱うことに関して、GitHub上のプロジェクトであろうと、ブログであろうと、 stackoverflowの有用な記事?ここで


 
"comments" : { 
 
     "-KaEl8IRyIxRbYlGqyXC" : { 
 
      "description" : "1", 
 
      "likes" : 1, 
 
      "postID" : "-KaEfosaXYQzvPX5WggB", 
 
      "profileImageUrl" : "https://firebasestorage.googleapis.com", 
 
      "timePosted" : 1484175742269, 
 
      "userID" : "9yhij9cBhJTmRTexsRfKRrnmDRQ2", 
 
      "username" : "HouseOfPaine" 
 
     } 
 
     }, 
 
     
 
     "postCommentGroup" : { 
 
     "-KaEfosaXYQzvPX5WggB" : { 
 
      "-KaEl8IRyIxRbYlGqyXC" : true, 
 
      "-KaEl9HiPCmInE0aJH_f" : true, 
 
      "-KaF817rRpAd2zSCeQ-M" : true 
 
     }, 
 
     "-KaF9ZxAekTEBtFgdB_5" : { 
 
      "-KaFEcXsSJyJwvlW1w2u" : true 
 

 
     }, 
 
     "-KaJyENJFkYxCffctymL" : { 
 
      "-KaQYa0d08D7ZBirz5B4" : true 
 
     } 
 
     }, 
 
     "posts" : { 
 
     "-KaEfosaXYQzvPX5WggB" : { 
 
      "caption" : "Test", 
 
      "comments" : 11, 
 
      "imageUrl" : "https://firebasestorage.googleapis.com/", 
 
      "likes" : 0, 
 
      "profileImageUrl" : "https://firebasestorage.googleapis.com/", 
 
      "timePosted" : 1484174347995, 
 
      "title" : "test", 
 
      "user" : "17lIDKNx6LgzQmaeQ2ING582zi43", 
 
      "username" : "Freedom" 
 
     } 
 
     },
が私のコードです:ここで

はfirebaseデータ構造です

func commentGroupObserver() { 

    DataService.ds.REF_POST_COMMENT_GROUP.observeSingleEvent(of: .value, with: { (snapshot) in 

     if snapshot.value != nil { 

      if let snapshots = snapshot.children.allObjects as? [FIRDataSnapshot] , snapshots.count > 0 { 

       self.comments = [] 

       for snap in snapshots { 

        if let tempVarPostKeyForCommentGroup = snap.key as String? { 

         if tempVarPostKeyForCommentGroup == self.post.postKey { 

          self.postKeyForCommentGroup = tempVarPostKeyForCommentGroup 

          self.commentObservers() 
         } else { 

         } 
        } else { 

        } 
       } 

      } 

     } else { 
      print("error") 
     } 

    }) 


} 


func commentObservers() { 

    if postKeyForCommentGroup != nil { 

     constantHandle = DataService.ds.REF_POST_COMMENT_GROUP.child(postKeyForCommentGroup).observe(.value, with: { (snapshot) in 

      if snapshot.value != nil { 

       if let snapshots = snapshot.children.allObjects as? [FIRDataSnapshot], snapshots.count > 0 

       { 

        self.comments = [] 

        for snap in snapshots { 

         if let theCommentIDForEachComment = snap.key as String? { 
          DataService.ds.REF_COMMENTS.child(theCommentIDForEachComment).queryOrdered(byChild: "timePosted").observeSingleEvent(of: .value, with: { (snapshots) in 

           if let commentDict = snapshots.value as? Dictionary<String, AnyObject> { 

            let key = snapshots.key 
            let comment = Comment(commentKey: key, dictionary: commentDict) 
            self.comments.insert(comment, at: 0)  
           } 
           self.tableView.reloadData() 
          }) 
         } 
        } 
       } 

      } else { 

      } 
     }) 

    } else { 

    } 

} 

UPDATE:私はどのように考え出し

クエリと以前のstackoverflowの記事で概説したデリゲートパターンを使用します。

getting data out of a closure that retrieves data from firebase

しかし、私は正しくデリゲートパターンを使用していた場合、私は知りません。

コードはクエリを使用して簡略化されていますが、依然として点滅しています。たぶん私はデリゲートパターンを正しく使用していないでしょうか?

func commentGroupObserver() { 
    DataService.ds.REF_POST_COMMENT_GROUP.queryOrderedByKey().queryStarting(atValue: post.postKey).queryEnding(atValue: post.postKey).observeSingleEvent(of: .value, with: { (snapshot) in 
     self.postKeyForCommentGroup = self.post.postKey 
     self.commentObservers() 
    }) 

} 

func commentObservers() { 
    if postKeyForCommentGroup != nil { 
     constantHandle = DataService.ds.REF_POST_COMMENT_GROUP.child(postKeyForCommentGroup).observe(.value, with: { (snapshot) in 
      if snapshot.value != nil { 
       if let snapshots = snapshot.children.allObjects as? [FIRDataSnapshot] 
       { 
        self.comments = [] 
        for snap in snapshots { 
         if let theCommentIDForEachComment = snap.key as String? { 
          DataService.ds.REF_COMMENTS.child(theCommentIDForEachComment).queryOrdered(byChild: "timePosted").observe(.value, with: { (snapshots) in 

           if let commentDict = snapshots.value as? Dictionary<String, AnyObject> { 

            let key = snapshots.key 
            let comment = Comment(commentKey: key, dictionary: commentDict) 
            self.comments.insert(comment, at: 0) 

           } 

           self.didFetchData(comments: self.comments) 

          }) 



         } 

        } 

       } 

      } else { 

      } 
     }) 

    } else { 

    } 

} 

func didFetchData(comments data:[Comment]){ 
    self.tableView.reloadData() 
} 

}

とプロトコル

protocol MyDelegate{ 
func didFetchData(comments:[Comment]) } 

それを解決した私の最後のコード:ジェイの提案の際

私は不要postCommentGroupを排除し、単にUIDを照会コメントがコメントの下にある投稿の投稿:

func commentObservers() { 

    let queryRef = DataService.ds.REF_COMMENTS.queryOrdered(byChild: "postID").queryEqual(toValue: self.post.postKey) 

    queryRef.observe(.value, with: { snapshot in 

     if let snapshots = snapshot.children.allObjects as? [FIRDataSnapshot] { 

      for snap in snapshots { 

       if let commentDict = snap.value as? Dictionary<String, AnyObject> { 
        let key = snap.key 
        let comment = Comment(commentKey: key, dictionary: commentDict) 
        self.comments.insert(comment, at: 0) 
       } 
      } 
     } 

     self.tableView.reloadData() 
    }) 
} 
+1

データ構造の画像を削除し、テキストデータに置き換えてください。そうすれば、コピー&ペーストが可能で検索が可能です。 JSON構造をエクスポートするには、Firebaseコンソールとデータベースに移動し、ウィンドウ内の右隅にある「3つの垂直方向の点」から「JSONの書き出し」を選択します。 – Jay

+0

.valueを使用して一度読み取ると、同じデータをすべて.childAddedでもう一度読み込んでいるときに、データを二重に読み込んで(ちらつきを引き起こす)可能性があります。しかし、それはそうではありません - 知るべき質問には十分なコードがありません。 – Jay

+0

イメージを削除し、編集したバージョンのデータ(JSON構造)をアップロードしました。私は、この質問を見るかもしれない人々に混乱していたので、childAddedで2番目の観測を削除しました。私はこれまで、データをロードするさまざまな方法をテストするためにこれを追加しました。 childAddedでの2回目の観察の削除は、ちらつきには何の影響もなく、追加する前に点滅していました。 – jasonhdev

答えて

1

あなたのアプローチは、単純化することによって調整する必要があります。私はナットとボルトのすべてを提供したかったので少し長めにしてそれを単純化することができました。

正規化は正常ですが、これは必須ではなく、場合によってはさらに複雑さを増す可能性があります。構造体postCommentGroupの 'layer'は不要です。

投稿を含むビューコントローラと、ユーザーが最初のコントローラの投稿をタップしたときにコメントを表示する2番目のビューコントローラがあるようです。

class CommentClass { 
    var commentKey = "" 
    var comment = "" 
    var likes = "" 
} 

var postsArray = [ [String: [String:AnyObject] ] ]() 
var commentsArray = [CommentClass]() 

コードへ:あなたは本当に唯一のポストノードやコメントノード

posts 
    post_id_0 
    title: "my post title" 
    caption: "some caption" 
    uid: "uid_0" 
    post_id_1 
    title: "another post title 
    caption: "another caption 
    uid: "uid_0" 

とポストに

comments 
    comment_0 
    post_id: "post_id_0" 
    uid: "uid_1" 
    likes: "10" 
    comment_1 
    post_id: "post_id_0" 
    uid: "uid_1" 
    likes: "7" 
    comment_2 
    post_id: "post_id_1" 
    uid: "uid_1" 
    likes: "2" 

を参照するコメントノードのセットアップを必要とする

すべての投稿を読み込みます:

let postsRef = ref.child("posts") 

    postsRef.observeSingleEvent(of: .value, with: { snapshot in 

     for snap in snapshot.children { 
      let postSnap = snap as! FIRDataSnapshot 
      let postKey = postSnap.key //the key of each post 
      let postDict = postSnap.value as! [String:AnyObject] //post child data 

      let d = [postKey: postDict] 
      self.postsArray.append(d) 
     } 
     //postsTableView.reloadData 
     print(self.postsArray) //just to show they are loaded 
    }) 

ユーザーが投稿をタップすると、コメントを読み込んで表示します。

self.commentsArray = [] //start with a fresh array since we tapped a post 
    //placeholder, this will be the post id of the tapped post 
    let postKey = "post_id_0" 
    let commentsRef = ref.child("comments") 
    let queryRef = commentsRef.queryOrdered(byChild: "post_id") 
           .queryEqual(toValue: postKey) 

    //get all of the comments tied to this post 
    queryRef.observeSingleEvent(of: .value, with: { snapshot in 

     for snap in snapshot.children { 
      let commentSnap = snap as! FIRDataSnapshot 
      let commentKey = commentSnap.key //the key of each comment 
      //the child data in each comment 
      let commentDict = commentSnap.value as! [String:AnyObject] 
      let comment = commentDict["comment"] as! String 
      let likes = commentDict["likes"] as! String 
      let c = CommentClass() 
      c.commentKey = commentKey 
      c.comment = comment 
      c.likes = likes 

      self.commentsArray.append(c) 
     } 

     //commentsTableView.reload data 

     //just some code to show the posts are loaded 
     print("post: \(postKey)") 
     for aComment in self.commentsArray { 
      let comment = aComment.comment 
      print(" comment: \(comment)") 
     } 
    }) 

得られた出力

post: post_id_0 
    comment: I like post_id_0 
    comment: post_id_0 is the best evah 

上記のコードを試験し、フリッカを有していません。明らかに、あなたがいくつかのイメージをロードするなど、あなたのユースケースのために調整する必要がありますが、上記の問題を解決し、よりメンテナンス可能にする必要があります。

+0

ありがとうございました。あなたの時間をありがとう。私はこれが最善のアプローチだと思う。これは実際にプロジェクトの1回の反復でデータがどのように構造化されたかということです。そのため、問題を修正したオブザーバーコードを簡単に修正できました。 – jasonhdev

+0

簡単な質問が1つありますが、それはSOのq/aフォーマットではどれくらい適切かはわかりませんが、このようにコストがかかりますか?クエリするのが悪いことはありますか?私はそれがコストのかかることであることをどこかで読んだので、それを避けようとしていたのです。 – jasonhdev

+0

@jasonhdev答えが助けられたら、それを受け入れてください!追加のコストはかかりません。クエリに「悪い」ことはありません。しかし、クエリのオーバーヘッドは観測値よりも大きいので、クエリの代わりに観測できるのであれば、アプリケーションの方が効率的です。実際には次のようになります。ノード内のデータ、またはノード内のデータの選択*すべてを*したいですか?選択はデータが少ないため、ダウンロードが効率的でバイト数が減ります。 Firebaseプランの費用は、あなたが保管している金額と月にどれだけダウンロードしたかに基づいています。フリースパークプランは1ヶ月に10G、1Gに保存されます。開発のためにたくさん。 – Jay

関連する問題