Наблюдатель.childAdded не вызывается, если функция не вызывается явно. Swift 4

У меня есть функция, которая должна прослушивать узел Firebase и получать моментальный снимок новых сообщений, когда они публикуются, но функция вообще не отключается, как если бы наблюдатель .observe(DataEventType.childAdded, with: { (snapshot) in не видел новых сообщений в узле. Я проверил, и новые сообщения действительно зарегистрированы в реальном времени в Firebase. Должен ли я вызвать функцию или наблюдатель должен это сделать? Вот полная функция:

func getNewerAlerts(setCompletion: @escaping (Bool) -> ()) {

        print("                     MapArray.alertNotificationCoordinatesArray before  getNewerAlerts snapshot is: \(MapArray.alertNotificationCoordinatesArray)")
        print("                     self.userAlertNotificationArray before getNewerAlerts snapshot is: \(self.userAlertNotificationArray)")

        ref = Database.database().reference()

        ref?.child("Continent").child("Europe").child("Country").child("Italy").child("Region").child("Emilia-Romagna").child("City").child("Bologna").child("Community").child("Alert Notifications").observe(DataEventType.childAdded, with: { (snapshot) in
            print("         snapshot is: \(snapshot)")
            guard let data = snapshot.value as? [String:[String:String]] else { return }
            guard let firebaseKey = snapshot.key as? String else { return }
            //                let date = data!["Date"]
            //                let time = data!["Time"]
            data.values.forEach {
                let dataLatitude = $0["Latitude"]!
                let dataLongitude = $0["Longitude"]!

                let type = $0["Description"]!
                let id = Int($0["Id"]!)
                let doubledLatitude = Double(dataLatitude)
                let doubledLongitude = Double(dataLongitude)
                let recombinedCoordinate = CLLocationCoordinate2D(latitude: doubledLatitude!, longitude: doubledLongitude!)

                //            print("Firebase alerts posts retrieved")

                let userAlertAnnotation = UserAlert(type: type, coordinate: recombinedCoordinate, firebaseKey: firebaseKey, title: type,id: id!)

                self.mapView.addAnnotation(userAlertAnnotation)
                self.userAlertNotificationArray.append(userAlertAnnotation)
                MapArray.alertNotificationCoordinatesArray.append(recombinedCoordinate)
            }
            print("                 MapArray.alertNotificationCoordinatesArray after getNewerAlerts snapshot is: \(MapArray.alertNotificationCoordinatesArray)")
            print("                     self.userAlertNotificationArray after getNewerAlerts snapshot is: \(self.userAlertNotificationArray)")
            setCompletion(true)
        })

    }

Большое спасибо.

0 ответов

Вместо долгой дискуссии в комментариях, давайте попробуем ответить на это парой ссылок и примером кода.

Во-первых, синхронизировать данные следует только тогда, когда представление является видимым, и метод viewWillAppear вызывается каждый раз, когда представление становится видимым, поэтому это хорошее место для добавления наблюдателей. Также хорошей практикой является удаление наблюдателей, когда они вам не нужны (экономит пропускную способность), и это можно сделать с помощью маркера firebase в viewDidDisappear. Вот немного устаревшая статья, но отличное чтение

Лучшие практики для UIViewController и Firebase

и для хорошего примера, смотрите ответ на этот вопрос

Firebase: когда вызывать removeObserverWithHandle в swift

Для решения остальной части вопроса (обратите внимание, я держу это коротким, поэтому я не включил использование ручек)

У меня есть класс для хранения оповещений

class AlertClass {
    var node_key = ""
    var msg = ""

    init(aKey: String, aMsg: String) {
        self.node_key = aKey
        self.msg = aMsg
    }
}

а затем массив var класса для хранения всех предупреждений

var alertArray = [AlertClass]()

а затем мы добавляем наших наблюдателей из функции viewWillAppear

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

который добавляет трех наблюдателей к указанному узлу;.childAdded,.childChanged и.childRemoved. Имейте в виду, что.childAdded будет перебирать узлы в узле ref и заполнять наш источник данных каждый раз, когда вызывается viewWillAppear, поэтому нам нужно "сбросить" массив, чтобы мы случайно не загрузили данные поверх существующих данных. Ваш вариант использования может отличаться, поэтому используйте соответствующий код.

Вот код для добавления наблюдателей и распечатки массива каждый раз, когда происходит изменение.

func addObservers() {
    let ref = self.ref.child("Continent").child("Europe").child("Country").child("Italy").child("Region").child("Emilia-Romagna").child("City").child("Bologna").child("Community").child("Alert Notifications")
    self.alertArray = []
    ref.observe(.childAdded, with: { (snapshot) in
        let key = snapshot.key
        let msg = snapshot.childSnapshot(forPath: "msg").value as! String
        let aAlert = AlertClass(aKey: key, aMsg: msg)
        self.alertArray.append(aAlert) //append the new alert
        self.showAlertArray() //this is called for every child
    })

    ref.observe(.childChanged, with: { (snapshot) in
        let key = snapshot.key
        let msg = snapshot.childSnapshot(forPath: "msg").value as! String
        if let foundAlert = self.alertArray.first(where: { $0.node_key == key } ) {
            foundAlert.msg = msg //update the alert msg
            self.showAlertArray()
        }
    })

    ref.observe(.childRemoved, with: { (snapshot) in
        let key = snapshot.key
        self.alertArray.removeAll(where: { $0.node_key == key }) //remove the alert
        self.showAlertArray()
    })
}

func showAlertArray() {
    for alert in self.alertArray {
        print(alert.node_key, alert.msg)
    }
}

и как примечание стороны...

Если вы заполняете источник данных tableView через childAdded, вам может быть интересно, как это сделать без повторного вызова tableView.reloadData, что может вызвать мерцание. Есть способ сделать это, используя тот факт, что события.value вызываются после.childAdded. Смотрите мой ответ на этот вопрос для примера.

Другие вопросы по тегам