Updating Table View after Updating Values in Firebase - ios

I'm able to successfully update my entries in Firebase, for it shows up on the console, but not the table view. If I restart my app the changes will then show, but I want them to show immediately.
My approach is to edit the array that populates the tableview as soon possible under the "ChildChanged" notification.
ref.observeEventType(.ChildChanged, withBlock: { (snapshot) in
print("One of the entries were changed so we're reloading the table view")
if let firstname = snapshot.value?.objectForKey("firstname"), lastname = snapshot.value?.objectForKey("lastname")
{
let fullname = "\(firstname) \(lastname)"
names[I_Need_This_Index] = fullname
dispatch_async(dispatch_get_main_queue())
{
self.tableView.reloadData()
}
}
}, withCancelBlock: nil)
As you can see, I just need to locate index of what I need, for I already have the "fullname" value which is the edited value that I wish to update the table view with.
How would I go about doing that?

You need to lookup the index using the key of the changed child. Therefore a dictionary could be set up initially to map the keys for all children to the index of your tableview cell when receiving all values.
Edit:
If you need the old value itself you have to obtain the initial change snapshot yourself.

Related

Get count of child in firebase when view controller is showing

I have a collection view with a list of matches cell and a button to add new matches.
I'd like to have the same numbers of cell as per matches (IE: 10 matches, 10 cells) . I want the list every time the view appears.
var countOfMatches = [DataSnapshot]()
viewWillAppear {
ref.child("matches").observe(.childAdded, with: { (snapshot) in
self.countOfMatches = [DataSnapshot]()
for item in snapshot.children {
self.countOfMatches.append(item as! DataSnapshot)
}
})}
and in the collection view
return countOfMatches.count
Why my cells are not showing? I can see the results created under /matches in Firebase DB but not the cells
You should use .value instead of .childAdded.
Also you should reload the tableView.reloadData() method.

attach Firebase listeners to tableViewCell

I have a chat feature in an iOS app. the chat previews are presented in a tableView and I am using a class for the TableView cells themselves. I want to listen for new messages so I can put a "new Message" label on the cells. The relevant parts of the tableView Cell are:
var chat: Chat! {
didSet {
self.updateUI()
self.observeNewMessages()
}
}
extension ChatTableViewCell {
func observeNewMessages() {
let chatMessageIdsRef = chat.ref.child("messageIds")
chatMessageIdsRef.observe(.childAdded, with: { snapshot in
let messageId = snapshot.value as! String
DatabaseReference.messages.reference().child(messageId).observe(.childAdded, with: { snapshot in
//let message = Message(dictionary: snapshot.value as! [String : Any])
print("looks like there is a new message") //works
self.messageLabel.text = "New Message!"
//self.delegate?.newMessage()
})
})
}
}
The print statement is there to test the listener. The problem I am having is when the table view loads and the cells get created, observeNewMessages() is executing the print statement regardless of whether there were new messages. It is just reading the old children that are already there first? Obviously this is going to cause the message label to always read "New Message." I'm not too good with Firebase so is there a better Firebase function to use or a better way to do this?
*I am using 2 queries because the Message dictionary will allow me to see if the current user made the message or not. The delegate is there because my next step will be to reload the tableview -- as it is now, this will also cause an infinite print statement loop...

How can I observe Firebase child value changes in multiple nodes in Swift?

