DispatchGroup с функциями asyn c в базе данных Firebase Realtime DB в Swift - PullRequest
1 голос
/ 27 января 2020

Я разрабатываю приложение для чата iOS, которое использует базу данных Firebase Realtime для хранения сообщений. У меня есть функция, которая вызывается при загрузке экрана домашнего чата. Эта функция загружает имя получателя, последнее сообщение, метку времени и профиль pi c. Я использовал DispatchGroup для синхронизации c всех вызовов. Сначала я думал, что это работает, но когда я отправляю новое сообщение (каким-либо образом обновляю БД), приложение вылетает. Я полагаю, что это потому, что закрытие наблюдения вызывается снова, и между вызовами входа / выхода есть дисбаланс. Я не могу придумать, как заставить это работать с 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 ед. Поэтому здесь у меня есть массив всех чатов.

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 голосов
/ 05 февраля 2020

Итак, я понял это с помощью обработчика завершения с обработчиком начала.

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. Вызовы выхода сбалансированы, потому что они вызываются из обработчиков завершения / начала или изнутри наблюдателя пожарной базы. Я не думаю, что это лучший способ решить эту проблему, но это определенно один из способов. Если кто-то знает лучшее решение - пожалуйста, дайте мне знать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...