#ios #swift #firebase #firebase-realtime-database
#iOS #swift #firebase #firebase-realtime-database
Вопрос:
Если я хочу получить некоторые данные из базы данных, я могу проверить, завершается ли он с помощью snapshot.exists()
:
ref.observeSingleEvent(of: .value, with: { (snapshot) in
if !snapshot.exists() { return }
}
Но при запуске a Transaction
я просто обновил то, что больше не существует, и вместо получения ошибки он обновил ссылку, чего я не ожидал.
1 — дочерняя ссылка, которую должна обновить транзакция:
@posts
@postId_123 // this post has actually been deleted
-url: ...
-timeStamp: ...
-comments_count: 10
-2 пользователь может удалить один из своих комментариев, фактически не просматривая его. Как только это произойдет, количество комментариев уменьшится.
let postsRef = Database.database().reference().child("posts").child("postId_123").child("comments_count")
postsRef.runTransactionBlock({ (mutableData: MutableData) -> TransactionResult in
var currentCount = mutableData.value as? Int ?? 0
mutableData.value = currentCount - 1
currentCount = mutableData.value as? Int ?? 0
if currentCount < 0 {
mutableData.value = 0
}
return TransactionResult.success(withValue: mutableData)
}, andCompletionBlock: { [weak self](error, completion, snap) in
if !completion || (error != nil) {
print("The value wasn't able to update")
print(error?.localizedDescription as Any)
} else {
print("The value updated")
}
})
3- проблема в том, что если postId_123 был удален до запуска транзакции, приведенная выше транзакция приводит к тому, что postId_123 помещается обратно в ссылку posts:
@posts
@postId_123 // this postId has been been put back but should no longer exist
-comments_count: 0
Как я могу запустить TransactionResult.abort()
, если дочерний элемент для изменяемых данных больше не существует?
let postsRef = Database.database().reference().child("posts").child("postId_123").child("comments_count")
postsRef.runTransactionBlock({ (mutableData: MutableData) -> TransactionResult in
if !mutableData.exists() { // *** this check isn't real and is used just as an example ***
return TransactionResult.abort() // this is real
}
var currentCount = mutableData.value as? Int ?? 0
// ...
})
Ответ №1:
Это единственный способ, который я нашел, чтобы вызвать прерывание. Если кто-то опубликует другой способ, я удалю этот ответ.
Первое, что нужно знать, это то, что для того, чтобы этот ответ сработал, любой дочерний элемент, который вы пытаетесь запустить Transaction
, должен иметь значение заранее и не может быть равен нулю. Примером может служить попытка запустить a Transaction
для comments_count в сообщении, когда сообщение создается впервые, comments_count должен быть создан вместе с ним и установлен в 0 (комментариев пока нет). Если там ничего нет, то с помощью этого метода ниже comments_count НИКОГДА не будет обновляться:
var dict = [String: Any]()
dict.updateValue(postId_123, forKey: "postId")
dict.updateValue(0, forKey: "comments_count") // *** this is important ***
dict...
let postsRef = ...child("posts").child(postId_123)
postsRef.updateChildValues(dict)
Затем при попытке запустить Transaction
on comments_count, если значение равно нулю, сообщение было удалено (это предполагает, что нет другого способа удалить comments_count, кроме удаления всего сообщения). Если значение равно нулю, запустите TransactionResult.abort()
. Внутри completionBlock
вы проверяете, равно ли значение error
nil
let postsRef = Database.database().reference().child("posts").child("postId_123").child("comments_count")
postsRef.runTransactionBlock({ (mutableData: MutableData) -> TransactionResult in
// if nil then the post was deleted
let checkIfCountExists = mutableData.value as? Int
if checkIfCountExists == nil {
return TransactionResult.abort()
}
// not nil then continue on
var currentCount = mutableData.value as? Int ?? 0
mutableData.value = currentCount - 1
currentCount = mutableData.value as? Int ?? 0
if currentCount < 0 {
mutableData.value = 0
}
return TransactionResult.success(withValue: mutableData)
}, andCompletionBlock: { [weak self](error, completion, snap) in
if !completion || (error != nil) {
print("The value wasn't able to update")
print(error?.localizedDescription as Any)
if error == nil {
print("*** transaction value is nil ***")
return
}
// there is a value there but something else went wrong so try again
} else {
print("The value updated")
}
})