0

I've looked around and tried different queries but none seem to work.

I have a social networking iOS app. When I change a user’s profile image, I need all the previous posts made by this particular user to also update and show the new profile image. I have this current user's userId.

It seems the only way is to query all the posts made by this user and then loop over them and update the image url. But I’m not sure how to perform this query.

This is how the posts node of the database looks like.

posts

    post1ID (e.g. -TaldfjWEFa92SASnKN)
           name: ”Jack Johnson”
           profileImageUrl: ”https://firebasestorage.googleapis.com/….”
           userId: "UekUSYobEKZYO9WqXJQu2"
    -asjfka1ADFAJ23klja=
           name: "Karin Bold"
           profileImageUrl: ”https://firebasestorage.googleapis.com/….”
           userId: "YobEKZYIUxqXJQu2dl2AD"

And the relevant code:

if let imgData = UIImageJPEGRepresentation(img, 0.1) {
    let imgUid = NSUUID().uuidString
    let metadata = StorageMetadata()
    let imagesRef = DataService.instance.imagesStorageRef.child(imgUid)

    imagesRef.putData(imgData, metadata: metadata, completion: { (metadata, error) in
        if error != nil {
          //show alert
        } else {
            let downloadURL = metadata?.downloadURL()?.absoluteString
            if let url = downloadURL {
                changeRequest?.photoURL = URL(string: url)
                changeRequest?.commitChanges() { (error) in
                    if error == nil {

                        //get all the posts created by this user id
                        //update profile image url in all of them

                        self.userImageUrlRef = DataHandler.instance.postsRef.child("userId").queryOrdered(byChild: AuthHandler.instance.currentUserId).ref //child(AuthHandler.instance.currentUserId).child("profileImgUrl")
                        self.userImageUrlRef.updateChildValues(["profileImageUrl" : url])
                    } else {
                     //show alert
                    }

                }
            }
        }
    })
}
Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
Jenny W
  • 3
  • 2
  • Another option here would be to change the data structure. – André Kool Mar 29 '18 at 13:23
  • The query should be a fairly simple `ref.queryOrdered(byChild: "name").queryEqual(toValue: "Jack Johnson")`. If that somehow doesn't work for you, edit your question to include the minimal code that reproduces where you're stuck pleas.e – Frank van Puffelen Mar 29 '18 at 13:34
  • @FrankvanPuffelen Thanks for the quick turnaround. I failed to mention that I can only query by current user's userId. – Jenny W Mar 29 '18 at 13:50
  • @AndréKool How? – Jenny W Mar 29 '18 at 13:50
  • My comment was just a general suggestion. But for a specific answer you should include more information. And maybe @FrankvanPuffelen can share some links, he always has ready, about nosql data structuring – André Kool Mar 29 '18 at 15:15
  • @FrankvanPuffelen Added the code. Thanks. – Jenny W Mar 29 '18 at 17:21

1 Answers1

0

The relevant code from what you shared is this:

DataHandler.instance.postsRef
  .child("userId")
  .queryOrdered(byChild: AuthHandler.instance.currentUserId)
  .ref 

That's really a quite creative way to do very little. You'll want to change it into a query that orders on the field you need and then filters with the value you need. So:

let query = DataHandler.instance.postsRef
  .queryOrdered(byChild: "userId")
  .queryEqual(toValue: AuthHandler.instance.currentUserId)

Then attach a listener to that query, and you should be getting the posts for the current user. To update them, loop over the results and update each in turn:

ref.observeSingleEvent(of: .value, with: { (snapshot) in 
  let childUpdates = ["profileImageUrl": url]
  for child in snapshot.children.allObjects as [FIRDataSnapshot] {
    child.ref.updateChildValues(childUpdates, withCompletionBlock: { (error, ref) in 
    }) 
  }
})

Note that if you care about always showing the correct profile information, you should consider not storing that information in each post, but instead only storing it in a /users/$uid/profileImageUrl node.

Also see my answer on How to write denormalized data in Firebase.

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
  • Thanks, but how do I update that specific profileImageUrl in the result of the query so that when I go back to my social feed, I can see that the profile images of all the posts made by that user now have the updated profile image? – Jenny W Mar 30 '18 at 10:24
  • let ref = DataHandler.instance.postsRef.queryOrdered(byChild: "userId").queryEqual(toValue: AuthHandler.instance.currentUserId).ref – Jenny W Mar 30 '18 at 10:26
  • ref.observeSingleEvent(of: .value, with: { (snapshot) in let value = snapshot.value as? NSDictionary let childUpdates = ["profileImageUrl": url] – Jenny W Mar 30 '18 at 10:27
  • ref.updateChildValues(childUpdates, withCompletionBlock: { (error, ref) in }) }) – Jenny W Mar 30 '18 at 10:28
  • Sorry, I wasn't sure how to add my code to the original post without confusing the future readers. – Jenny W Mar 30 '18 at 10:28
  • I added a snippet on running the update, and updated with how to properly get the query. Mostly you were: 1) missing a loop 1) again putting a `ref` at the end of your query. I recommend wiping this idiom from your vocabulary - ending a query with `.ref` just reverts you back to the reference that you started with. – Frank van Puffelen Mar 30 '18 at 14:01
  • Thank you Frank! Just one thing, this line is redundant, I think: let value = snapshot.value as? NSDictionary – Jenny W Mar 31 '18 at 11:45