I have a "users" node in my Firebase database. Each user in it has a root with a uid for it. One of the properties of each user is "coordinates". I want to observe any change in any of those coordinates for all users. I almost need something like:
usersDatabaseReference.child("*").child("coordinates").observe(.value, with: { (snapshot) in
My structure looks like this:
users
abcdefghijklmnop
coordinates: "12.345,12.345"
other props
qrstuvwxyz
coordinates: "34.567,34.567"
other props
So I'm looking for a global notification for any user whose coordinates value changes.
Do I need to loop through all children in my userDatabaseReference and set observers for each? And then any time a user is added or removed, set it all up again?
You can see why I'd like to use a wildcard. How should I approach this?
You could just observe the users structure as a whole, then use the snapshot to determine if the coordinate is the element that has changed
usersDatabaseReference.child("users").observe(.childChanged, with: { (snapshot) in
//Determine if coordinate has changed
})
What you could also do is :
func observeChildAdded(){
usersDatabaseReference.child("users").observe(.childAdded, with: { (snapshot) in
//Get the id of the new user added
let id = "123"
self.usersDatabaseReference.child("users").child(id).observe(.childChanged, with: { (snapshot) in
self.foundSnapshot(snapshot)
})
})
}
func foundSnapshot(_ snapshot: DataSnapshot){
let idChanged = snapshot.key
//Process new coordinates
}
So that whenever you add a new child it automatically sets up an observer for it, but they all get pushed to the same function foundSnapshot

loading information from Firebase in a defined order

I am posting some information to Firebase database
let bookObject: Dictionary<String, Any> = [
"uid": uid,
]
FIRDatabase.database().reference().child("posts").child(key).setValue(bookObject)
and I want to retreive the data in a tableView
func loadData(){
if (FIRAuth.auth()?.currentUser) != nil{
FIRDatabase.database().reference().child("posts").child(self.loggedInUser!.uid).observeSingleEvent(of: .value, with: { (snapshot:FIRDataSnapshot) in
let loggedInUserData = snapshot
if let postsDictionary = snapshot .value as? [String: AnyObject] {
for post in postsDictionary {
self.posts.add(post.value)
}
self.TableView.reloadData()
}})}
}
Which works, however, the posts get loaded to the TableView randomly, sometimes a new post would be posted to the top of the TableView, sometimes it would be posted to the bottom of the TableView. What do I do so that the new post shows on top of the previous cell.
You'll want to add a timestamp to each post and then sort the posts based on the timestamp. You didn't post how your TableView is built, but I recommend using a tableArray made of multiple posts that are a custom class Post that has all the properties of the post in Firebase. Then you can arrange the posts by doing tableArray.sort({$0.date > $1.date}), where date is one of the variables in Post containing a String of the timestamp when it was created. You probably want to sort the tableArray right before where you reload the TableView's data.
You can then populate your TableView with
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = //dequeueResuableCell
//retrieve data from the cell's corresponding post
let postCreator = tableArray[indexPath.row].creatorUID
let postContent = tableArray[indexPath.row].content
...
//populate the cell using the above data
...
}
and since your posts in the tableArray are in chronological order, the posts in your TableView will also be in chronological order.
Have a look at the section called 'Sort data' on this page of Firebase's documentation.
https://firebase.google.com/docs/database/ios/lists-of-data
It basically allows you to order the result of your query by a child of your choice. In your case you should order by 'date' or 'timestamp' depending on what you store on your Firebase Real-Time database.
Example:
// My top posts by number of stars
let myTopPostsQuery = (ref.child("user-posts").child(getUid())).queryOrdered(byChild: "starCount")
If when you order by child you still see the new entries coming at the bottom of your tableView, you can simply reverse your array to ensure the newest will come at the top.
yourArray.reversed()
Hope this helps.

Adding two of the same values to array instead of one - swift

I am trying to make a search bar, where you can search for users names. The cell is showing two things - and image and a label.
The label is showing the users name and the image is showing the profile picture. The image needs the user userID to display his/her picture.
The countryAndCode contains the users userID called country, and the users name called code.
My problem is that it is adding the first user twice and I do not know why or how to change it.
Here is the relevant code, let me know if you would like to see more:
func startObersvingDB() {
FIRDatabase.database().reference().child("UserInformation").observeEventType(.ChildAdded, withBlock: { snapshot in
let title = snapshot.value!["usersUID"] as? String
let name = snapshot.value!["userName"] as? String
self.tableData.append(title!)
self.tableDataNames.append(name!)
for i in 0..<self.tableData.count {
print(self.tableDataNames.count)
print(self.tableData.count)
self.countryAndCode.append((self.tableData[i], code: self.tableDataNames[i]))//this shows error in XCode some times
//if the above code doesn't compile then use the below code
print("DONE")
print(self.countryAndCode)
}
self.tableView.reloadData()
})
}

Resources