Reputation: 31
I have an array containing post IDs, then I do a for
loop and retrieve each post content and append the post to the post array. After the loop ends, I need to return the post array in the completion handler.
However, the .observeSingleEvent is an async method, therefore the posts contents are retrieve from Firebase, the completionhandler(posts) is already executed, which returns an empty array. I want to return the array after every post is appended to the array, similar to making these methods synchronous.
static func getPost(postKeys:[String], completionHandler: @escaping ([Post])->()){
var posts = [Post]()
for postKey in postKeys{
let postRef = DataService.ds.REF_POSTS.child(postKey)
postRef.observeSingleEvent(of: .value, with: { (snapshot) in
if let postDict = snapshot.value as? Dictionary<String, Any>{
//create a post and append it to the post array
let key = snapshot.key
let post = Post(postKey: key, postData: postDict)
posts.append(post)
}
})
}
completionHandler(posts)
}
Upvotes: 1
Views: 608
Reputation: 114974
You can use a DispatchGroup
to execute some code when a series of tasks is complete.
Call the enter
function before you start a task and the leave
function once the task is complete. You can use notify
to provide a closure that will be executed once all tasks have 'left' the DispatchGroup
.
You also need to be careful to avoid concurrency issues with updating the array; Swift arrays are not thread-safe. Executing the array update on a serial dispatch queue can fix this.
static func getPost(postKeys:[String], completionHandler: @escaping ([Post])->()){
var posts = [Post]()
let dispatchGroup = DispatchGroup()
let arrayQueue = DispatchQueue(label: "arrayQueue")
for postKey in postKeys{
dispatchGroup.enter()
let postRef = DataService.ds.REF_POSTS.child(postKey)
postRef.observeSingleEvent(of: .value, with: { (snapshot) in
if let postDict = snapshot.value as? Dictionary<String, Any>{
//create a post and append it to the post array
let key = snapshot.key
let post = Post(postKey: key, postData: postDict)
arrayQueue.sync {
posts.append(post)
}
}
dispatchGroup.leave()
})
}
dispatchGroup.notify(queue: DispatchQueue.main) {
completionHandler(posts)
}
}
Upvotes: 4