Search code examples
swiftfirebasefirebase-realtime-databasefirebase-authenticationmultipath

updateChildValue writes data


Having following issue:

I denormalised my data structure in my Realtime Firebase Database. As a consequence of that I need to update the username in all other "posts" (in this case recipes) as well as in recipes with different authors. To do that, every time a user creates a comment on a recipe this user also creates a reference to this recipe in his user object.

Now when you change your username, it reads all those references and changes the username on this path like following (Swift):

ref.child("users/profile").child(uid ?? "ERROR").child("comments/DE").observeSingleEvent(of: .value, with: { (recipes) in
   // recipe dict is a dictionary of all the references to recipes a user has commented
   let recipeDict = recipes.value as! [String:Any]
   // now every reference is being used to change the username at given place to the new username with variable "username"
   var updateObjects: [String:String] = [:]
   for i in recipeDict.keys {
      updateObjects["recipes/DE/\(i)/comments/\(uid ?? "ERROR")/username"] = username
   }
   // Now the changes stored in the "updateObjects" variable will be applied
   ref.updateChildValues(updateObjects)
})

Now this works, but the problem with this approach is, that say a recipe gets deleted, the reference doesn't get deleted (would be complicated since security rules don't allow to update another users profile so therefore not their references as well). So that's not a huge deal for me, but since the 'updateChildValues' function creates the missing objects it is. What ends up happening is, that at the place where the deleted recipe would be, a "new" empty recipe structure without any other nodes than the comment gets created. This crashes my app and is not what I want. How can I stop this from happening?

Can you call a Multi-path update without writing data, when there is no existing structure (recipe was deleted)? Or do I need to remove all comment references and somehow give other users access to the comment references (which is tedious...)?


Solution

  • You could write security rules to reject a write operation to a non-existing comment if the write is just a username. For example, say that another property for each comment is the text, you could do:

    "comments": {
      "$commentid": {
        "$uid": {
          "username": {
            ".write": "data.parent.parent.child('text').exists()"
          }
        }
      }
    }     
    

    The problem with this is that a multi-path update performs one big check for the security rules across all these updates. So if one node was deleted, the updating of all names will fail. If that is what you want, I'd go for that.