Swift Firebase -Как добавить наблюдателя в viewDidLoad, удалить его в viewDidDisappear, затем снова прочитать его в viewWillAppear?

#ios #swift #firebase #firebase-realtime-database #uicollectionview

#iOS #swift #firebase #firebase-realtime-database #uicollectionview

Вопрос:

У меня есть журнал чата, и сначала я .observe( .childAdded) вызвал своего слушателя viewDidLoad и удалил его, viewDidDisappear но после прочтения этого комментария введите описание изображения здесь из этого вопроса я переместил слушателя viewWillAppear , и теперь всякий раз, когда я переключаю просмотры и возвращаюсь, слушатель запускается снова и снова, и мои табличные данные заполняются одной и той же информацией дважды.

Например. скажем, у меня есть только 1 сообщение в чате от другого пользователя, в котором говорится «привет». Если чат vc находится в tabTwo, при первом появлении vc код будет запущен, и в источнике данных CollectionView появится 1 сообщение с надписью «привет». Если я нажму другую вкладку и вернусь к tabTwo, теперь будет 2 сообщения с надписью «привет» (то же самое точное сообщение), потому что код повторялся снова и снова. Это становится серьезной проблемой, если имеется 100 сообщений. Это означает, что будет 200 сообщений, и они будут добавляться каждый раз, когда я переключаю вкладки и возвращаюсь.

Должен ли я также очищать табличные данные и перезагружать CollectionView в viewDidDisappear при удалении наблюдателя, чтобы все данные не дублировались при повторном вызове viewWillAppear? Это то, что я сделал, чтобы решить проблему, но это не кажется правильным.

Есть ли лучший способ вызвать наблюдателя в viewDidLoad, а затем снова прочитать его в viewWillAppear, не заставляя мой CollectionView дублировать данные?

 let currentUserId = Auth.auth().currentUser?.uid!
let toId = "theOtherUsersId"

var tableData = [Message]()
let ref = Database.database().reference().child("messageIds")

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)

    ref.child(currentUserId).child(toId).observe( .childAdded, with: { [weak self] (snapshot) in

        let messageId = snapshot.key

        self?.getMessagesWith(messageId)
    })
}

func getMessagesWith(_ messageId: String) {

    Database.database().reference().child("messages").child(messageId).observeSingleEvent(of: .value, with: { [weak self] (snapshot) in

        guard let dict = snapshot.value as? [String: Any] else { return }

        let message = Message(dict: dict)

        self?.tableData.append(message)
        self?.collectionView.reloadData()
    })
}

override func viewDidDisappear(_ animated: Bool) {
    super.viewDidDisappear(animated)

    tableData.removeAll() // something seems wrong about doing this
    collectionView.reloadData()
    ref.removeAllObservers()
}
  

Ответ №1:

комментарии @GaloTorresSevilla под моим первоначальным ответом были правильными. Он предложил переместить наблюдателя в viewWillAppear и просто использовать MessageId для фильтрации любых сообщений, которые уже существуют в табличных данных. Он был на 100% правильным, потому что, когда я возвращаюсь на вкладку, данные не дублируются

 let currentUserId = Auth.auth().currentUser?.uid!
let toId = "theOtherUsersId"

var tableData = [Message]()
let ref = Database.database().reference().child("messageIds")

override func viewDidLoad() {
    super.viewDidLoad()
    // nothing to do here
}

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)

    ref.child(currentUserId).child(toId).observe( .childAdded, with: { [weak self] (snapshot) in

        let messageId = snapshot.key

        // in viewWillAppear if the tableData contains the messageId above then return
        if self.tableData.contains (where: { $0.messageId ?? “” == messageId }) {
            return
        }
        self?.getMessagesWith(messageId)
    })
}

func getMessagesWith(_ messageId: String) {

    Database.database().reference().child("messages").child(messageId).observeSingleEvent(of: .value, with: { [weak self] (snapshot) in

        guard let dict = snapshot.value as? [String: Any] else { return }

        let message = Message(dict: dict)

        self?.tableData.append(message)
        self?.collectionView.reloadData()
    })
}

override func viewDidDisappear(_ animated: Bool) {
    super.viewDidDisappear(animated)

    ref.child(currentUserId).child(toId).removeAllObservers()
}
  

Комментарии:

1. Возможно, вы не получаете никаких дубликатов, но как именно вы будете получать новые сообщения в этом случае? Ваши наблюдатели удаляются viewDidDisappear и инициализируются обратно viewWillAppear . Затем, если придет новое сообщение, как это сообщение будет добавлено, если новый viewWillAppear наблюдатель не вызывает getMessagesWith метод? Я думаю, что лучший способ для вас сделать это — иметь UID для каждого сообщения и выполнить сравнение, чтобы проверить, есть ли сообщение уже в источнике данных

2. Я думал о том же, но когда я это сделал, добавилось новое сообщение.

3. @GaloTorresSevilla это хорошая идея для проверки каждого идентификатора сообщения

4. Если вы все еще получаете сообщения, это, скорее всего, потому, что у вас есть сильная ссылка viewDidLoad . В какой-то момент это приведет к утечке. Я действительно думаю, что сопоставимый с UID сообщений является лучшим вариантом.

5. @GaloTorresSevilla Я выяснил, почему все работает, хотя это и не предполагалось. Наблюдатели не удалялись, поэтому пустая ссылка, которую я имею в viewWillAppear, ничего не делала, потому что ссылка из viewDidLoad все еще использовалась. Во всяком случае, я переключил все на viewWillAppear и использовал MessageId для фильтрации, как вы предложили, и это работает на 100%. Если вы опубликуете это в качестве ответа, я соглашусь, потому что это работает. Я также собираюсь удалить этот ответ. Я оставляю его здесь только для того, чтобы связаться с вами и сообщить, что ваша идея сработала.