问题
I have the following function which fetches a list of users using a Geofire then a Firebase query in a nested setup.
I first execute the Geofire query to get the key
and I then execute the Firebase query and create the User
object that matches the key
appending the User
to an array which is then passed back via the handler
.
The User
array
is then use in a CollectionView
, the problem is that by the time the CollectionView
is displayed it only shows one User
object.
I have placed some print()
lines through out the code for debugging and have found that the function is passing data via handler
every time an item is appended and therefore the count of the array by the time is reaches the VC
which is displaying the CollectionView
is 1
.
Moving the following line to outside of the for loop
parentheses passes an empty array
How do I update my function so that the handler
is only called once and passes a complete array
?
handler(self.shuffleArray(array: filteredUsers) as! [User], true)
Here is the function:
//Fetches all users currently at a Venue location of radius 50 metres
func getUsersAtVenue(forVenueLocation venueLocation: CLLocation, forUid uid: String, handler: @escaping (_ users: [User], _ success: Bool) -> ()){
print("uid: \(uid)")
var users = [User]()
guard let currentLocation = LocationSingleton.sharedInstance.lastLocation else { return}//get current user's (device) location
let distanceApart = round(10 * (venueLocation.distance(from: currentLocation) / 1000)) / 10 //get distance between currentLocation and venueLocation and convert from Mts to Kms rounding to 2 decimals
if distanceApart <= 50 { //if distance apart if within 50kms then proceed
let query = self.GEOFIRE_USERS_LOC.query(at: currentLocation, withRadius: 50)//radius in Kms
query.observe(.keyEntered) { (key: String!, userLocation: CLLocation!) in
print(key)
self.REF_USERS.observeSingleEvent(of: .value, with: { (snapshot) in
guard let usersSnapshot = snapshot.children.allObjects as? [DataSnapshot] else { return }
for user in usersSnapshot{
let discoverable = user.childSnapshot(forPath: "discoverable").value as! Bool
if user.key == key && discoverable == true {
let uid = user.key
let name = user.childSnapshot(forPath: "name").value as! String
let email = user.childSnapshot(forPath: "email").value as! String
let profilePictureURL = user.childSnapshot(forPath: "profilePictureURL").value as! String
let birthday = user.childSnapshot(forPath: "birthday").value as! String
let firstName = user.childSnapshot(forPath: "firstName").value as! String
let lastName = user.childSnapshot(forPath: "lastName").value as! String
let gender = user.childSnapshot(forPath: "gender").value as! String
let discoverable = user.childSnapshot(forPath: "discoverable").value as! Bool
let online = user.childSnapshot(forPath: "online").value as! Bool
let dictionary: [String : Any] = ["uid": uid, "name": name, "email": email, "profilePictureURL": profilePictureURL, "birthday": birthday, "firstName": firstName, "lastName": lastName, "gender": gender, "discoverable": discoverable, "online": online]
let user = User(uid: uid, dictionary: dictionary)
users.append(user)
}//end if
}//end for
//filter out current user from array
let filteredUsers = users.filter({ (user: User) -> Bool in return !user.uid.contains(uid) })
print("filteredUsers count: \(filteredUsers.count)")
//handler passing a shuffled version of the array
handler(self.shuffleArray(array: filteredUsers) as! [User], true)
})//end FIR snapshot call
}//end geoquery
} else {//if distanace apart is NOT within 50kms then do this
print("You're too far apart.")
handler(users, false)
}
}//end func
Console:
uid: dBQd541pxlRypR7l1WT2utKVxdX2
some("dBQd541pxlRypR7l1WT2utKVxdX5")
some("dBQd541pxlRypR7l1WT2utKVxdX3")
some("dBQd541pxlRypR7l1WT2utKVxdX2")
some("dBQd541pxlRypR7l1WT2utKVxdX4")
filteredUsers count: 1
users count: 1
filteredUsers count: 2
users count: 2
filteredUsers count: 2
users count: 2
filteredUsers count: 3
users count: 3
回答1:
The .keyEntered
event is fired for every key that is or comes within range of your geoquery. So initially that means it fires for all keys within range, and from that moment on it will fire whenever a new key comes in range (i.e. if you add a new user in range, or if a user moves to within range).
It sounds like you want to detect when .keyEntered
has been called for all initial users. For that you can observe the ready event.
query.observeReadyWithBlock({
//handler passing a shuffled version of the array
handler(self.shuffleArray(array: filteredUsers) as! [User], true)
})
If you're not interested in getting updates on new/moving users after the initial query, this is also a great moment to remove your observer by calling removeObserverWithFirebaseHandle
or removeAllObservers
.
Also see waiting for queries to be 'ready' in the Geofire documentation.
来源:https://stackoverflow.com/questions/50722753/geofire-firebase-function-is-executing-handler-multiple-times-in-swift