Search code examples
iosswiftfirebasefirebase-realtime-databasevoting

Voting: Vote registers twice


I am making a voting system. But when you upvote then remove the upvote, the score moves up one then down two. If you then try to downvote, the score does not move at all. I think the code records removing an upvote as a downvote, but I am not sure why. Switching your vote does the intended 2 point swing to the score. Unless you upvote, leave the viewController, then switch your vote. That only gives you a 1 point change to the vote.

    @IBOutlet weak var checkbox: Checkbox!
    @IBOutlet weak var downVote: Checkbox!
    var boxcheck = false
    var downcheck = false
    var postRef:DatabaseReference{
    return Database.database().reference().child("posts").child("comments")}
 }
      func set(comment:Comment) {
    commentLabel.text = comment.text
    upvoteCountLabel.text = String(comment.upvotes)
    downcheck = UserDefaults.standard.bool(forKey: "\(comment.snap) downvotes")
    boxcheck = UserDefaults.standard.bool(forKey: "\(comment.snap) upvotes")
    downVote.isChecked = UserDefaults.standard.bool(forKey: "\(comment.snap) downvotes")
    checkbox.isChecked = UserDefaults.standard.bool(forKey: "\(comment.snap) upvotes")

    downcheck = UserDefaults.standard.bool(forKey: "\(comment.snap) downvotes")
    boxcheck = UserDefaults.standard.bool(forKey: "\(comment.snap) upvotes")
    func voteUp() {
        self.postRef.child(comment.postID).child(comment.snap).child("upvotes").runTransactionBlock({ (currentData:MutableData)  -> TransactionResult in
            if (currentData.value as? [String: AnyObject]) == nil {

                let upvotesNumber = comment.upvotes
                var upvotes = upvotesNumber
                upvotes += 1

                currentData.value = upvotes

                return TransactionResult.success(withValue: currentData)
            }

            //Abort like if there was a problem
            print("abortion")
            return TransactionResult.abort()

        })

    }
  func voteDown(){
        self.postRef.child(comment.postID).child(comment.snap).child("upvotes").runTransactionBlock({ (currentData:MutableData)  -> TransactionResult in
            if (currentData.value as? [String: AnyObject]) == nil {

                let upvotesNumber = comment.upvotes
                var upvotes = upvotesNumber
                upvotes -= 1

                currentData.value = upvotes

                return TransactionResult.success(withValue: currentData)
            }

            //Abort like if there was a problem
            print("abortion")
            return TransactionResult.abort()

        })
    }

    checkbox.valueChanged = { (value) in
        print("checkbox value change: \(value)")

      self.boxcheck = self.checkbox.isChecked
        UserDefaults.standard.set(self.checkbox.isChecked, forKey: "\(comment.snap) upvotes")
        UserDefaults.standard.set(self.downVote.isChecked, forKey: "\(comment.snap) downvotes")
        if self.checkbox.isChecked == true && self.downVote.isChecked == true{
            voteUp()
            self.boxcheck = self.checkbox.isChecked
        }
        if self.checkbox.isChecked == true && self.downVote.isChecked == false{

        UserDefaults.standard.set(self.checkbox.isChecked, forKey: "\(comment.snap) upvotes")
        UserDefaults.standard.set(self.downVote.isChecked, forKey: "\(comment.snap) downvotes")

            voteUp()
            self.boxcheck = self.checkbox.isChecked
        }

       if self.checkbox.isChecked == false && self.downVote.isChecked == false{
        UserDefaults.standard.set(self.checkbox.isChecked, forKey: "\(comment.snap) upvotes")
        UserDefaults.standard.set(self.downVote.isChecked, forKey: "\(comment.snap) downvotes")

           voteDown()
        self.downVote.isChecked = false
        self.boxcheck = self.checkbox.isChecked
        self.downcheck = self.downVote.isChecked
        }

       if self.checkbox.isChecked == false && self.downVote.isChecked == true{
           voteDown()
        self.boxcheck = self.checkbox.isChecked
        }
            if self.downVote.isChecked == true && self.checkbox.isChecked == true {
            self.downVote.isChecked = false
            self.downcheck = self.downVote.isChecked
            }

        print("POSTITIVE My current downcheck value is \(self.downcheck)")
        print("POSITIVE My current upcheck value is \(self.boxcheck)")
        UserDefaults.standard.set(self.checkbox.isChecked, forKey: "\(comment.snap) upvotes")
        UserDefaults.standard.set(self.downVote.isChecked, forKey: "\(comment.snap) downvotes")
    }


    downVote.valueChanged = { (value) in
        self.downcheck = self.downVote.isChecked
        print("downVote value change: \(value)")
        UserDefaults.standard.set(self.checkbox.isChecked, forKey: "\(comment.snap) upvotes")
        UserDefaults.standard.set(self.downVote.isChecked, forKey: "\(comment.snap) downvotes")
        if self.checkbox.isChecked == true && self.downVote.isChecked == true {
            voteDown()
            self.downcheck = self.downVote.isChecked

        }
        if self.checkbox.isChecked == false && self.downVote.isChecked == true {

        UserDefaults.standard.set(self.checkbox.isChecked, forKey: "\(comment.snap) upvotes")
        UserDefaults.standard.set(self.downVote.isChecked, forKey: "\(comment.snap) downvotes")

            voteDown()
            self.downcheck = self.downVote.isChecked
        }

        if self.checkbox.isChecked == false && self.downVote.isChecked == false{
        UserDefaults.standard.set(self.checkbox.isChecked, forKey: "\(comment.snap) upvotes")
        UserDefaults.standard.set(self.downVote.isChecked, forKey: "\(comment.snap) downvotes")
           voteUp()
            self.downcheck = self.downVote.isChecked
            self.boxcheck = self.checkbox.isChecked
        }
     if self.checkbox.isChecked == true && self.downVote.isChecked == false   {
        UserDefaults.standard.set(self.checkbox.isChecked, forKey: "\(comment.snap) upvotes")
        UserDefaults.standard.set(self.downVote.isChecked, forKey: "\(comment.snap) downvotes")
                voteUp()
        self.downcheck = self.downVote.isChecked
            }

            if self.checkbox.isChecked == true && self.downVote.isChecked == true {
            self.checkbox.isChecked = false
            self.boxcheck = self.checkbox.isChecked
        }
        UserDefaults.standard.set(self.checkbox.isChecked, forKey: "\(comment.snap) upvotes")
        UserDefaults.standard.set(self.downVote.isChecked, forKey: "\(comment.snap) downvotes")
    }
    print("My current downcheck value is \(self.downcheck)")
    print("My current upcheck value is \(self.boxcheck)")
}


}

