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