0

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

          }
        }
       }
     }
   }
EverythingEnds
  • 77
  • 1
  • 11
  • 1
    Why are you checking if currentData is nil? `if (currentData.value as? [String: AnyObject]) == nil`. You should be checking to ensure it's NOT nil before accessing it. See [Save Data As Transaction](https://firebase.google.com/docs/database/ios/read-and-write#save_data_as_transactions) – Jay Aug 13 '19 at 16:23
  • Hmm... when I change it to `if (currentData.value as? [String: AnyObject]) == nil` no votes go through. I got this code by going through the documentation. I believe you though, but I am not sure what is going on – EverythingEnds Aug 13 '19 at 16:34
  • 1
    Unless I am overlooking something, that logic doesn't make sense. The whole idea is that you want to verify that `currentData.value` is not nil so then you can read and write to it. If it was nil then the app would crash. I am not sure where you're seeing that code in the documentation - if you review the link in my comment to the documentation you can clearly see if's being checked for not nil before using it; `if var post = currentData.value as? [String : AnyObject],` Also, the code in your comment is the same thing I pointed out, so you didnt make a change. – Jay Aug 13 '19 at 16:38
  • Sorry I meant `if (currentData.value as? [String: AnyObject]) != nil {` makes the code not update the value. So I took out the if statement, then I printed what `currentData.value` is, and I got Optional(0) – EverythingEnds Aug 13 '19 at 17:07
  • 1
    What your code is doing is the following; If currentData.value is nil, meaning the node does not exist, then write some data. If it exists, do nothing. This also means that the node will be created if it didn't previously exist, and it's value will be set to *comment.upvotes*. So what you think the code is doing, isn't what it's actually doing under the hood, which is probably why you're not getting expected results. – Jay Aug 13 '19 at 17:31
  • I'll look more into this tonight. I found a relevant SO thread about why the currentdata.value is always nil: https://stackoverflow.com/a/34804228/11555567 i will rewrite the code tonight – EverythingEnds Aug 13 '19 at 17:44

1 Answers1

0

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

    }
EverythingEnds
  • 77
  • 1
  • 11