FIRDatabase UISearchBar string for child - ios

New to searchbars.
Working:
[String] "firstname" returns correct value when searching. If I have 3 people with "firstname" beginning with "G" (for example) table reloads with 3 cells.
Problem:
Although table reloads with proper cell values for "firstname", users.append(user) returns nil and wrong names are loaded onto the tableview.
Assistance:
How can I load correct names to tableview after search has been completed?
Here is my code:
func searchBar(_ searchBar: UISearchBar, textDidChange textSearched: String)->Void {
FIRDatabase.database().reference().child("users").queryOrdered(byChild: "firstname").queryStarting(atValue: textSearched).queryEnding(atValue: textSearched+"\u{f8ff}").observe(.value, with: { snapshot in
var users = [User]()
let user = User()
print(user)
for _ in snapshot.children.allObjects as! [FIRDataSnapshot] {
if let dictionary = snapshot.value as? [String: AnyObject]{
user.lastname = dictionary["firstname"] as? String
users.append(user)
}
}
self.users = users
let search = searchCell()
search.firstName.text = user.firstname
self.attempReloadOfTable()
}, withCancel: nil)
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath) as! searchCell
var user = User()
user = users[indexPath.row]
if let id = user.id{
let ref = FIRDatabase.database().reference().child("users").child(id)
ref.observe(.value, with: { (snapshot) in
cell.lastName.text = user.lastname
cell.firstName.text = user.firstname
})
}
return cell
}

Your problem is cell is returned before it's bound with user data in the block. Because code in the FIRBase result query block will be performed after return cell is executed.
i edited your code like this:
func searchBar(_ searchBar: UISearchBar, textDidChange textSearched: String)->Void {
FIRDatabase.database().reference().child("users").queryOrdered(byChild: "firstname").queryStarting(atValue: textSearched).queryEnding(atValue: textSearched+"\u{f8ff}").observe(.value, with: { snapshot in
var users = [User]()
for _ in snapshot.children.allObjects as! [FIRDataSnapshot] {
if let dictionary = snapshot.value as? [String: AnyObject] {
let user = User()
user.lastname = dictionary["firstname"] as? String
print(user)
users.append(user)
}
}
self.users = users
let search = searchCell()
search.firstName.text = user.firstname
self.attempReloadOfTable()
}, withCancel: nil)
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath) as! searchCell
let user = users[indexPath.row]
cell.lastName.text = user.lastname
cell.firstName.text = user.firstname
return cell
}
Hope it'll work for you.

Related

Update that object with the new info in array and to display tableview Swift

