Felipe Rodriguez Carcher Felipe Rodriguez Carcher - 6 months ago 64
Swift Question

Firebase 3 can't modify other user created data

In this application, each user has a counter for "votes" but it only works when the same user votes, when another user tries to do a FIRTransaction in that reference it returns nil. The rules are set to any user.

When I need that user2 updates the user1 "votes" value it doesn't reads that key value to perform the transaction block, so it returns nil.

votesCountRef = ref.child("user1").child("votes")
votesCountRef.runTransactionBlock( { (currentData) -> FIRTransactionResult in
let value = currentData.value! as! Int
currentData.value = value + 1
return FIRTransactionResult.successWithValue(currentData)
})


result:


Received votes actual value: Optional()
Could not cast value of type 'NSNull' (0x110684600) to 'NSNumber' (0x10fce92a0).


But when original user runs this it works successfully.

Json tree for user1:

{
"userUID" : "user1",
"votes" : 2
}


Database rules:

{
"rules": {
".read": "auth != null",
".write": "auth != null"
}
}


Thanks to @Frank I could manage it like this:

ownerReceivedVotesCountRef.runTransactionBlock( { (currentData) -> FIRTransactionResult in

let value = currentData.value as? Int ?? 0
currentData.value! = (value as Int) + 1
return FIRTransactionResult.successWithValue(currentData)
})

Answer

A transaction handler is initially run with the client's best guess fo rthe current value. Quite often that will be nil, so your code has to handle that.

From the Firebase documentation:

Note: Because runTransactionBlock:andCompletionBlock: is called multiple times, it must be able to handle nil data. Even if there is existing data in your remote database, it may not be locally cached when the transaction function is run, resulting in nil for the initial value.

Something like this should work for your case:

let value = currentData.value as? Int ?? 0