Reputation: 127
I am creating a friends list on my app, fetchfriends makes a list of users that person is friends with, fetch user then looks at this list and populates a table view with friends information. This seems like it would work fine but for some reason fetch user is being ran before fetch friends can anyone help me with this? This is my code. I even tried a conditional statement which is now commented out and it just never ran the second function that way.
override func viewDidLoad() {
super.viewDidLoad()
ref = FIRDatabase.database().reference()
friendsTableView.delegate = self
friendsTableView.dataSource = self
fetchfriends()
//if userspresent == true {
fetchuser()
// }
// Do any additional setup after loading the view.
}
func fetchfriends () {
ref.child("users").child(userID!).child("friends").observeSingleEvent(of: .value, with: { (snapshot) in
if let value = snapshot.value as? NSDictionary {
let userName = value["username"] as? String ?? ""
let friendid = value["userid"] as? String ?? ""
self.userIds.append(friendid)
self.userspresent = true
print(userName, friendid)
}
})
}
// var index1:Int = 0
//if userspresent == true {
func fetchuser () {
//let count = userIds.count
for index1 in 0...userIds.count-1 {
ref.child("users").child(userIds[index1]).observeSingleEvent(of: .value, with: { (snapshot) in
let value = snapshot.value as? NSDictionary
let user = User()
let userName = value?["username"] as? String ?? ""
let email = value?["email"] as? String ?? ""
let profileImageUrl = value?["profileImage"] as? String ?? ""
user.userName = userName
user.email = email
user.profileImageUrl = profileImageUrl
print(user)
self.users.append(user)
DispatchQueue.main.async {
self.friendsTableView.reloadData()
}
print(user.userName!, user.email!)
// print(snapshot)
}, withCancel: nil)
}
}
Upvotes: 1
Views: 320
Reputation: 2160
@Taylor M is correct. These are asynchronous network operations, so you can't really say for certain which one will be completed first. However, since fetchuser
relies on data produced in fetchfriends
(namely, that there will be values in userIds
) you can simply add a closure as a parameter to fetchfriends
:
func fetchfriends (_ completion: ((Void) -> Void)?) {
ref.child("users").child(userID!).child("friends").observeSingleEvent(of: .value, with: { (snapshot) in
if let value = snapshot.value as? NSDictionary {
let userName = value["username"] as? String ?? ""
let friendid = value["userid"] as? String ?? ""
self.userIds.append(friendid)
self.userspresent = true
print(userName, friendid)
// call the closure function upon completion
completion?()
}
})
Now, when you call fetchfriends
in viewDidLoad
simply do:
fetchfriends {
// this will be called when the friends are successfully fetched
self.fetchuser()
}
Upvotes: 3
Reputation: 1865
What's happening is asyncronous network calls. You're incorrect in saying that fetch user is being ran before fetch friends if your code is as follows:
override func viewDidLoad() {
...
fetchfriends()
//if userspresent == true {
fetchuser()
// }
}
In your observeSingleEvent
call you provide this function a closure to run after firebase observes that event.
So you call fetchfriends
(first) which calls an observeSingleEvent
. The OS places this network call on a background thread to do its work, and when the network call is finished, your provided closure is executed. Meanwhile, on the main thread, your program keeps running and calls fetchuser
(second) which also calls observeSingeEvent
and places that call and closure on a different background thread and your program keeps running. Most likely it seems as though the closure written in the fetchuser
method is be run first because that network takes less time and is the first one back.
Upvotes: 0