I am using firebase realtime database and implementing user profile data with usersFriend and location. I need to implement the update in object array and show updated values in tableview. I have tried but I am not successful in updating object and then tableview reload. Function already developed.
I need to show updated object array swapped with new values and display in tableview.
var myFriendsDataSource = [FriendClass]()
func watchForChangesInMyFriends() {
let usersRef = self.ref.child("profiles") usersRef.observe(.childChanged, with: { snapshot in
let key = snapshot.key
if let friendIndex = self.myFriendsDataSource.firstIndex(where: { $0.uid == key} ) {
let friend = self.myFriendsDataSource[friendIndex]
print("found user \(friend.batteryStatus), updating")
self.myFriendsDataSource[friendIndex] = friend
self.tableView.reloadData()
}
})
}
Class:
class FriendClass {
var uid = ""
var name = ""
var batteryStatus = Int()
var latitude = Double()
var longitude = Double()
var timeStamp = Int64()
//var profilePic
init(withSnapshot: DataSnapshot) {
self.uid = withSnapshot.key
self.name = withSnapshot.childSnapshot(forPath: "name").value as? String ?? "No Name"
self.batteryStatus = withSnapshot.childSnapshot(forPath: "batteryStatus").value as? Int ?? 0
self.latitude = withSnapshot.childSnapshot(forPath: "latitude").value as? Double ?? 0.0
self.longitude = withSnapshot.childSnapshot(forPath: "longitude").value as? Double ?? 0.0
self.timeStamp = withSnapshot.childSnapshot(forPath: "timeStamp").value as? Int64 ?? 0
}
}
Updated:
func loadUsersFriends() {
let uid = "zzV6DQSXUyUkPHgENDbZ9EjXVBj2"
let myFriendsRef = self.ref.child("userFriends").child(uid)
myFriendsRef.observeSingleEvent(of: .value, with: { snapshot in
let uidArray = snapshot.children.allObjects as! [DataSnapshot]
for friendsUid in uidArray {
self.loadFriend(withUid: friendsUid.key)
print(friendsUid)
}
})
}
func loadFriend(withUid: String) {
let thisUserRef = self.ref.child("profiles").child(withUid)
thisUserRef.observeSingleEvent(of: .value, with: { snapshot in
let aFriend = FriendClass(withSnapshot: snapshot)
self.myFriendsDataSource.append(aFriend)
print(self.myFriendsDataSource)
self.tableView.reloadData()
self.watchForChangesInMyFriends()
})
}
Update 2:
extension ViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 70
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 10
}
}
extension ViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return myFriendsDataSource.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "FriendListTableViewCell", for: indexPath) as! FriendListTableViewCell
let dic = myFriendsDataSource[indexPath.row]
cell.frndName.text = dic.name
return cell
}
Given the above comment discussion, I think you need to update your watchForChangesInMyFriends method as below to actually update the datasource with the new friend data. You should also do all your UI updates on the main thread, and as there is no guarantee that this closure will run on the main thread you need to force the tableView update onto the main thread.
func watchForChangesInMyFriends() {
let usersRef = self.ref.child("profiles") usersRef.observe(.childChanged, with: { snapshot in
let key = snapshot.key
if let friendIndex = self.myFriendsDataSource.firstIndex(where: { $0.uid == key} ) {
let friend = self.myFriendsDataSource[friendIndex]
print("found user \(friend.batteryStatus), updating")
self.myFriendsDataSource[friendIndex] = FriendClass(withSnaphot: snapshot)
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
})
}
It's also better practice to update just the tableView data that has changed rather than reloading the whole tableView. You can probably use the array index to generate an IndexPath for the appropriate row and then just reload that row. Without seeing your tableView methods I can't be precise, but it'll probably look something like this:
let indexPath = IndexPath(row: friendIndex, section: 0)
DispatchQueue.main.async {
self.tableView.reloadRows(at: [indexPath], with: .automatic)
}

custom class data not showing in to the tableview in swift

hi am fetching from data from firebase and stored into custom class and displaying into the tableview, when I displaying tableview its not showing empty but when I am calling with another array data its working fine
class datapass {
var fullName : String?
var status : String?
var dateAndTimeofVisit :String?
var profilephot :String?
var uid :String?
var inviterUID: String?
init(fullname:String?,status:String?,dateAndTimeofVisit:String?,profilephot:String,uid:String?,inviterUID:String?) {
self.fullName = fullname
self.status = status
self.dateAndTimeofVisit = dateAndTimeofVisit
self.profilephot = profilephot
self.uid = uid
self.inviterUID = inviterUID
}
}
inside viewdidload
var datapassval = [datapass]()
HandedDataRef = Database.database().reference()
.child(Constants.FIREBASE_CHILD_VISITORS)
.child(Constants.FIREBASE_CHILD_PRE_APPROVED_VISITORS).child("-LHO1TuRZTKCZV5Mli13")
HandedDataRef?.observeSingleEvent(of: .value, with: {(snapshot) in
print("snapshot values",snapshot)
if snapshot.exists() {
let visitorData = snapshot.value as? [String: AnyObject]
print("visitordata",visitorData as Any)
let dateAndTimeOfVisit = visitorData?[VisitorListFBKeys.dateAndTimeOfVisit.key] as? String
let fullName = visitorData?[VisitorListFBKeys.fullName.key] as? String
let inviterUID = visitorData?[VisitorListFBKeys.inviterUID.key] as? String
let mobileNumber = visitorData?[VisitorListFBKeys.mobileNumber.key] as? String
let profilePhoto = visitorData?[VisitorListFBKeys.profilePhoto.key] as? String
let status = visitorData?[VisitorListFBKeys.status.key] as? String
let uid = visitorData?[VisitorListFBKeys.uid.key] as? String
self.datapassval.append(datapass.init(fullname: fullName, status: status , dateAndTimeofVisit: dateAndTimeOfVisit, profilephot: profilePhoto!, uid: uid, inviterUID: inviterUID))
}
})
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return datapassval.count //count getting 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! HandedThingsToGuestTableViewCell
let datamy = datapassval[indexPath.row]
print("datamy",datamy)
cell.lbl_VisiterName.text = datamy.fullName
}
its not showing any thing how to display custom class data where I did mistake
You need to reload the table
self.datapassval.append(datapass.init(fullname: fullName, status: status , dateAndTimeofVisit: dateAndTimeOfVisit, profilephot: profilePhoto!, uid: uid, inviterUID: inviterUID))
self.tableView.reloadData()

