Ios 在TableView单元格中处理从Firebase检索数据的更好方法
在我的聊天功能中,我在tableview中向当前用户显示他与其他用户的聊天记录。tableview上的每个单元格都是对方用户的信息、化身和姓名。将这些信息存储在聊天对象中很容易,但问题是,对方用户可能在此时更改了他的化身或姓名 因此,我在聊天室的手机配置中获取对方用户的头像和姓名。它工作得很好,但我不确定是否有更好的方法,因为我不赞成在每个单元中进行Firebase网络呼叫 以下是我的单元配置方法:Ios 在TableView单元格中处理从Firebase检索数据的更好方法,ios,swift,firebase,google-cloud-firestore,Ios,Swift,Firebase,Google Cloud Firestore,在我的聊天功能中,我在tableview中向当前用户显示他与其他用户的聊天记录。tableview上的每个单元格都是对方用户的信息、化身和姓名。将这些信息存储在聊天对象中很容易,但问题是,对方用户可能在此时更改了他的化身或姓名 因此,我在聊天室的手机配置中获取对方用户的头像和姓名。它工作得很好,但我不确定是否有更好的方法,因为我不赞成在每个单元中进行Firebase网络呼叫 以下是我的单元配置方法: func configureCell(from chat: Chat){ //
func configureCell(from chat: Chat){
// 1st Get opposite user id
if let currentUser = Auth.auth().currentUser{
let user1 = chat.people.first
let user2 = chat.people.last
let user1Id = user1?["id"] ?? ""
let user2Id = user2?["id"] ?? ""
var oppositeUserId = ""
if user1Id == currentUser.uid{
oppositeUserId = user2Id
}else{
oppositeUserId = user1Id
}
//2nd Fetch opposite user doc
let oppositeUserDocRef = References.Instance.getUserDocRef(for: oppositeUserId)
oppositeUserDocRef.getDocument { (oppositeUserDocSnapshot, error) in
if error != nil{
print("ERROR FETCHING OPPOSITE USER DOC:: ERROR IS: \(error?.localizedDescription ?? "")")
return
}
// 3rd get opposite user avatar url and name
if let otherUserDic = oppositeUserDocSnapshot?.data(){
if let avatarDic = otherUserDic["avatar"] as? [String: String]{
let avatarUrl = avatarDic["url"] ?? ""
self.avatarView.sd_setImage(with: URL(string: avatarUrl),
placeholderImage: nil, options: .refreshCached)
}
if let name = otherUserDic["name"] as? String{
self.uiLabelChatTitle.text = name
}
}
}
}
}
我从未真正使用过Firebase,但对于类似的东西,我会说在一个网络请求中填充一个“chatDetails”对象数组,然后配置每个可重用单元
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let chat = self.chatDetails[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: "identifier") as! ChatDetailsCell
cell.configureCell(chat: chat)
return cell
}
正如他们在评论中所建议的那样,预取数据将是一个更好的解决方案。这很棘手,取决于每个应用程序的细节。我的目标是模仿WhatsApp的聊天列表功能,实时更新用户信息 以下是我为实现这一目标所做的:
func showChats(){
guard let currentUser = Auth.auth().currentUser else{
return
}
let profileController = UserProfileController.instance
if let snapshot = profileController.chats{
for document in snapshot.documents{
let chat = ChatController.instance.getChat(from: document)
chats.append(chat)
print("GOT CHAT:::: \(document.data())")
}
tableview.reloadData()
}
else{
print("NOTHING IN INBOX!!!!")
}
// Attach listeners to chat users docs to listen for change in avatars and names
// 1st: Loop through each chat to get people and know opposite user id
for (i, var chat) in chats.enumerated(){
let person1 = chat.people.first
let person2 = chat.people.last
let person1Id = person1?["id"] ?? ""
let person2Id = person2?["id"] ?? ""
var oppositeUserId = ""
var opposteUsrIsPerson1 = false
if person1Id == currentUser.uid{
oppositeUserId = person2Id
opposteUsrIsPerson1 = false
}
else{
oppositeUserId = person1Id
opposteUsrIsPerson1 = true
}
// 2nd: Fetch opposite user doc and add listener to it
let oppositeUserDocRef = References.Instance.getUserDocRef(for: oppositeUserId)
let docListener = oppositeUserDocRef.addSnapshotListener { (oppositeUserDocSnapshot, error) in
if error != nil {
print("ERROR FETCHING OTHER PERSON DOC:: ERROR IS: \(error?.localizedDescription ?? "")")
return
}
// 3rd: get other user avatar url and name from his doc
if let oppositeUserDic = oppositeUserDocSnapshot?.data(){
var avatarUrl = ""
var name = ""
if let avatarDic = oppositeUserDic["avatar"] as? [String: String]{
avatarUrl = avatarDic["url"] ?? ""
}
if let oppositeUsrName = oppositeUserDic["name"] as? String{
name = oppositeUsrName
}
// 4th: Create ChatPerson object with the fetched values and replace the existing one
let chatPerson = ChatPerson(id: oppositeUserId, name: name, avatarUrl: avatarUrl)
if opposteUsrIsPerson1{
chat.people[0] = chatPerson.toDictionalry()
}
else{
chat.people[1] = chatPerson.toDictionalry()
}
// 5th: Update data and reload the chat row
self.chats[i] = chat
let indexpath = IndexPath(row: i, section: 0)
self.tableview.reloadRows(at: [indexpath], with: .automatic)
}
}
//6th: Add doc listener to liteners list to remove it later in deinit()
chatUserListeners.append(docListener)
}
}
在视图控制器的deinit()循环中,通过侦听器删除它们:
deinit {
for listener in chatUserListeners{
listener.remove()
}
}
问题是我需要从每个文档中获取每个相反用户的姓名和头像。我可以在第一次创建聊天对象时输入这些信息,但对方用户可能在某个时候更改了自己的姓名或头像,在这种情况下,聊天对象信息将过时那么,如果对方用户在每个小区配置完毕后更改了他们的姓名或化身呢?该更改不会反映在表视图中。因此,无论您是同时获取每个单元格的所有数据,还是单独获取每个单元格的所有数据,您都必须调用reloadData方法。为了澄清这一点,Firebase有观察者监视数据库的更改。当发生更改时,它会向应用程序通知包含更改数据的事件。firebase observer代码闭包将使用新信息更新tableView数据源,并且可以仅重新加载tableView中的该行,或者如您所述重新加载所有行。如果firebase具有此功能,这显然是正确的选择。感谢各位的建议,我用监听器来监听每个聊天室用户的变化,并发布了我的答案。我不赞成在每个手机中拨打Firebase网络电话。真为你高兴。你说得对。请不要这样做,它可能是滞后的,有时给用户一个糟糕的体验。TableView是为高性能而设计的,因此工作越少越好。您应该使用tableView的数据预填充tableView数据源。然后将观察者连接到存储/跟踪用户化身的节点,并观察其变化。发生更改时,观察员应使用新信息更新tableView数据源,然后刷新tableView。感谢您的建议。我认为性能不会有问题,因为Firebase调用是在后台进行的。我更担心的是,我的列表在填充之前会在每个单元格中不断进行网络呼叫,这在金钱上是很昂贵的。我得想个办法。预取可以工作,但很棘手,因为可能会有很多聊天,我需要遍历每个聊天用户来获取他的数据。我认为他们使用批处理函数来实现for循环中的调用。我找到了答案并发布了我的答案,谢谢你的建议。