Ahad Sheriff Ahad Sheriff - 3 months ago 18
Swift Question

Swift: How to fix infinite loop when adding a value to a Firebase variable

I have this piece of code in my program that should allow a user to tap on a message and the score should increase by one:

super.collectionView(collectionView, didTapMessageBubbleAtIndexPath: indexPath)
let data = self.messages[indexPath.row]

print("They tapped: " + (data.text) + "- " + (data.senderDisplayName))

let rootRef : FIRDatabaseReference = FIRDatabase.database().reference()
let senderID = FIRAuth.auth()!.currentUser!.uid

rootRef.child("messages").observeEventType(.Value, withBlock: { (snap) in

print(senderID)

if snap.exists(){
if let messagesDict = snap.value! as? [String : AnyObject]
{
for each in messagesDict as [String : AnyObject]{

let postID = each.0
if let messageDict = each.1 as? [String:AnyObject]{
if senderID == messageDict["senderId"] as! String{

//Checking for the senderID of the user so that you only increment the score of that particular message post
var userScore = messageDict["score"] as! Int
userScore = userScore + 1
rootRef.child("messages").child(postID).child("score").setValue(userScore)

print(senderID)
print(messageDict["senderId"])
print(userScore)
}
}
}
}
}
})


However, when the above code is run and a user taps on a message, the score variable does increase in Firebase however it never stops running. This causes the score to increase infinitely which is not what I am looking for. I wanted my code to just add one to the score a single time, but that doesn't seem to be working. Would anybody be able to help me find my infinite loop or suggest a way where I could more efficiently add to my score variable in Firebase?

Thanks!

P.s. I also know for a fact that the function doesn't run multiple times by itself because the print statement:
print("They tapped: " + (data.text) + "- " + (data.senderDisplayName))
only prints once, and returns the correct data.

Answer

Ivan's answer will solve the current problem you have. But loading all messages from the server to detect which one the user clicked sounds like a potentially big waste of bandwidth.

If you're showing a list of messages from Firebase to the user, I recommend keeping track of the key of each message. With that information, you won't have scan all messages, but can instead directly look up the message that was clicked and increase its score with a transaction:

rootRef.child("messages").child(postID).child("score").runTransactionBlock({ (currentData: FIRMutableData) -> FIRTransactionResult in
  // Set value and report transaction success
  currentData.value = currentData.value + 1
  return FIRTransactionResult.successWithValue(currentData)
}) { (error, committed, snapshot) in
  if let error = error {
    print(error.localizedDescription)
  }
}

A transaction will also solve the potential race condition you now have: if two users click the same message at almost the same time, one might be overwriting the score-increment of the other.

Comments