Duplicate Cell loading in UItableview Swift 4

I have been stuck on this bug for the past 5 hours I Really need some help. I am making a chat application that has private messaging. When a user sends a text everything works great. But when a use trys to reply to a message another user sent , things get weird.
the user duplicates, along with failing to update time and the most recent text! whats going on! here is the code
Message controller: loads the message users from which firebase provides I know this could have potentially been an uploading to firebase issue, where their are two children but my firebase looks fine, NO duplicates on the back end. The error ive narrowed it down to being in this class when loading these custom cells into a table view! how do i force it to stop duplicating and rather load the correct one? Thank you so much!
import UIKit
import Firebase
class MessagesViewController: UIViewController , UITableViewDelegate
, UITableViewDataSource{
#IBOutlet weak var messagesLabelOutlet: UILabel!
#IBOutlet weak var messagesTableView: UITableView!
var newUser : User?
var messageArr = [Message]()
var messageDict = [String: Message]()
override func viewDidLoad() {
super.viewDidLoad()
messagesTableView.dataSource = self
messagesTableView.delegate = self;
self.messagesTableView.register(UserCell.self, forCellReuseIdentifier: "cellId")
checkIfUserIsLoggedIn()
messageArr.removeAll()
messageDict.removeAll()
messagesTableView.reloadData()
observeUserMessages()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return messageArr.count;
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 72;
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.newUser = User()
if let chatPartnerId = messageArr[indexPath.row].chatPartnerId(){
self.newUser?.toId! = chatPartnerId;
let chatPartnerDataRef = Database.database().reference().child("users").child(chatPartnerId)
chatPartnerDataRef.observeSingleEvent(of: .value) { (snapshot) in
guard let dict = snapshot.value as? [String : AnyObject] else{
return
}
self.newUser?.userName = dict["username"] as? String
self.newUser?.picURL = dict["pic"] as? String
self.newUser?.score = dict["score"] as? String
self.performSegue(withIdentifier:
"goToChatLogControllerPlzFromMessages", sender: self)
}
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cellId", for: indexPath) as! UserCell
let message = messageArr[indexPath.row]
let totalSection = tableView.numberOfSections
cell.textLabel?.font = UIFont(name: "Avenir Book" , size: 19);
cell.detailTextLabel?.font = UIFont(name: "Avenir Light" , size: 14);
cell.message = message;
return cell;
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// do not remove
if segue.identifier == "goToChatLogControllerPlzFromMessages"{
print("going to chat log")
let recieveVC = segue.destination as! ChatLogController
if let textUser = newUser{
recieveVC.user = textUser;
}
}
}
func checkIfUserIsLoggedIn()
{
if Auth.auth().currentUser?.uid == nil{
print("uid is nil")
performSegue(withIdentifier: "noUserFoundGoingBackToLogin", sender: self);
}
else{
let uid = Auth.auth().currentUser?.uid
Database.database().reference().child("users").child(uid!).observeSingleEvent(of: .value, with: { (snapshot) in
})
}
}
func observeUserMessages(){
print("NEW USER \(newUser?.userName)")
print("MESSAGE ARR \(messageArr)")
print("MESSAGE DICT\(messageDict.values)")
guard let uid = Auth.auth().currentUser?.uid else{
checkIfUserIsLoggedIn()
return;
}
let ref = Database.database().reference().child("user-messages").child(uid)
ref.observe(.childAdded) { (snapshot) in
let messageId = snapshot.key
let messagesRef = Database.database().reference().child("messages").child(messageId)
messagesRef.observeSingleEvent(of: .value, with: { (snapshot) in
if let dict = snapshot.value as? [String : AnyObject]
{
let message = Message()
message.fromId = dict["fromid"] as? String;
message.text = dict["text"] as? String;
message.timestamp = dict["timestamp"] as? String;
message.toId = dict["toid"] as? String;
self.messageArr.append(message)
if let toID = message.toId{
self.messageDict[toID] = message;
self.messageArr = Array(self.messageDict.values)
self.messageArr.sort(by: { (message1, message2) -> Bool in
let time1 = Int(truncating: (message1.timestamp?.numberValue)!)
let time2 = Int(truncating: (message2.timestamp?.numberValue)!)
return time1 > time2
})
}
DispatchQueue.main.async {
print(message.text!)
self.messagesTableView.reloadData()
}
}
})
}

UITableView not loading Firebase Snapshot Data.

I have a Firebase database with structure:
"users"
-uid
- name
- email
. I would like to input the "users" email and name into a UITableviewController tableview in XCode. The data can be seen in my console, but is not appended to my Table View
class DictionaryTableViewController: UITableViewController {
var ref: FIRDatabaseReference!
let cellID = "Cell"
var refHandle: UInt!
var userList = [Users]()
override func viewDidLoad() {
super.viewDidLoad()
//Set firebase database reference
ref = FIRDatabase.database().reference()
//Retrieve posts and listen for changes
refHandle = ref?.child("users").observe(.childAdded, with: { (snapshot) in
//Code that executes when child is added
if let dict = snapshot.value as? [String: AnyObject] {
let user = Users()
user.name = snapshot.childSnapshot(forPath: "name").value as? String
print(user.name)
user.email = snapshot.childSnapshot(forPath: "email").value as? String
print(user.email)
print("databaseHandle was called")
for user in self.userList {
print(user)
self.userList.append(user)
}
self.tableView.reloadData()
}
})
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return userList.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: .subtitle, reuseIdentifier: cellID)
cell.textLabel?.text = userList[indexPath.row].name.self
cell.textLabel?.text = userList[indexPath.row].email.self
return cell
}
}
}
Remove this:
self.tableView.reloadData()
And after the if let statements add this:
DispatchQueue.main.async{
self.tableView.reloadData()
}
Like so; does not show the data on the table still.
//Retrieve posts and listen for changes
func fetchUserData(with completion:#escaping (Bool)->()) {
refHandle = ref?.child("users").observe(.childAdded, with: {
(snapshot) in
//Code that executes when child is added
if (snapshot.value as? [String: AnyObject]) != nil {
let user = Users()
user.name = snapshot.childSnapshot(forPath: "name").value as?
String
print(user.name)
DispatchQueue.main.async{
user.email = snapshot.childSnapshot(forPath: "email").value
as? String
print(user.email)
print("databaseHandle was called")
for user in self.userList {
print(user)
self.userList.append(user)
self.userTable.reloadData()
}

How to give different tags to buttons in different TableView cells in Swift

I'm developing an app where users writes entries to different topics then can give up and down points to the entries. I used in my tableViewController the function:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
and I added these two lines at the end of this function:
cell.plusButton.tag = indexPath.row
cell.minusButton.tag = indexPath.row
So this should give every button in the tableView a tag so that its the same as indexpath.row of that cell, am I wrong? Because when I then try to call the buttons, all of their tags are same and equals to 0. How can I give them different tags? Is there no way to do so in this way?
This is what the code is when I want to call the button:
#IBAction func plus(sender: AnyObject) {
print(self.tag)
let ref = FIRDatabase.database().reference().child("topics/"+topicClicked+"/"+entriesArrayTwo[self.tag])
var value = Int()
var date = String()
var user = String()
var votedDown = [""]
var votedUp = [""]
ref.observeSingleEventOfType(.Value, withBlock: { snapshot in
let dict = snapshot.value as! [String: AnyObject]
value = dict["point"] as! Int
date = String(dict["date"]!)
user = String(dict["user"]!)
votedUp = dict["votedUp"] as! NSArray as! [String]
votedDown = dict["votedDown"] as! NSArray as! [String]
var tempBool = false
var temp = -1
for uid in votedDown {
temp = temp + 1
if uid == FIRAuth.auth()?.currentUser?.uid {
votedDown.removeAtIndex(temp)
tempBool = true
}
}
if tempBool == false {
votedUp.append((FIRAuth.auth()?.currentUser?.uid)!)
}
ref.setValue(["point": value+1, "date": date, "user": user, "votedDown": votedDown, "votedUp": votedUp])
self.point.text = String(value+1)
})
if minusButton.hidden == true {
minusButton.hidden = false
} else {
plusButton.hidden = true
}
}
My tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell function is below:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "entryCell", for: indexPath) as! HubEntryTableViewCell
if self.resultSearchController.isActive {
let ref = FIRDatabase.database().reference().child("topics/"+topicClicked+"/"+filteredTableData[(indexPath as NSIndexPath).row])
ref.observeSingleEvent(of: .value, with: { snapshot in
let value = snapshot.value as? NSDictionary
cell.point.text = String(describing: value!["point"]!)
let postRef = FIRDatabase.database().reference().child("users/"+String(describing: value!["user"]!))
postRef.observeSingleEvent(of: .value, with: { snapshotTwo in
let valueTwo = snapshotTwo.value as? NSDictionary
cell.subInfo.text = String(describing: valueTwo!["name"]!)+" "+String(describing: valueTwo!["surname"]!)+" - "+String(describing: value!["date"]!)
})
})
cell.entry.text = self.filteredTableData[(indexPath as NSIndexPath).row]
} else {
let ref = FIRDatabase.database().reference().child("topics/"+topicClicked+"/"+entriesArray[(indexPath as NSIndexPath).row])
ref.observeSingleEvent(of: .value, with: { snapshot in
let value = snapshot.value as? NSDictionary
cell.point.text = String(describing: value!["point"]!)
let postRef = FIRDatabase.database().reference().child("users/"+String(describing: value!["user"]!))
postRef.observeSingleEvent(of: .value, with: { snapshotTwo in
let valueTwo = snapshotTwo.value as? NSDictionary
cell.subInfo.text = String(describing: valueTwo!["name"]!)+" "+String(describing: valueTwo!["surname"]!)+" - "+String(describing: value!["date"]!)
})
let votedUpRef = ref.child("votedUp")
votedUpRef.observeSingleEvent(of: .value, with: { upSnapshot in
var tempDict = snapshot.value as! [String: AnyObject]
let tempArray = tempDict["votedUp"] as! [String]
for uid in tempArray {
if String(uid) == FIRAuth.auth()?.currentUser?.uid {
cell.plusButton.isHidden = true
}
}
})
let votedDownRef = ref.child("votedDown")
votedUpRef.observeSingleEvent(of: .value, with: { upSnapshot in
var tempDict = snapshot.value as! [String: AnyObject]
let tempArray = tempDict["votedDown"] as! [String]
for uid in tempArray {
if String(uid) == FIRAuth.auth()?.currentUser?.uid {
cell.minusButton.isHidden = true
}
}
})
})
cell.entry.text = self.entriesArray[(indexPath as NSIndexPath).row]
}
cell.plusButton.tag = (indexPath as NSIndexPath).row
cell.minusButton.tag = (indexPath as NSIndexPath).row
// NEW METHOD TO GET THE BUTTON
let check1: UIButton = (cell.viewWithTag(1) as! UIButton)
let check2: UIButton = (cell.viewWithTag(2) as! UIButton)
check1.addTarget(self, action: #selector(HubEntriesTableViewController.CloseMethod(_:event:)), for: .touchDown)
check2.addTarget(self, action: #selector(HubEntriesTableViewController.CloseMethod1(_:event:)), for: .touchDown)
// Configure the cell...
return cell
}
Perhaps finally found an issue. When I reproduced the problem in my project, I realised that downcasting to UIButton was missing.
So within HubEntryTableViewCell subclass update the method like this:
#IBAction func plus(sender: AnyObject) {
// self.tag, if called from UITableViewCell subclass, is rather cell's tag, not button's tag
let button = sender as! UIButton
print("button.tag = \(button.tag)")
...
}
If you have perform action on button click in tableview then try this code you don't worry about tag ..
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Eventcell", forIndexPath: indexPath)
let check1: UIButton = (cell.viewWithTag(11) as! UIButton)
let check2: UIButton = (cell.viewWithTag(12) as! UIButton)
check1.addTarget(self, action: #selector(EventListController.CloseMethod(_:event:)), forControlEvents: .TouchDown)
check2.addTarget(self, action: #selector(EventListController.CloseMethod1(_:event:)), forControlEvents: .TouchDown)
return cell
}
#IBAction func CloseMethod(sender: UIButton, event: AnyObject) {
let touches = event.allTouches()!
let touch = touches.first!
let currentTouchPosition = touch.locationInView(self.Eventlisttable)
let indexPath = self.Eventlisttable.indexPathForRowAtPoint(currentTouchPosition)!
print("\(Int(indexPath.row))")
}
#IBAction func CloseMethod1(sender: UIButton, event: AnyObject) {
let touches = event.allTouches()!
let touch = touches.first!
let currentTouchPosition = touch.locationInView(self.Eventlisttable)
let indexPath = self.Eventlisttable.indexPathForRowAtPoint(currentTouchPosition)!
print("\(Int(indexPath.row))")
}
If my answer help you then give vote. thank you..

Resources