I am running multiple Firestore queries in a single user tapGesture, which requires me to ensure that there are minimum to no simultaneous Firestore queries running in the app. I have read multiple answers(
With @Kiril S.'s answer, codes were corrected as shown below.
class WorkoutViewController: UIViewController {
let dispatchGroup = DispatchGroup()
alert.addAction(UIAlertAction(title: "Select", style: .default, handler: { [self]_ in
dispatchGroup.enter()
DispatchQueue.main.async {
print("Firestore Sequence 2 Initiated")
startActivityIndicator()
if let user = Auth.auth().currentUser {
let userID = user.uid
db.collection("user").whereField("author_uid", isEqualTo: userID).getDocuments { snapshot, error in
if error == nil && snapshot != nil {
for document in snapshot!.documents {
let docID = document.documentID
db.collection("user")
.document(docID)
.setData(["selectedWorkoutID" : workoutRow.workoutId], merge: true)
print("Firestore Sequence 2 Success - user selectedWorkoutID updated")
}
}
if let user = Auth.auth().currentUser {
let userID = user.uid
checkAndCreateUserWorkoutProfile(selectedWorkout: workoutRow, userID: userID)
dispatchGroup.leave()
}
}
}
}
dispatchGroup.notify(queue: .main) {
print("Firestore Sequence 4 Initiated")
print("Firestore Sequence 5 Initiated - Create/Read User Specific Dayprogram data")
}
}))
func checkAndCreateUserWorkoutProfile(selectedWorkout: Workout, userID: String) {
print("Firestore Sequence 3 Initiated - createORread user specific workout profile")
dispatchGroup.enter()
db.collection("Workout")
.whereField("workoutId", isEqualTo: selectedWorkout.workoutId)
.whereField("userID", isEqualTo: userID)
.getDocuments() { (querySnapshot, err) in
if querySnapshot?.count == 0 {
var ref: DocumentReference? = nil
ref = self.db.collection("Workout").addDocument(data:
[
"author_uid": selectedWorkout.author_uid!,
"workoutId": selectedWorkout.workoutId,
"userID": userID
])
{ err in
if let err = err {
print("Error adding user specific workout profile: \(err)")
self.dispatchGroup.leave()
} else {
print("Firestore Sequence 3 Success - User specific workout profile added/created with ID: \(ref!.documentID)")
self.dispatchGroup.leave()
}
}
}
}
}
Xcode Console showing corrected sequence of codes/queries.
CodePudding user response:
So basically what happens now:
- Alert action starts
- Creates
let dispatchGroup = DispatchGroup()
- Enters that group
- Leaves the group ==>
dispatchGroup.notify
fires checkAndCreateUserWorkoutProfile
starts, creates its own dispatch group and so on (already wrong, doesn't matter)
So you have 2 problems:
You have to have 1 dispatch group, not a separate group for each function. So move
let dispatchGroup = DispatchGroup()
to class member level, that way both functions are in the same dispatch group.And your
checkAndCreateUserWorkoutProfile
needs to enter dispatch group before previous call leaves it. So the order should be changed toif let user = Auth.auth().currentUser { let userID = user.uid checkAndCreateUserWorkoutProfile(selectedWorkout: workoutRow, userID: userID) } dispatchGroup.leave()
That way:
- Alert action starts
- Enters a group defined as a class member
- Calls
checkAndCreateUserWorkoutProfile
, which enters the same dispatch group and starts its async call - Alert action leaves the group (but notification doesn't fire, since
checkAndCreateUserWorkoutProfile
didn't leave the group yet) checkAndCreateUserWorkoutProfile
leaves the group eventually ==>dispatchGroup.notify
fires