Соберите список пользователей с 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()
Обратный звонок в более подходящее место в зависимости от ваших потребностей.