The Firebasse references work fine, but here is my Comment model in case you are curious:

import Foundation

class Comment: Equatable {
static func == (lhs: Comment, rhs: Comment) -> Bool {
    return lhs.id == rhs.id
}


var snap:String
var id:String
var text:String
var reports: Int
var upvotes: Int
var postID: String
init(id: String, text:String, reports:Int, snap: String, upvotes: Int, postID: String) {
    self.id = id
    self.text = text
    self.reports = reports
    self.snap = snap
    self.upvotes = upvotes
    self.postID = postID
}

And the Firebase JSON:

   {
  "posts" : {
    "-Lku3osQQXgagYMVI9ua" : {
      "commentsNumber" : 2,
      "reports" : 1,
      "text" : "Hey",
      "timestamp" : 1564342439656,
      "title" : "Welcome to Librex",
      "upvotes" : 1,
      "userID" : "mMqFhoNiR3gWiTd7J4wtPABhtYB3"
    }
    "comments" : {
      "-Lku3osQQXgagYMVI9ua" : {
        "-LlcJ_6Kus1nuqaCk17R" : {
          "comment_by_uid" : "N3HNT1aVkPXhiCRu9fzJknWYxCt2",
          "comment_text" : "This is really cool; thanks!",
          "reports" : 0,
          "upvotes" : 3
        },
        "-Llhr2E7UodiuyvzeGMi" : {
          "comment_by_uid" : "N3HNT1aVkPXhiCRu9fzJknWYxCt2",
          "comment_text" : "K ",
          "reports" : 0,
          "upvotes" : 1

          }
        }
       }
     }
   }

Solution

  • Here is the code I used for the voting!

     func voteUp() {
            self.postRef.child(comment.postID).child(comment.snap).child("upvotes").runTransactionBlock({
                (currentData: MutableData!) in
    
                //value of the counter before an update
                var value = currentData.value as? Int
    
                //checking for nil data is very important when using
                //transactional writes
                if value == nil {
                    value = 0
                }
    
                //actual update
                currentData.value = value! + 1
                UserDefaults.standard.set(self.checkbox.isChecked, forKey: "\(comment.snap) upvotes")
                UserDefaults.standard.set(self.downVote.isChecked, forKey: "\(comment.snap) downvotes")
                return TransactionResult.success(withValue: currentData)
    
            }, andCompletionBlock: {
                error, commited, snap in
    
                //if the transaction was commited, i.e. the data
                //under snap variable has the value of the counter after
                //updates are done
                if commited {
                    let upvotes = snap?.value as! Int
    
                } else {
                    UserDefaults.standard.set(self.checkbox.isChecked, forKey: "\(comment.snap) upvotes")
                    UserDefaults.standard.set(self.downVote.isChecked, forKey: "\(comment.snap) downvotes")
                    TransactionResult.abort()
                }
            })
    
        }
    
      func voteDown(){
        self.postRef.child(comment.postID).child(comment.snap).child("upvotes").runTransactionBlock({
            (currentData: MutableData!) in
    
            //value of the counter before an update
            var value = currentData.value as? Int
            UserDefaults.standard.set(self.checkbox.isChecked, forKey: "\(comment.snap) upvotes")
            UserDefaults.standard.set(self.downVote.isChecked, forKey: "\(comment.snap) downvotes")
            //checking for nil data is very important when using
            //transactional writes
            if value == nil {
                value = 0
                UserDefaults.standard.set(self.checkbox.isChecked, forKey: "\(comment.snap) upvotes")
                UserDefaults.standard.set(self.downVote.isChecked, forKey: "\(comment.snap) downvotes")
            }
    
            //actual update
            UserDefaults.standard.set(self.checkbox.isChecked, forKey: "\(comment.snap) upvotes")
            UserDefaults.standard.set(self.downVote.isChecked, forKey: "\(comment.snap) downvotes")
            currentData.value = value! - 1
            return TransactionResult.success(withValue: currentData)
        }, andCompletionBlock: {
            error, commited, snap in
    
            //if the transaction was commited, i.e. the data
            //under snap variable has the value of the counter after
            //updates are done
            if commited {
                let upvotes = snap?.value as! Int
                UserDefaults.standard.set(self.checkbox.isChecked, forKey: "\(comment.snap) upvotes")
                UserDefaults.standard.set(self.downVote.isChecked, forKey: "\(comment.snap) downvotes")
            } else {
    
                TransactionResult.abort()
                UserDefaults.standard.set(self.checkbox.isChecked, forKey: "\(comment.snap) upvotes")
                UserDefaults.standard.set(self.downVote.isChecked, forKey: "\(comment.snap) downvotes")
            }
        })
    
        }