前提・実現したいこと
投稿に対してコメントでき、そのコメントにいいねできるような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
##参考にしたサイト
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。