Соберите список пользователей с Geofire/Firebase

У меня есть класс с именем User, у которого есть функция, которая получает все близлежащие фургоны с едой, используя GeoFire. Я использовал наблюдающийРедийный блок, чтобы взять идентификаторы грузовиков, возвращенные GeoFire, и получить остальную их информацию, используя Firebase. Однако, когда я добавляю их имя и описание, чтобы получить доступ к одному из грузовиков из моего массива объектов Truck, похоже, xCode сообщает, что массив пуст.

Я планирую использовать этот массив соседних грузовиков в других классах контроллеров, чтобы заполнить таблицы, показывающие все соседние грузовики и некоторую основную информацию для пользователя.

Как я могу правильно заполнить мой массив грузовиков и что я могу ошибаться, основываясь на приведенном ниже коде. Спасибо большое!

func getNearbyTrucks(){
    //Query GeoFire for nearby users
    //Set up query parameters
    let center = CLLocation(latitude: 37.331469, longitude: -122.029825)
    let circleQuery = geoFire.queryAtLocation(center, withRadius: 100)

    circleQuery.observeEventType(GFEventTypeKeyEntered, withBlock: { (key: String!, location: CLLocation!) in

        let newTruck = Truck()
        newTruck.id = key
        newTruck.currentLocation = location
        self.nearbyTrucks.append(newTruck)

    }) //End truckQuery

    //Execute code once GeoFire is done with its' query!
    circleQuery.observeReadyWithBlock({

        for truck in self.nearbyTrucks{

            ref.childByAppendingPath("users/\(truck.id)").observeEventType(.Value, withBlock: { snapshot in
                print(snapshot.value["name"] as! String)

                truck.name = snapshot.value["name"] as! String
                truck.description = snapshot.value["selfDescription"] as! String
                let base64String = snapshot.value["profileImage"] as! String
                let decodedData = NSData(base64EncodedString: base64String as String, options: NSDataBase64DecodingOptions.IgnoreUnknownCharacters)
                truck.photo = UIImage(data: decodedData!)!
            })
        }

    }) //End observeReadyWithBlock

    print(nearbyTrucks[0].id)
    //This line gives the error that the array index is out of range
}

1 ответ

Решение

Данные из Geofire и остальной части вашей базы данных Firebase не просто "получены" из базы данных. Он асинхронно загружается, а затем постоянно синхронизируется. Это меняет поток вашего кода. Это легче всего увидеть, добавив некоторые записи:

func getNearbyTrucks(){
    //Query GeoFire for nearby users
    //Set up query parameters
    let center = CLLocation(latitude: 37.331469, longitude: -122.029825)
    let circleQuery = geoFire.queryAtLocation(center, withRadius: 100)

    print("Before Geoquery")

    circleQuery.observeEventType(GFEventTypeKeyEntered, withBlock: { (key: String!, location: CLLocation!) in
        print("In KeyEntered block ")

        let newTruck = Truck()
        newTruck.id = key
        newTruck.currentLocation = location
        self.nearbyTrucks.append(newTruck)

    }) //End truckQuery

    print("After Geoquery")
}

Вывод журнала будет в другом порядке, чем вы можете ожидать:

Перед геоквери

После Геоквери

В блоке KeyEntered

В блоке KeyEntered

...

Пока гео-ключи и пользователи извлекаются с сервера, код продолжается и getNearbyTrucks() выходит до того, как будут возвращены какие-либо ключи или пользователи.

Один из распространенных способов справиться с этим - изменить способ представления кода с "сначала загрузите грузовики, затем напечатайте первый грузовик" на "всякий раз, когда загружаются грузовики, печатайте первый".

В коде это переводится как:

func getNearbyTrucks(){
    //Query GeoFire for nearby users
    //Set up query parameters
    let center = CLLocation(latitude: 37.331469, longitude: -122.029825)
    let circleQuery = geoFire.queryAtLocation(center, withRadius: 100)

    circleQuery.observeEventType(GFEventTypeKeyEntered, withBlock: { (key: String!, location: CLLocation!) in

        let newTruck = Truck()
        newTruck.id = key
        newTruck.currentLocation = location
        self.nearbyTrucks.append(newTruck)

        print(nearbyTrucks[0].id)
    }) //End truckQuery

    //Execute code once GeoFire is done with its' query!
    circleQuery.observeReadyWithBlock({

        for truck in self.nearbyTrucks{

            ref.childByAppendingPath("users/\(truck.id)").observeEventType(.Value, withBlock: { snapshot in
                print(snapshot.value["name"] as! String)

                truck.name = snapshot.value["name"] as! String
                truck.description = snapshot.value["selfDescription"] as! String
                let base64String = snapshot.value["profileImage"] as! String
                let decodedData = NSData(base64EncodedString: base64String as String, options: NSDataBase64DecodingOptions.IgnoreUnknownCharacters)
                truck.photo = UIImage(data: decodedData!)!
            })
        }

    }) //End observeReadyWithBlock
}

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

Более повторно используемый подход - это тот, который используют сами базы данных Firebase и Geofire: вы передаете блок в observeEventType withBlock: и этот блок содержит код для запуска, когда ключ доступен. Если вы примените тот же шаблон к вашему методу, он станет:

func getNearbyTrucks(withBlock: (key: String) -> ()){
    //Query GeoFire for nearby users
    //Set up query parameters
    let center = CLLocation(latitude: 37.331469, longitude: -122.029825)
    let circleQuery = geoFire.queryAtLocation(center, withRadius: 100)

    circleQuery.observeEventType(GFEventTypeKeyEntered, withBlock: { (key: String!, location: CLLocation!) in

        let newTruck = Truck()
        newTruck.id = key
        newTruck.currentLocation = location
        self.nearbyTrucks.append(newTruck)

        withBlock(nearbyTrucks[0].id)
    }) //End truckQuery

    //Execute code once GeoFire is done with its' query!
    circleQuery.observeReadyWithBlock({

        for truck in self.nearbyTrucks{

            ref.childByAppendingPath("users/\(truck.id)").observeEventType(.Value, withBlock: { snapshot in
                print(snapshot.value["name"] as! String)

                truck.name = snapshot.value["name"] as! String
                truck.description = snapshot.value["selfDescription"] as! String
                let base64String = snapshot.value["profileImage"] as! String
                let decodedData = NSData(base64EncodedString: base64String as String, options: NSDataBase64DecodingOptions.IgnoreUnknownCharacters)
                truck.photo = UIImage(data: decodedData!)!
            })
        }

    }) //End observeReadyWithBlock
}

Здесь вы захотите переместить withBlock() Обратный звонок в более подходящее место в зависимости от ваших потребностей.

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