質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Firebase

Firebaseは、Googleが提供するBasSサービスの一つ。リアルタイム通知可能、並びにアクセス制御ができるオブジェクトデータベース機能を備えます。さらに認証機能、アプリケーションのログ解析機能などの利用も可能です。

Cloud Firestore

Cloud Firestore は、自動スケーリングと高性能を実現し、アプリケーション開発を簡素化するように構築された NoSQLドキュメントデータベースです。

iOS

iOSとは、Apple製のスマートフォンであるiPhoneやタブレット端末のiPadに搭載しているオペレーションシステム(OS)です。その他にもiPod touch・Apple TVにも搭載されています。

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

Q&A

1回答

1161閲覧

Firestore:コメント一覧で過去にいいね済みのものを反映する方法

maztak

総合スコア61

Firebase

Firebaseは、Googleが提供するBasSサービスの一つ。リアルタイム通知可能、並びにアクセス制御ができるオブジェクトデータベース機能を備えます。さらに認証機能、アプリケーションのログ解析機能などの利用も可能です。

Cloud Firestore

Cloud Firestore は、自動スケーリングと高性能を実現し、アプリケーション開発を簡素化するように構築された NoSQLドキュメントデータベースです。

iOS

iOSとは、Apple製のスマートフォンであるiPhoneやタブレット端末のiPadに搭載しているオペレーションシステム(OS)です。その他にもiPod touch・Apple TVにも搭載されています。

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

0グッド

0クリップ

投稿2021/02/10 09:43

編集2021/02/10 09:48

前提・実現したいこと

投稿に対してコメントでき、そのコメントにいいねできるようなInstagramやTikTokのようなSNSアプリをSwift×Firestoreで構築しています。コメント一覧はTableViewで表示しています。

コメント一覧を開いたときに、過去にいいねしたコメントにはいいね済みである状態(いいねボタンの画像を塗りつぶしたハートに変え、再度押したらいいね取り消しになる状態)を反映したいです。
イメージ説明

Firestoreの構造は以下です(【】はコレクション、{}はドキュメント)。

【posts】 - {postId} - 【comments】 - {commentId} - 【commentLikedUsers】 - {UID} L postId L commentId // 念の為持たせてみた L uid // ドキュメントIdに持たせているので重複して気持ち悪い L createTime

コメントのいいね数は最高で数千〜数万、1回/s以上の書き込みにも耐えうるように作っておきたく、ドキュメントフィールドにArrayとしていいねしたユーザーのuidを持たせるのではなくて、サブコレクションcommentLikedUsers配下にドキュメントを作っていく形にしています。

各CommentCellの生成ごとにcommentLikedUsersリスナーを設定するのはリスナーが多すぎ重すぎる処理になる思っております。

一方で、コレクショングループクエリで以下のように取得するのも「1投稿のコメント一覧を表示するたびに、全投稿の全コメントのcommentLikedUsersコレクションに対してクエリするって重すぎないんだろうか‥」となり、いい方法が浮かびません。

swift

1db.collectionGrop("commentLikedUsers") 2.where('postId', '==', self.post.postId) 3.where('uid', '==', self.user.uid) 4.addSnapshotLitener()

よくある実装だとは思うのですが、過去に自分がいいね済みのコメントに、表示時点で「いいね済み」状態を反映させるいい方法はないでしょうか?

該当のソースコード

上記のFirestore構造でコメント一覧をtableViewで表示する部分だけのコードです(いいねボタンをいじるコードは抜いてます)。

swift

