Swift -Firebase как запустить TransactionResult.abort(), если ссылка больше не существует

#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")
    }
})