Firebase Realtime DB中具有异步功能的DispatchGroup在Swift

时间:2020-01-27 14:03:07

标签: ios swift firebase firebase-realtime-database

我正在开发一个使用Firebase实时数据库存储消息的iOS聊天应用程序。我有一个在加载家庭聊天屏幕时调用的函数。此功能加载收件人姓名,最后一条消息,时间戳和个人资料图片。 我已经使用DispatchGroup来同步所有呼叫。起初,我认为它可以工作,但是当我发送新消息(以任何方式更新数据库)时,应用程序崩溃。我相信这是因为watch闭包被再次调用,并且enter / leave调用之间存在不平衡。 我想不出一种方法来使其与DispatchGroup一起使用。有没有办法来解决这个问题?还是有比DispatchGroup更好的选择?

这是firebase观察器的主要功能:

func getAllChatsForCurrentUser(completion: @escaping (_ chats: [Chat], _ error: Error?) -> Void) {
        var chats = [Chat]()
        let group = DispatchGroup()
        let currentUserUID = Auth.auth().currentUser!.uid
        let chatRef = Database.database().reference(withPath: "chats")
        group.enter()
        chatRef.observe(.value) { (snapshot) in
            var childrenArray = [String]()
            let children = snapshot.children
            while let rest = children.nextObject() as? DataSnapshot {
                childrenArray.append(rest.key)                         //1
            }
            for child in childrenArray {
                if child.contains(currentUserUID) {                    //2
                    let otherUserUID = child.replacingOccurrences(of: currentUserUID, with: "")
                    group.enter()
                    self.getChatInfo(uid: otherUserUID, chatID: child) { (chat, err) in
                        chats.append(chat)
                        group.leave()
                    }
                }
            }
            group.leave()
        }
        group.notify(queue: .main) {
            completion(chats, nil)
        }

    }

1 -对于聊天名称,我使用2个uid的组合。所以这里有所有聊天记录。

2 -如果聊天名称包含当前用户的uid-我正在使用它。字符串另一部分中的收件人uid。

getChatInfo函数如下:

func getChatInfo(uid: String, chatID: String, completion: @escaping (_ chat: Chat, _ error: Error?) -> Void) {
        let miniGroup = DispatchGroup()
        var newChat = Chat()
        newChat.otherUserUid = uid
        miniGroup.enter()
        self.getUserProfileFromUID(uid: uid) { (user, error) in
            newChat.name = user.name
            newChat.profilePic = user.photoURL
            miniGroup.leave()
        }
        miniGroup.enter()
        self.getLastMessageAndTimeForChat(chatID: chatID) { (message, time, error) in
            newChat.lastMessage = message
            newChat.lastMessageTime = time
            miniGroup.leave()
        }
        miniGroup.notify(queue: .main) {
            completion(newChat, nil)
        }
    }

我知道这可能是构造数据和调用函数的一种不好的方法。至少有人告诉我,没有任何理由。由于此问题困扰了将近一个星期,任何信息将不胜感激。

更新1 尝试将leave()调用包装在defer {}中,并尝试使用NSOperations代替DispatchGroup。还是没有运气。

Adding a screenshot of my DB for reference.

1 个答案:

答案 0 :(得分:0)

所以我通过将完成处理程序与begin处理程序一起使用来弄清楚了。

getChatsWithBeginAndComplete(beginHandler: {
            self.group.enter()
            self.group.notify(queue: .main) {
                print("done")
                self.tableView.reloadData()
            }
        }) {
            self.group.leave()
        }

函数:

func getChatsWithBeginAndComplete(beginHandler: @escaping () -> (), completionHandler: @escaping () -> ()) {
       allChatsHandle = allChatsRef.observe(.value) { (snapshot) in
           let bigGroup = DispatchGroup()
           beginHandler()
           var childrenArray = [String]()
           let children = snapshot.children
           while let rest = children.nextObject() as? DataSnapshot {
               childrenArray.append(rest.key)
           }
           for chatID in childrenArray {
               if chatID.contains(currentUserUID) {
                   bigGroup.enter()
                   let funcGroup = DispatchGroup()

                   //Do more async stuff in the funcGroup

                   funcGroup.notify(queue: .main) {
                       self.chats.append(chat)
                       bigGroup.leave()
                   }
               }
           }

           bigGroup.notify(queue: .main) {
               completionHandler()
           }

       }
   }

因此,这里的所有group.enter和group.leave调用都是平衡的,因为它们是从完成/开始处理程序或Firebase观察器内部调用的。 我认为这不是解决此问题的最佳方法,但绝对是一种方法。如果有人知道更好的解决方案-请让我知道。