You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Firebase Swift聊天应用:如何获取每个联系人的最后一条消息

解决Swift Firebase聊天应用展示联系人最后一条消息的问题

嘿,我来帮你搞定这个需求!要在首页菜单显示每个联系人的最后一条消息,核心思路要么是维护一个专门存储最后消息的节点(推荐,性能更好),要么是直接查询每个联系人的消息记录取最后一条。下面给你详细的实现方案:

方案一:维护单独的「最后消息」节点(推荐)

这种方式是在发送消息时,同步更新一个专门的lastMessages节点,这样首页读取时直接拉取这个节点的数据,效率很高,不用反复查询完整的消息历史。

步骤1:定义数据模型

先创建一个结构体来存储联系人信息和最后消息:

struct ChatUser {
    let userId: String
    let name: String?
    let lastMessage: String?
    let lastMessageTimestamp: TimeInterval?
}

步骤2:实现getAllMsg()函数

这个函数会读取当前用户的lastMessages节点,结合用户信息组装成列表:

func getAllMsg() {
    self.users = []
    guard let currentUserId = Auth.auth().currentUser?.uid else { 
        print("用户未登录")
        return 
    }
    
    // 指向当前用户的最后消息节点
    let lastMessagesRef = Database.database().reference().child("lastMessages").child(currentUserId)
    
    // 监听节点变化,实时更新列表
    lastMessagesRef.observe(.value) { snapshot in
        guard let lastMessagesDict = snapshot.value as? [String: [String: Any]] else {
            self.users = []
            self.tableView.reloadData()
            return
        }
        
        // 清空旧数据,避免重复添加
        self.users.removeAll()
        
        for (recipientId, messageData) in lastMessagesDict {
            let lastMessage = messageData["content"] as? String ?? ""
            let timestamp = messageData["timestamp"] as? TimeInterval ?? 0
            
            // 获取联系人的基本信息(比如昵称)
            let userRef = Database.database().reference().child("users").child(recipientId)
            userRef.observeSingleEvent(of: .value) { userSnapshot in
                guard let userData = userSnapshot.value as? [String: Any] else { return }
                let userName = userData["name"] as? String ?? "未知用户"
                
                let chatUser = ChatUser(
                    userId: recipientId,
                    name: userName,
                    lastMessage: lastMessage,
                    lastMessageTimestamp: timestamp
                )
                
                self.users.append(chatUser)
                // 按最后消息时间排序,最新的对话排在最前面
                self.users.sort { $0.lastMessageTimestamp ?? 0 > $1.lastMessageTimestamp ?? 0 }
                
                self.tableView.reloadData()
            }
        }
    }
}

步骤3:发送消息时同步更新最后消息节点

在你发送消息的函数里,加上更新lastMessages的逻辑,确保两端用户的最后消息都同步:

func sendMessage(to recipientId: String, content: String) {
    guard let currentUserId = Auth.auth().currentUser?.uid else { return }
    let timestamp = Date().timeIntervalSince1970
    
    // 组装消息数据
    let messageData = [
        "content": content,
        "senderId": currentUserId,
        "timestamp": timestamp
    ] as [String: Any]
    
    // 更新当前用户的最后消息记录
    Database.database().reference()
        .child("lastMessages")
        .child(currentUserId)
        .child(recipientId)
        .setValue(messageData)
    
    // 更新收件人的最后消息记录
    Database.database().reference()
        .child("lastMessages")
        .child(recipientId)
        .child(currentUserId)
        .setValue(messageData)
    
    // 同时把消息存入完整的聊天历史节点(可选,保留所有对话记录)
    let messageHistoryRef = Database.database().reference()
        .child("messages")
        .child(currentUserId)
        .child(recipientId)
        .childByAutoId()
    messageHistoryRef.setValue(messageData)
}

方案二:直接查询每个联系人的最后一条消息

如果不想维护额外节点,也可以直接查询每个联系人的消息历史,取时间戳最新的那一条。但注意:如果联系人数量多,这种方式会发起大量查询,性能较差,仅适合联系人较少的场景。

实现代码

func getAllMsg() {
    self.users = []
    guard let currentUserId = Auth.auth().currentUser?.uid else { return }
    
    // 先获取当前用户的所有联系人(假设你有一个contacts节点存储联系人ID)
    let contactsRef = Database.database().reference().child("contacts").child(currentUserId)
    contactsRef.observe(.value) { snapshot in
        guard let contactIds = snapshot.value as? [String: Bool] else {
            self.users = []
            self.tableView.reloadData()
            return
        }
        
        self.users.removeAll()
        
        for recipientId in contactIds.keys {
            // 查询该联系人的最后一条消息:按时间戳倒序,取第一条
            let messagesRef = Database.database().reference()
                .child("messages")
                .child(currentUserId)
                .child(recipientId)
                .queryOrdered(byChild: "timestamp")
                .queryLimited(toLast: 1)
            
            messagesRef.observeSingleEvent(of: .value) { messageSnapshot in
                var lastMessage: String? = nil
                var timestamp: TimeInterval = 0
                
                if let messageDict = messageSnapshot.children.allObjects.first as? DataSnapshot,
                   let messageData = messageDict.value as? [String: Any] {
                    lastMessage = messageData["content"] as? String
                    timestamp = messageData["timestamp"] as? TimeInterval ?? 0
                } else {
                    // 没有消息的情况
                    lastMessage = "暂无消息"
                }
                
                // 获取联系人信息并添加到列表
                self.fetchUserInfoAndAdd(recipientId: recipientId, lastMessage: lastMessage, timestamp: timestamp)
            }
        }
    }
}

// 封装获取用户信息的逻辑,避免重复代码
func fetchUserInfoAndAdd(recipientId: String, lastMessage: String?, timestamp: TimeInterval) {
    let userRef = Database.database().reference().child("users").child(recipientId)
    userRef.observeSingleEvent(of: .value) { userSnapshot in
        guard let userData = userSnapshot.value as? [String: Any] else { return }
        let userName = userData["name"] as? String ?? "未知用户"
        
        let chatUser = ChatUser(
            userId: recipientId,
            name: userName,
            lastMessage: lastMessage,
            lastMessageTimestamp: timestamp
        )
        
        self.users.append(chatUser)
        self.users.sort { $0.lastMessageTimestamp ?? 0 > $1.lastMessageTimestamp ?? 0 }
        self.tableView.reloadData()
    }
}

额外提示

  • 记得在viewWillDisappear里移除Firebase监听,避免内存泄漏:比如lastMessagesRef.removeAllObservers()
  • 如果使用Firestore而非Realtime Database,思路类似:可以维护一个lastMessages集合,或者用orderBy("timestamp", descending: true).limit(to: 1)查询最后一条消息
  • 排序逻辑很重要,让最新的对话始终排在列表最上方

内容的提问来源于stack exchange,提问作者William Loke

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 10:57:32