显示与Firestore聊天时的最新消息

时间:2019-12-04 17:17:35

标签: ios swift firebase google-cloud-firestore

如何在我的聊天中获取并显示最后一条消息

为了进行测试,我创建了四个用户,这些用户具有测试消息。现在,我只能为所有用户显示最新消息。我将颜色标记为红色

我还使用Firebase保存消息并创建频道

firebase中的结构如下:

- Chats 
    - channel id 
        - document data (then be stored ID and NAME of channel)
        - collection thread
            - documents data (then be stored MESSAGES)

我在频道中的结构:

struct Channel {
    let id: String?
    let name: String

    init(name: String) {
        id = nil
        self.name = name
    }

    init?(document: DocumentSnapshot) {
        let data = document.data()!
        guard let name = data["name"] as? String else {
            return nil
        id = document.documentID
        self.name = name
    }
}

extension Channel: DatabaseRepresentation {
    var representation: [String : Any] {
        var rep = ["name": name]
        if let id = id {
            rep["id"] = id
        }
        return rep
    }
}

我的结构消息,我使用MessageKit

struct Message: MessageType {

let id: String?
let content: String
let sentDate: Date
let sender: SenderType

var kind: MessageKind {
    if let image = image {
        return .photo(ImageMediaItem.init(image: image))
    } else {
        return .text(content)
    }
}

var messageId: String {
    return id ?? UUID().uuidString
}

var image: UIImage? = nil
var downloadURL: URL? = nil

init(profile: Profile, content: String) {
    sender = Sender(id: profile.id, displayName: profile.name)
    self.content = content
    sentDate = Date()
    id = nil
}

init?(document: QueryDocumentSnapshot) {
    let data = document.data()

    guard let sentDate = (data["created"] as? Timestamp)?.dateValue() else { 
        return nil
    }

    guard let senderID = data["senderID"] as? String else {
        return nil
    }
    guard let senderName = data["senderName"] as? String else {
        return nil
    }

    id = document.documentID

    self.sentDate = sentDate
    sender = Sender(id: senderID, displayName: senderName)

    if let content = data["content"] as? String {
        self.content = content
        downloadURL = nil
    } else if let urlString = data["url"] as? String, let url = URL(string: urlString) {
        downloadURL = url
        content = ""
    } else {
        return nil
    }
}

}

扩展消息:DatabaseRepresentation {

var representation: [String : Any] {
    var rep: [String : Any] = [
        "created": sentDate,
        "senderID": sender.senderId,
        "senderName": sender.displayName 
    ]

    if let url = downloadURL {
        rep["url"] = url.absoluteString
    } else {
        rep["content"] = content
    }

    return rep
}

}

要加载我的通道,请使用以下代码:

fileprivate func observeQuery() {
    guard let query = query else { return }
    listener = query.addSnapshotListener { (snapshot, error) in
        guard let snapshot = snapshot else {
            print("Error listening for channel updates: \(error?.localizedDescription ?? "No error")")
            return
        }

        snapshot.documentChanges.forEach { (change) in
            self.handleDocumentChange(change)
        }
    }
}

private func handleDocumentChange(_ change: DocumentChange) {
    guard let channel = Channel(document: change.document) else {
        return
    }
    switch change.type {
    case .added:
        addChannelToTable(channel)
    case .modified:
        updateChannelInTable(channel)
    case .removed:
        removeChannelFromTable(channel)
    }
}

private func addChannelToTable(_ channel: Channel) {
    guard !channels.contains(channel) else {
        return
    }
    channels.append(channel)
    channels.sort()

    guard let index = channels.index(of: channel) else {
        return
    }
    tableView.insertRows(at: [IndexPath(row: index, section: 0)], with: .automatic)
}

private func updateChannelInTable(_ channel: Channel) {
    guard let index = channels.index(of: channel) else {
        return
    }
    channels[index] = channel
    tableView.reloadRows(at: [IndexPath(row: index, section: 0)], with: .automatic)
}

private func removeChannelFromTable(_ channel: Channel) {
    guard let index = channels.index(of: channel) else {
        return
    }
    channels.remove(at: index)
    tableView.deleteRows(at: [IndexPath(row: index, section: 0)], with: .automatic)
}

我认为需要更新我的频道结构。但是怎么做呢?

以及如何纠正加载并显示 firebase 中的最新消息

如果需要更多信息,请告诉我,我会更新我的问题。

enter image description here

1 个答案:

答案 0 :(得分:0)

如果问题是如何仅从Firestore获取最后一条消息,则需要定义如何确定最后一条消息是什么。这通常是通过时间戳完成的-最新的时间戳将成为最后一条消息。

问题的结构尚不清楚,所以让我提供一个简单的例子。

messages //collection
   document_0 //documentID auto-generated
      msg: "Last Message"
      timestamp: "20191201"
   document_1
      msg: "First message"
      timestamp: "20190801"
   document_2
      msg: "A message in the middle"
      timestamp: "20191001"

如您所见,无论将它们写入Firestore的顺序如何,很显然带有最新时间戳(20191201)的消息都是最后一条消息。

要获取最后一条消息,我们需要执行以下两项操作的查询:

1)查询message节点,按降序排序,这会将最后一条消息置于“顶部”

2)将查询限制为1,这将获得该消息。

func readLastMessage() {
    let ref = Firestore.firestore().collection("messages").order(by: "timestamp", descending: true).limit(to: 1)
    ref.getDocuments(completion: { querySnapshot, error in
        guard let snapshot = querySnapshot else {
            print("Error fetching snapshots: \(error!)")
            return
        }

        if let doc = snapshot.documents.first {
            let docID = doc.documentID
            let msg = doc.get("msg")
            print(docID, msg)
        }
    })
}

和输出

Last Message

以上代码获取了最后一条消息,但是可以通过添加观察者而不是getDocuments进行扩展,其方式与出现新的最后一条消息时通知应用程序的方式相同。