在 Swift 中的 Firebase 实时数据库观察方法中具有异步函数的 DispatchGroup

Posted

技术标签:

【中文标题】在 Swift 中的 Firebase 实时数据库观察方法中具有异步函数的 DispatchGroup【英文标题】:DispatchGroup with async functions in a Firebase Realtime DB observe method in Swift 【发布时间】:2020-01-27 14:03:07 【问题描述】:

我正在开发一个 ios 聊天应用程序,它使用 Firebase 实时数据库来存储消息。我有一个在加载主聊天屏幕时调用的函数。此函数加载收件人姓名、最后一条消息、时间戳和个人资料图片。 我使用 DispatchGroup 来同步所有呼叫。起初,我认为它有效,但是当我发送一条新消息(以任何方式更新数据库)时,应用程序崩溃了。我相信这是因为再次调用了观察闭包,并且进入/离开调用之间存在不平衡。 我想不出一种让它与 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。还是没有运气。

【问题讨论】:

【参考方案1】:

所以我通过使用完成处理程序和开始处理程序来解决这个问题。

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 观察者内部调用的。 我不认为这是处理这个问题的最好方法,但它绝对是一种方法。如果有人知道更好的解决方案 - 请告诉我。

【讨论】:

以上是关于在 Swift 中的 Firebase 实时数据库观察方法中具有异步函数的 DispatchGroup的主要内容,如果未能解决你的问题,请参考以下文章

iOS Swift 中的 firebase 实时数据库推送等价物是啥?

在 Swift 中的 Firebase 实时数据库观察方法中具有异步函数的 DispatchGroup

在 Swift 中更新 Firebase 实时数据库中的父标题

Swift 和 Firebase 实时数据库:如何获取对象的数量?

在 Firebase 实时数据库中实现用户名(swift 4)

从firebase实时数据库IOS Swift中检索数据