DispatchGroup 等待 Firestore 查询完成
DispatchGroup to wait for Firestore query completion
我在单个用户 tapGesture 中 运行 多个 Firestore 查询,这要求我确保应用程序中同时进行的 Firestore 查询最少甚至没有 运行。我已经阅读了关于这个问题的多个答案(),但我的查询并没有像我预期的那样 运行 在所需的序列中。
非常感谢您帮助指导我使用 DispatchGroup 来确保代码和查询的逻辑顺序。
我想确保 Firestore 查询 在函数 checkAndCreateUserWorkoutProfile 在 DispatchGroup.notify(...) 中的 2 个打印语句之前完成,从“Firestore”开始序列”方法启动。相反,查询在两个打印语句启动后完成。
下面是我的 Xcode 调试器的代码和屏幕截图。如屏幕截图所示,问题是使用“DispatchGroup.notify(...)”不会等到函数 checkAndCreateUserWorkoutProfile 中的 Firestore 查询已完成。
alert.addAction(UIAlertAction(title: "Select", style: .default, handler: { [self]_ in
let dispatchGroup = DispatchGroup()
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")
}
}
dispatchGroup.leave()
if let user = Auth.auth().currentUser {
let userID = user.uid
checkAndCreateUserWorkoutProfile(selectedWorkout: workoutRow, userID: userID)
}
}
}
}
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")
let dispatchGroup = DispatchGroup()
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)")
dispatchGroup.leave()
} else {
print("Firestore Sequence 3 Success - User specific workout profile added/created with ID: \(ref!.documentID)")
dispatchGroup.leave()
}
}
}
}
}
根据@Kiril S. 的回答,代码已更正如下所示。
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 控制台显示 codes/queries 的更正序列。
所以基本上现在发生了什么:
- 警报行动开始
- 创建
let dispatchGroup = DispatchGroup()
- 进入那个组
- 离开小组 ==>
dispatchGroup.notify
解雇
checkAndCreateUserWorkoutProfile
启动,创建自己的调度组等等(已经错了,无所谓)
所以你有两个问题:
您必须有 1 个调度组,而不是每个函数单独的组。所以将 let dispatchGroup = DispatchGroup()
移动到 class 成员级别,这样两个函数都在同一个调度组中。
而你的checkAndCreateUserWorkoutProfile
需要进入调度组
before 之前的调用离开了它。所以顺序应该改为
if let user = Auth.auth().currentUser {
let userID = user.uid
checkAndCreateUserWorkoutProfile(selectedWorkout: workoutRow, userID: userID)
}
dispatchGroup.leave()
这样:
- 警报行动开始
- 进入定义为 class 成员的组
- 调用
checkAndCreateUserWorkoutProfile
,它进入 相同的 调度组并开始其异步调用
- 离开群组的警报操作(但不会触发通知,因为
checkAndCreateUserWorkoutProfile
尚未离开群组)
checkAndCreateUserWorkoutProfile
最终离开小组 ==> dispatchGroup.notify
开火
我在单个用户 tapGesture 中 运行 多个 Firestore 查询,这要求我确保应用程序中同时进行的 Firestore 查询最少甚至没有 运行。我已经阅读了关于这个问题的多个答案(
非常感谢您帮助指导我使用 DispatchGroup 来确保代码和查询的逻辑顺序。
我想确保 Firestore 查询 在函数 checkAndCreateUserWorkoutProfile 在 DispatchGroup.notify(...) 中的 2 个打印语句之前完成,从“Firestore”开始序列”方法启动。相反,查询在两个打印语句启动后完成。
下面是我的 Xcode 调试器的代码和屏幕截图。如屏幕截图所示,问题是使用“DispatchGroup.notify(...)”不会等到函数 checkAndCreateUserWorkoutProfile 中的 Firestore 查询已完成。
alert.addAction(UIAlertAction(title: "Select", style: .default, handler: { [self]_ in
let dispatchGroup = DispatchGroup()
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")
}
}
dispatchGroup.leave()
if let user = Auth.auth().currentUser {
let userID = user.uid
checkAndCreateUserWorkoutProfile(selectedWorkout: workoutRow, userID: userID)
}
}
}
}
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")
let dispatchGroup = DispatchGroup()
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)")
dispatchGroup.leave()
} else {
print("Firestore Sequence 3 Success - User specific workout profile added/created with ID: \(ref!.documentID)")
dispatchGroup.leave()
}
}
}
}
}
根据@Kiril S. 的回答,代码已更正如下所示。
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 控制台显示 codes/queries 的更正序列。
所以基本上现在发生了什么:
- 警报行动开始
- 创建
let dispatchGroup = DispatchGroup()
- 进入那个组
- 离开小组 ==>
dispatchGroup.notify
解雇 checkAndCreateUserWorkoutProfile
启动,创建自己的调度组等等(已经错了,无所谓)
所以你有两个问题:
您必须有 1 个调度组,而不是每个函数单独的组。所以将
let dispatchGroup = DispatchGroup()
移动到 class 成员级别,这样两个函数都在同一个调度组中。而你的
checkAndCreateUserWorkoutProfile
需要进入调度组 before 之前的调用离开了它。所以顺序应该改为if let user = Auth.auth().currentUser { let userID = user.uid checkAndCreateUserWorkoutProfile(selectedWorkout: workoutRow, userID: userID) } dispatchGroup.leave()
这样:
- 警报行动开始
- 进入定义为 class 成员的组
- 调用
checkAndCreateUserWorkoutProfile
,它进入 相同的 调度组并开始其异步调用 - 离开群组的警报操作(但不会触发通知,因为
checkAndCreateUserWorkoutProfile
尚未离开群组) checkAndCreateUserWorkoutProfile
最终离开小组 ==>dispatchGroup.notify
开火