Reputation: 143
I am having trouble understanding why this code is executing the completion block before users are appended to a category.
First I am trying to fetch the category. Each category has an array of user Id's, which are looped over to fetch a user from a separate location in my database.
Here is the Firebase data:
["Category 1": {
title = lifestyle;
users = (
ESKYpDMPiHW34,
HJ8ItJDoExZMQ,
1WDnoPy4PeQkm
);
}, "Category 2": {
title = fitness;
users = (
ESKYpDMPiHW3,
HJ8ItJDoExZM,
1WDnoPy4PeQk
);
}, "Category 3": {
title = health;
}]
Here is my code:
class func fetchFeaturedUsers(completion: @escaping ([UserCategory]) -> Swift.Void) {
var categories = [UserCategory]()
let dbRef = Database.database().reference().child("categories")
dbRef.observeSingleEvent(of: .value, with: { (snap) in
if let dict = snap.value as? [String: Any] {
for (_, value) in dict {
if let category = value as? [String:Any] {
let title = category["title"] as? String
let newCategory = UserCategory(title: title?.capitalized, users: [User]())
categories.append(newCategory)
if let users = category["users"] as? [String] {
for id in users {
User.fetchUser(userId: id, completion: { (newUser) in
newCategory.users?.append(newUser)
})
}
}
}
}
}
DispatchQueue.main.async {
completion(categories)
}
})
}
Upvotes: 1
Views: 291
Reputation: 2568
The problem is that your User.fetchUser
method is an asynchronous call that is being done in another thread. To solve that you can create a DispatchGroup
that will wait until all calls be completed before calling completion
like this:
class func fetchFeaturedUsers(completion: @escaping ([UserCategory]) -> Swift.Void) {
var categories = [UserCategory]()
let dispatchGroup = DispatchGroup()
let dbRef = Database.database().reference().child("categories")
dbRef.observeSingleEvent(of: .value, with: { (snap) in
if let dict = snap.value as? [String: Any] {
for (_, value) in dict {
if let category = value as? [String:Any] {
let title = category["title"] as? String
let newCategory = UserCategory(title: title?.capitalized, users: [User]())
categories.append(newCategory)
if let users = category["users"] as? [String] {
for id in users {
dispatchGroup.enter()
User.fetchUser(userId: id, completion: { (newUser) in
newCategory.users?.append(newUser)
dispatchGroup.leave()
})
}
}
}
}
}
dispatchGroup.notify(queue: DispatchQueue.global(qos: .background)){
completion(categories)
}
})
}
You can read more about DispatchGroup in the documentation.
Upvotes: 1