0

I have a Firebase DB with "post/(randID)" structure, and Post class that inherits from an Item class. I already wrote a snapshot function that properly takes the value of all child nodes, but am now trying to only take a snapshot of post/ children that match elements of a name array I already have.

I'm properly getting values properly getting values but not correctly appending temp values to my Item array at the breakpoint. Any help would be much appreciated

----------- CODE -----------

func getWavePosts() {
    self.tempPosts = []

    for name in self.tempNames {
        var postRef = Database.database().reference().child("posts/\(name)")

        postRef.observe(.value, with: {snapshot in
            var test = snapshot.value as? [String:Any]
            var author = test!["author"] as? [String:Any]
            var uid = author!["uid"] as? String
            var username = author!["username"] as? String
            var photoURL = author!["photoURL"] as? String
            var url = URL(string: photoURL!)
            var imageURL = test!["imageURL"] as? String
            var text = test!["text"] as? String
            var timestamp = test!["timestamp"] as? Double
            var userProfile = UserProfile(uid: uid!, username: username!, photoURL: url!)
            var post = Post(id: name, author: userProfile, text: text!, timestamp: timestamp!, imageURL: imageURL!)
            self.tempPosts.append(post)

            //print(self.tempPosts)
            //self.items = self.tempPosts
        })
       //self.items = self.tempPosts
    }
    print(self.tempPosts.count)
    print(self.items.count)
}
Doug Stevenson
  • 297,357
  • 32
  • 422
  • 441
diligentcrush
  • 39
  • 1
  • 5
  • `print(self.tempPosts.count)` will display `0` because `observe` works asynchronously. You need `DispatchGroup` to get notified after all items are retrieved. – vadian Dec 19 '18 at 19:03
  • Yeah in the other function I have the append functions in a Dispatch.main.queue block, but the breakpoint shows empty values as well which is why I'm confused – diligentcrush Dec 19 '18 at 19:04
  • Please look at https://stackoverflow.com/questions/52902639/know-when-an-iteration-over-array-with-async-method-is-finished/52902670#52902670 – vadian Dec 19 '18 at 19:08
  • While there are comments and suggestions, leveraging a DispatchGroup may not be needed. It seems you just want to look up information for each user in a list. You should not be using .observe as that leaves an observer in place. Use .observeSingleEvent. See [this question](https://stackoverflow.com/questions/43027817/how-to-perform-an-action-only-after-data-are-downloaded-from-firebase/43029121#43029121) and answer for a explanation of why you're getting 0 results. I am also a little suspect of what self.tempNames contains as userNames are keys for your posts - that will lead to other issues. – Jay Dec 21 '18 at 18:48

1 Answers1

1

First, your function should have completion with array of Post as parameter

func getWavePosts(_ completion: @escaping ([Post]) -> () )

...now let's meet with DispatchGroup.

First declare new DispatchGroup before foreach loop. Then before you observe postRef enter to dispatchGroup and after you append received Post to an array (define this array within function, don't use global variable) leave dispatchGroup. When every Post is added to an array, call completion in closure of dispatchGroup.notify(queue:)

func getWavePosts(_ completion: @escaping ([Post]) -> () ) {

    var tempPosts = []

    let dispatchGroup = DispatchGroup()

    for name in self.tempNames {

        dispatchGroup.enter()

        var postRef = Database.database().reference().child("posts/\(name)")

        postRef.observe(.value, with: { snapshot in
            ...
            tempPosts.append(post)
            dispatchGroup.leave()
        })
    }
    dispatchGroup.notify(queue: .main) {
        completion(tempPosts)
    }
}

Then you have access to your received posts in closure of this method when you call it

getWavePosts { posts in
    ... // do whatever you want to
}
Robert Dresler
  • 10,580
  • 2
  • 22
  • 40
  • @diligentcrush That code may be problematic; the tempPosts is being appended inside an asynch Firebase call. So, potentially the completion handler could be called well before all of the Firebase data has been retrieved. Also, it's creating a background task inside and already asychronous call - that can get messy. Also note [dispatchGroup.leave()](https://developer.apple.com/documentation/dispatch/dispatchgroup/1452872-leave) cannot be called more than .enter is called and that could happen in those case. It's not wrong but there are other options.... – Jay Dec 19 '18 at 21:01
  • One option is to simply use a counter so that when the last iteration over self.tempNames completes, *then* the next function you want executed is called - perhaps a tableView.reloadData(). Here's an unrelated question with an answer utilizing that technique; [Sort Data](https://stackoverflow.com/questions/53570841/how-to-sort-data-in-firebase/53573099#53573099). Note the *index == lastSnapIndex* comparison. Also, that technique eliminates the needs for a dispatchGroup, call back and loading background processes inside other processes. Just a thought. – Jay Dec 19 '18 at 21:02
  • @Jay thank you for this response! One question: I think I don't understand in which case for example should be this code problematic. Also thank you for the second comment. This was one of the option I had been considering but after I told myself, that this isn't look like clear way to solve this. – Robert Dresler Dec 19 '18 at 21:08
  • Check the link in the first comment for .leave() being matched to .enter(). The issue is that .observe may be called multiple times - any time anything within that node changes, the code in the observe closure is called. That would leave you with unbalanced .enter() and .leave() – Jay Dec 19 '18 at 21:17