Я читал этот вопрос о зависаниях приложений и семафорах, и я попытался реализовать ответьте на мой код, но приложение все еще зависает, несмотря на вызов пользовательского интерфейса в основном потоке. Моя цель - предотвратить зависание приложения после вызова всех записей и продолжить работу пользовательского интерфейса, как обычно.
Это предупреждающее действие, которое у меня есть в методе удаления до сих пор:
let deleteAction = UIAlertAction(title: "Delete", style: .destructive) { (deletion) in
let semaphore = DispatchSemaphore(value: 0)
self.deleteButton.isHidden = true
self.loadingToDelete.alpha = 1
self.loadingToDelete.startAnimating()
DispatchQueue.global(qos: .userInitiated).async {
self.db.collection("student_users/\(user.uid)/events_bought").getDocuments { (querySnapshot, error) in
guard error == nil else {
print("The docs couldn't be retrieved for deletion.")
return
}
guard querySnapshot?.isEmpty == false else {
print("The user being deleted has no events purchased.")
return
}
for document in querySnapshot!.documents {
let docID = document.documentID
self.db.collection("student_users/\(user.uid)/events_bought/\(docID)/guests").getDocuments { (querySnap, error) in
guard querySnap?.isEmpty == false else {
print("The user being deleted has no guests with his purchases.")
return
}
for doc in querySnap!.documents {
let guest = doc.documentID
self.db.document("student_users/\(user.uid)/events_bought/\(docID)/guests/\(guest)").delete { (error) in
guard error == nil else {
print("Error deleting guests while deleting user.")
return
}
print("Guests deleted while deleting user!")
semaphore.signal()
}
semaphore.wait()
}
}
}
}
self.db.collection("student_users/\(user.uid)/events_bought").getDocuments { (querySnapshot, error) in
guard error == nil else {
print("There was an error retrieving docs for user deletion.")
return
}
guard querySnapshot?.isEmpty == false else {
return
}
for document in querySnapshot!.documents {
let docID = document.documentID
self.db.document("student_users/\(user.uid)/events_bought/\(docID)").delete { (err) in
guard err == nil else {
print("There was an error deleting the the purchased events for the user being deleted.")
return
}
print("Purchases have been deleted for deleted user!")
semaphore.signal()
}
semaphore.wait()
}
}
self.db.document("student_users/\(user.uid)").delete(completion: { (error) in
guard error == nil else {
print("There was an error deleting the user document.")
return
}
print("User doc deleted!")
semaphore.signal()
})
semaphore.wait()
user.delete(completion: { (error) in
guard error == nil else {
print("There was an error deleting user from the system.")
return
}
print("User Deleted.")
semaphore.signal()
})
semaphore.wait()
DispatchQueue.main.async {
self.loadingToDelete.stopAnimating()
self.performSegue(withIdentifier: Constants.Segues.studentUserDeletedAccount, sender: self)
}
}
}
Таким образом, на самом деле это удаляет все чисто, без остаточных данных в базе данных Firestore, чего я и хотел все это время, единственная проблема заключается в том, что приложение зависает. Я думал, что ответ на вопрос, который я дал выше, сработает в моем случае, но это не так.
Также следует отметить, что у меня были предложения по использованию облачных функций для решения этой проблемы, но в моем приложении есть два типа пользователей с разной логикой и синтаксисом в процессе удаления, поэтому я не мог просто использовать простой auth().onDelete()
в облачных функциях и очистить остатки . Даже если бы я мог, это была бы та же проблема, с которой я столкнулся здесь, но только на стороне сервера, пытаясь правильно упорядочить задачи, что, на мой взгляд, является повторяющимся и не самым разумным делом на данный момент.
Есть ли другие предложения по преодолению этой проблемы? Заранее спасибо.
ИЗМЕНИТЬ Поскольку семафоры не подходящие, я прибег к следующему:
let deleteAction = UIAlertAction(title: "Delete", style: .destructive) { (deletion) in
self.deleteButton.isHidden = true
self.loadingToDelete.alpha = 1
self.loadingToDelete.startAnimating()
self.db.collection("student_users/\(user.uid)/events_bought").getDocuments { (querySnapshot, error) in
guard error == nil else {
print("The docs couldn't be retrieved for deletion.")
return
}
guard querySnapshot?.isEmpty == false else {
print("The user being deleted has no events purchased.")
return
}
for document in querySnapshot!.documents {
let docID = document.documentID
self.db.collection("student_users/\(user.uid)/events_bought/\(docID)/guests").getDocuments { (querySnap, error) in
guard querySnap?.isEmpty == false else {
print("The user being deleted has no guests with his purchases.")
return
}
let group = DispatchGroup()
for doc in querySnap!.documents {
let guest = doc.documentID
group.enter()
self.db.document("student_users/\(user.uid)/events_bought/\(docID)/guests/\(guest)").delete { (error) in
guard error == nil else {
print("Error deleting guests while deleting user.")
return
}
print("Guests deleted while deleting user!")
group.leave()
}
}
}
}
}
self.db.collection("student_users/\(user.uid)/events_bought").getDocuments { (querySnapshot, error) in
guard error == nil else {
print("There was an error retrieving docs for user deletion.")
return
}
guard querySnapshot?.isEmpty == false else {
return
}
let group = DispatchGroup()
for document in querySnapshot!.documents {
let docID = document.documentID
group.enter()
self.db.document("student_users/\(user.uid)/events_bought/\(docID)").delete { (err) in
guard err == nil else {
print("There was an error deleting the the purchased events for the user being deleted.")
return
}
print("Purchases have been deleted for deleted user!")
group.leave()
}
}
}
self.db.collection("student_users").whereField("userID", isEqualTo: user.uid).getDocuments { (querySnapshot, error) in
guard error == nil else {
print("There was an error deleting the user document.")
return
}
guard querySnapshot?.isEmpty == false else {
return
}
let group = DispatchGroup()
for document in querySnapshot!.documents {
let docID = document.documentID
group.enter()
self.db.document("student_users/\(docID)").delete { (err) in
guard err == nil else {
return
}
print("User doc deleted!")
group.leave()
}
}
}
let group = DispatchGroup()
group.enter()
user.delete(completion: { (error) in
guard error == nil else {
print("There was an error deleting user from the system.")
return
}
print("User Deleted.")
group.leave()
})
group.notify(queue: .main) {
self.loadingToDelete.stopAnimating()
self.performSegue(withIdentifier: Constants.Segues.studentUserDeletedAccount, sender: self)
}
}
Это по-прежнему оставляет остаточные данные и не выполняет задачи по порядку. Есть другие предложения?
async/await
(которого еще нет в Swift) может значительно упростить упорядочение задач. - person jnpdx   schedule 27.04.2021true
. Но лучшей формой рекурсии было бы завершение задачи, когда запросы к базе данных возвращают безошибочные пустые моментальные снимки. Когда последний запрос ничего не показывает, завершите задачу. Но здесь я бы избегал семафоров. - person liquid   schedule 27.04.2021group.enter()
иgroup.leave()
? Я обновлю свой пост, чтобы показать вам, что у меня есть на данный момент, так что мы оба на одной странице. @bxod - person dante   schedule 27.04.2021group.notify()
вызовов или только один последний после всех задач? В настоящее время у меня есть одинgroup.notify()
в последнем экземпляре группы отправки, но порядок задач по-прежнему неверен, когда срабатывает действие предупреждения. @bxod - person dante   schedule 27.04.2021