Assemble a list of users with Geofire/Firebase

百般思念 提交于 2020-07-05 12:42:01

问题


I have a class called User, which has a function that gets all nearby food trucks using GeoFire. I've used an observeReadyWithBlock to take the truck IDs returned by GeoFire, and get the rest of their information using Firebase. However, when I go to access one of the trucks from my array of Truck objects after adding their name and description, it looks like xCode is telling me the array is empty.

I am planning on using this array of nearby trucks in other controller classes, to populate tables showing all of the nearby trucks and some basic information to the user.

How can I properly populate my array of Trucks, and what could I be getting wrong based on the code below. Thanks very much!

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:


The data from Geofire and the rest of your Firebase Database is not simply "gotten" from the database. It is asynchronously loaded and then continuously synchronized. This changes the flow of your code. This is easiest to see by adding some logging:

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")
}

The output of the logging will be in a different order from what you may expect:

Before Geoquery

After Geoquery

In KeyEntered block

In KeyEntered block

...

While the Geo-keys and users are being retrieved from the server, the code continues and getNearbyTrucks() exits before any keys or users are returned.

One common way to deal with this is to change the way you think of your code from "first load the trucks, then print the firs truck" to "whenever the trucks are loaded, print the first one".

In code this translates to:

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
}

I've moved the printing of the first truck into the block for the key entered event. Depending on the actual code you're trying to run, you'll move it into different places.

A more reusable approach is the one the Firebase Database and Geofire themselves use: you pass a block into observeEventType withBlock: and that block contains the code to be run when a key is available. If you apply the same pattern to you method, it'd become:

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
}

Here again, you'll want to move the withBlock() callback to a more suitable place depending on your needs.



来源:https://stackoverflow.com/questions/38581573/assemble-a-list-of-users-with-geofire-firebase

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!