1class PostCommentViewController: UIViewController { 2 3 // MARK: - Variable 4 @IBOutlet weak var tableView: UITableView! 5 6 let db = Firestore.firestore() 7 var commentsRef: CollectionReference? 8 var commentsListener: ListenerRegistration? 9 var user: User! 10 11 var post: Post! 12 var comments: [Comment] = [] 13 14 override func viewDidLoad() { 15 super.viewDidLoad() 16 tableView.delegate = self 17 tableView.dataSource = self 18 } 19 20 override func viewWillAppear(_ animated: Bool) { 21 super.viewWillAppear(animated) 22 user = Auth.auth().currentUser 23 commentsRef = db.collection("posts").document(self.post.postId).collection("comments") 24 addCommentsListener() 25 } 26 27 func addCommentsListener() { 28 self.commentsListener = commentsRef? 29 .addSnapshotListener { (snapshot, error) in 30 if let err = error { 31 print(err) 32 return 33 } else { 34 guard let snapshot = snapshot else { return } 35 snapshot.documents.forEach { (doc) in 36 let comment = Comment(snapshot: doc) 37 self.comments.append(comment) 38 } 39 self.tableView.reloadData() 40 } 41 } 42 } 43} 44 45// MARK: - Tableview Delegate 46extension PostCommentViewController: UITableViewDelegate, UITableViewDataSource { 47 48 func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 49 return self.comments.count 50 } 51 52 func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 53 let cell = tableView.dequeueReusableCell(withIdentifier: "CommentCell", for: indexPath) as! CommentCell 54 cell.displayNameLabel.text = self.comments[indexPath.row].displayName 55 cell.bodyLabel.text = self.comments[indexPath.row].body 56 if let imgURL = self.comments[indexPath.row].profileImageURL { 57 // セルのimageViewにプロフィール画像を設定する処理 58 } 59 60 // ここに過去にいいねしたコメントならいいねボタンを「いいね済み」に変える処理を書きたい 61 62 cell.delegate = self 63 cell.indexPath = indexPath 64 return cell 65 } 66} 67 68// MARK: - CommentCell Delegate 69extension PostCommentViewController: CommentCellDelegate { 70 71 func addToLikedUsers(indexPath: IndexPath) { 72 let commentId = self.comments[indexPath.row].commentId 73 self.commentsRef?.document(commentId).collection("commentlikedUsers").document(self.user.uid) 74 .setData([ 75 "postId": self.post.postId, 76 "commentId": commentId, 77 "uid": self.user.uid, 78 "createTime": FieldValue.serverTimestamp(), 79 ], merge: true, completion: nil) 80 } 81 82 func removeFromLikedUsers(indexPath: IndexPath) { 83 let commentId = self.comments[indexPath.row].commentId 84 self.commentsRef?.document(commentId).collection("commentlikedUsers").document(self.user.uid) 85 .delete(completion: nil) 86 } 87}

swift

1import UIKit 2 3protocol CommentCellDelegate { 4 func addToLikedUsers(indexPath: IndexPath) 5 func removeFromLikedUsers(indexPath: IndexPath) 6} 7 8class CommentCell: UITableViewCell { 9 @IBOutlet weak var profileImageView: UIImageView! 10 @IBOutlet weak var displayNameLabel: UILabel! 11 @IBOutlet weak var bodyLabel: UILabel! 12 @IBOutlet weak var likeButton: UIButton! 13 14 var delegate: CommentCellDelegate? 15 var indexPath: IndexPath! 16 17 var isLiked: Bool = false 18 19 override func awakeFromNib() { 20 super.awakeFromNib() 21 } 22 23 @IBAction func didTapLikeButton(_ sender: UIButton) { 24 if isLiked { 25 self.isLiked = false 26 self.delegate?.removeFromLikedUsers(indexPath: indexPath) 27 } else { 28 self.isLiked = true 29 self.delegate?.addToLikedUsers(indexPath: indexPath) 30 } 31 } 32}

swift

1import FirebaseFirestore 2 3struct Comment { 4 var commentId: String 5 var uid: String? 6 var displayName: String? 7 var profileImageURL: String? 8 var body: String? 9 10 init (snapshot: DocumentSnapshot) { 11 self.commentId = snapshot.documentID 12 guard let data = snapshot.data() else { return } 13 if let uid = data["uid"] as? String { 14 self.uid = uid 15 } 16 if let displayName = data["displayName"] as? String { 17 self.displayName = displayName 18 } 19 if let profileImageURL = data["profileImageURL"] as? String { 20 self.profileImageURL = profileImageURL 21 } 22 if let body = data["body"] as? String { 23 self.body = body 24 } 25 } 26 27}

##試したこと

各commentのドキュメントフィールドにArrayでlikedUsersをもたせる方法であれば出来ました。ただ将来的に技術的負債となりそうですし、コメントがいいねされた時にユーザーに通知する処理をCloud Functionsでやろうとしたときにもサブコレクション式にした方が都合がよかったりしたので、ご教授いただけると嬉しいです。

swift

1import FirebaseFirestore 2 3struct Comment { 4 // 省略 5 likedUsers: [String]? 6 7 init (snapshot: DocumentSnapshot) { 8 self.commentId = snapshot.documentID 9 // 省略 10 if let likedUsers = data["likedUsers"] as? [String] { 11 self.likderUsers = likedUsers 12 } 13 } 14}

swift

1// MARK: - Tableview Delegate 2extension PostCommentViewController: UITableViewDelegate, UITableViewDataSource { 3 4 // 省略 5 6 func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 7 let cell = tableView.dequeueReusableCell(withIdentifier: "CommentCell", for: indexPath) as! CommentCell 8 // 省略 9 if let likedUsers = self.comments[indexPath.row].likderUsers { 10 if likedUsers.contains(self.user.uid) { 11 cell.likeButton.setImage(UIImage(systemName: "heart.fill"), for: .normal) 12 cell.isLiked = true 13 } else { 14 cell.isLiked = false 15 cell.likeButton.setImage(UIImage(systemName: "heart"), for: .normal) 16 } 17 cell.likedCountLabel.isHidden = false 18 cell.likedCountLabel.text = String(likedUsers.count) 19 } 20 // 省略 21 return cell 22 } 23} 24 25// MARK: - CommentCell Delegate 26extension PostCommentViewController: CommentCellDelegate { 27 28 func addToLikedUsers(indexPath: IndexPath) { 29 let commentId = self.comments[indexPath.row].commentId 30 self.commentsRef?.document(commentId).updateData([ 31 "likedUsers" : FieldValue.arrayUnion([self.user.uid]) 32 ]) 33 } 34 35 func removeFromLikedUsers(indexPath: IndexPath) { 36 let commentId = self.comments[indexPath.row].commentId 37 self.commentsRef? 38 .document(commentId).updateData([ 39 "likedUsers" : FieldValue.arrayRemove([self.user.uid]) 40 ]) 41 } 42}

##利用環境

  • Swift5

##参考にしたサイト

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

Userには何が収まっているのでしょうか?

【posts】 - {postId} - 【comments】 - {commentId} - 【commentLikedUsers】 - {UID}
L postId
L commentId // 念の為持たせてみた
L uid // ドキュメントIdに持たせているので重複して気持ち悪い
L createTime

、とありますが、どういう意味かわかりません。まずFireBaseでどういうデータを扱っているのかを説明したらどうでしょうか?

投稿2021/02/11 04:11

tomato879241

総合スコア133

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問