Swift 4 Firebase下仅允许用户删除自身评论的代码异常排查
问题描述
我刚实现了用户对他人帖子评论并删除自己评论的功能,但最初的代码允许所有人删除任意评论。之后我尝试修改代码限制仅能删除自己的评论,但现在遇到了两个问题:
- 只有UITableView第一行的评论能正常触发删除验证逻辑
- 删除其他行时,会在
let usersArray = self.uidArray[indexPath.row] as! [String: AnyObject]处触发索引越界错误
数据库结构正常,但我无法定位问题根源,希望得到技术帮助。
初始代码(存在任意删除问题)
func getKeysValue() { // let uid = Auth.auth().currentUser?.uid Database.database().reference().child("posts").child(postsKey).child("comments").observe( .value) { (snapshot) in for child in snapshot.children { let snap = child as! DataSnapshot let commentKey = snap.key self.keyArray.insert(commentKey, at: 0) // print(self.keyArray) // print("Here is the specific Key\(self.commentKey)") } } } func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) { if editingStyle == .delete { getKeysValue() let when = DispatchTime.now() + 1 DispatchQueue.main.asyncAfter(deadline: when, execute: { Database.database().reference().child("posts").child(postsKey).child("comments").child(self.keyArray[indexPath.row]).removeValue() self.commentsTableView.reloadData() }) } }
修改后的代码(出现索引越界问题)
func getKeysValue() { // let uid = Auth.auth().currentUser?.uid Database.database().reference().child("posts").child(postsKey).child("comments").observe( .value) { (snapshot) in for child in snapshot.children { let snap = child as! DataSnapshot let commentKey = snap.key self.keyArray.insert(commentKey, at: 0) // print(self.keyArray) // print("Here is the specific Key\(self.commentKey)") } } } var uid = Auth.auth().currentUser?.uid var userID = String() var idUser = String() var userUID = String() func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool { return true } func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) { if editingStyle == .delete { getKeysValue() let when = DispatchTime.now() + 1 DispatchQueue.main.asyncAfter(deadline: when, execute: { self.userID = self.keyArray[indexPath.row] self.idUser = self.userID print(" Here is the indexPath key: \(self.idUser)") Database.database().reference().child("posts").child(postsKey).child("comments").child(self.idUser).observeSingleEvent(of: .value, with: { (snapshot) in if let postsDictionary = snapshot.value as? [String: AnyObject] { self.uidArray.insert(postsDictionary as NSDictionary, at: 0) let usersArray = self.uidArray[indexPath.row] as! [String: AnyObject] let usersKey = usersArray["uid"] as? String self.userUID = usersKey! print("Here is the Comment:\(self.uidArray)") print("And here is the UID::: \(self.userUID)") if self.uid != self.userUID { let logInAlert = UIAlertController(title: "Failed to delete", message: "You can only delete your own comment(s)", preferredStyle: .actionSheet) logInAlert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil)) self.present(logInAlert, animated: true, completion: nil) }else { let commentSuccess = self.storyboard?.instantiateViewController(withIdentifier: "CommentReloadVC") self.present(commentSuccess!, animated: false, completion: nil) Database.database().reference().child("posts").child(postsKey).child("comments").child(self.keyArray[indexPath.row]).removeValue() self.commentsTableView.reloadData() } } }) }) } }
问题根源分析
你的代码出现索引越界和仅第一行有效的问题,主要有以下几个核心原因:
- 数组插入逻辑错误:你每次获取评论数据时都用
insert(_:at:0)把元素插在头部,导致uidArray和keyArray的顺序与UITableView行索引完全不匹配——比如第二行评论对应的数组索引其实是1,但你插入的新元素都挤在0位,用indexPath.row取值时自然只有第一行能匹配,其他行直接越界。 - 异步操作与数组管理混乱:
getKeysValue()是异步的,用DispatchQueue.main.asyncAfter延迟1秒等待数据的做法完全不可靠,而且每次触发删除都会重复调用它,导致keyArray不断重复添加元素,数组长度越来越大,索引彻底混乱。- 删除逻辑中每次获取UID都往
uidArray插元素,却从不清空旧数据,数组越攒越长,索引完全对应不上。
- 冗余操作:删除成功后跳转到
CommentReloadVC完全没必要,直接刷新当前TableView即可。
修正后的解决方案
我重构了你的代码,解决所有问题的同时优化了逻辑严谨性:
步骤1:统一数据模型与数组管理
先定义一个评论模型类(也可以用字典数组),把评论的key和uid绑定在一起,确保数据与TableView行索引一一对应:
// 评论模型,存储必要的删除验证信息 struct Comment { let key: String let uid: String // 可以添加其他字段,比如评论内容、时间戳等 } var comments: [Comment] = [] // 替代原来的keyArray和uidArray let currentUserUID = Auth.auth().currentUser?.uid ?? "" // 提前获取当前用户UID,避免重复调用
步骤2:重构评论数据获取逻辑
改成一次性获取所有评论并解析成Comment数组,确保数据顺序与TableView一致:
func fetchComments() { comments.removeAll() // 先清空旧数据,避免重复 Database.database().reference().child("posts").child(postsKey).child("comments").observeSingleEvent(of: .value) { snapshot in for child in snapshot.children { guard let snap = child as? DataSnapshot, let commentDict = snap.value as? [String: Any], let commentUID = commentDict["uid"] as? String else { continue // 安全解包,跳过无效数据 } let comment = Comment(key: snap.key, uid: commentUID) self.comments.append(comment) } // 如果你想让最新评论在顶部,反转数组即可 self.comments.reverse() self.commentsTableView.reloadData() } }
步骤3:优化TableView编辑与删除逻辑
在canEditRowAt中直接判断当前行是否属于当前用户,避免删除时再查数据库;删除时直接用评论的key操作数据库:
// 控制哪些行显示删除按钮——只有自己的评论才允许删除 func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool { let comment = comments[indexPath.row] return comment.uid == currentUserUID } // 处理删除操作 func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) { if editingStyle == .delete { let commentToDelete = comments[indexPath.row] // 删除数据库中的评论 Database.database().reference().child("posts").child(postsKey).child("comments").child(commentToDelete.key).removeValue { error, _ in if let error = error { // 处理删除失败的情况 let alert = UIAlertController(title: "删除失败", message: error.localizedDescription, preferredStyle: .alert) alert.addAction(UIAlertAction(title: "OK", style: .default)) self.present(alert, animated: true) } else { // 删除本地数组元素并刷新TableView self.comments.remove(at: indexPath.row) self.commentsTableView.deleteRows(at: [indexPath], with: .automatic) } } } }
步骤4:初始化加载评论
在viewDidLoad中调用fetchComments()加载初始数据:
override func viewDidLoad() { super.viewDidLoad() fetchComments() // 如果需要实时监听评论变化,可以把observeSingleEvent改成observe(.value),但要注意每次回调都清空数组 }
额外优化建议
- 避免强制解包:代码里的
!强制解包很容易导致崩溃,尽量用guard let或if let安全解包。 - 数据库规则验证:除了客户端验证,一定要在Firebase数据库规则中添加权限校验,确保只有评论所有者才能删除评论,防止恶意用户绕过客户端逻辑。
- 实时更新:如果需要实时同步评论变化,把
observeSingleEvent改成observe(.value)即可,但要注意每次回调都先清空comments数组再重新加载。
内容的提问来源于stack exchange,提问作者Jiyar
相关产品推荐
相关产品推荐

