Reputation: 357
I've made an SwiftUI App, which gets its Firestore Data via multiple Firebase cloud functions. The structure of the Firestore Document i want to get is the following:
Now, i want to call a cloud function called "getLocationObjectsFromUser" which gets all LocationIds from the user related collection locationIds. Then, i want to get all Data from the Location Document with the specific locationId, including the collection "UserIds".
I've tried something like this, but in this case, the firebase functions log always tells me that the function has finished, although it has not finished getting all data. Because of that, my swift App does not get any data. How am i able to return all data i want to have?
Functions Code:
exports.getLocationObjectsFromUser =
functions.https.onCall((data, context) => {
const locations = [];
let userIds = [];
return userRef
.doc(data.userId)
.collection("locationIds")
.where("status", "==", true)
.get()
.then((querySnapshot) => {
querySnapshot.forEach((doc) => {
return locationRef
.doc(doc.id)
.get()
.then((locationDoc) => {
return locationRef
.doc(locationDoc.id)
.collection("userIds")
.get()
.then((querySnapshot1) => {
querySnapshot1.forEach((doc1) => {
userIds.push(doc1.id);
});
const object = {...locationDoc.data(), userIds};
locations.push(object);
userIds = [];
// if statement to avoid return before function has finished running
if (querySnapshot.size == locations.length) {
return {locations: locations};
}
});
});
});
});
});
Swift Code:
func getLocationObjectsFromUser(_ user_id: String, onSuccess: @escaping ([LocationModel]) -> Void, onError: @escaping(_ error: String?) -> Void) {
let dic = ["userId" : user_id] as [String : Any]
self.functions.httpsCallable("getLocationObjectsFromUser").call(dic) { (result, error) in
if let error = error as NSError? {
print("ERROR")
print(error)
onError(error.localizedDescription)
}
if let data = result?.data as? [String: Any] {
print("DATA")
print(data)
// Later on i want to return the LocationModel with something like this: onSuccess(Data).
}
// i do not get any data after calling the function.
}
}
Upvotes: 0
Views: 444
Reputation: 598728
The problem is the last line here:
return userRef
.doc(data.userId)
.collection("locationIds")
.where("status", "==", true)
.get()
.then((querySnapshot) => {
querySnapshot.forEach((doc) => {
return locationRef...
Since you're looping over the documents in querySnapshot
, you have multiple calls return locationRef...
in there, and you have no code that ensures that all those reads have finished before the Cloud Function is terminated.
Whenever you need to wait for multiple operations on the same level in your code, your answer is in using Promise.all
:
return userRef
.doc(data.userId)
.collection("locationIds")
.where("status", "==", true)
.get()
.then((querySnapshot) => {
return Promise.all(querySnapshot.docs.map((doc) => {
return locationRef...
So the changes:
We return a Promise.all()
which only resolves once all nested reads are done.
We use querySnapshot.docs.map
instead of querySnapshot.forEach
, so that we get an array of promises to pass to Promise.all
.
Upvotes: 1