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()
}
Related
I am creating a tableview with one cell, and retrieving data from Firebase to display as a label firstName. The data is being retrieved (I have checked by using a print statement) however the label is not showing up, even though alpha is set to 1. Any help would be much appreciated.
Here is my code:
import FirebaseDatabase
import FirebaseStorage
import FirebaseAuth
import SwiftKeychainWrapper
class userTableViewController: UITableViewController {
var profileData = [profileStruct]()
let storageRef = Storage.storage().reference()
var databaseRef = Database.database().reference()
var ref: DatabaseReference?
var firstName = ""
var lastName = ""
var email = ""
var phoneNumber = ""
override func viewDidLoad() {
super.viewDidLoad()
getProfile()
// Do any additional setup after loading the view.
// tableView.register(profileCell.self, forCellReuseIdentifier: "ProfileCell")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
struct profileStruct {
let email : String!
let phoneNumber : String!
let firstName : String!
let lastName : String!
}
#IBAction func signOut(_ sender: Any) {
KeychainWrapper.standard.removeObject(forKey: "uid")
do {
try Auth.auth().signOut()
} catch let signOutError as NSError {
print ("Error signing out: %#", signOutError)
}
dismiss(animated: true, completion: nil)
}
func getProfile() {
let databaseRef = Database.database().reference()
databaseRef.child("users").queryOrderedByKey().observeSingleEvent(of: .childAdded, with: {
snapshot in
self.firstName = ((snapshot.value as? NSDictionary)!["firstname"] as? String)!
self.lastName = ((snapshot.value as? NSDictionary)!["lastname"] as? String)!
self.email = ((snapshot.value as? NSDictionary)!["email"] as? String)!
self.phoneNumber = ((snapshot.value as? NSDictionary)!["phone number"] as? String)!
print(self.firstName)
DispatchQueue.main.async {
self.tableView.reloadData()
}
})
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
// tableView.dequeueReusableCell(withIdentifier: "PostCell")!.frame.size.height
return 500
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "ProfileCell") as? profileCell else { return UITableViewCell() }
cell.firstNameLabel?.text = "first name: " + firstName
cell.lastNameLabel?.text = "last name: " + lastName
cell.emailLabel?.text = "email: " + email
cell.phoneNumberLabel?.text = "phone number: " + phoneNumber
return cell
}
}
Add a breakpoint on guard let cell = tableView.dequeueReusableCell(withIdentifier: "ProfileCell") as? profileCell else { return UITableViewCell() }, you need to check whether the as? profileCell is successful, and check whether the Data Source is invoked.
If the first step is working, you need to check the profileCell UI layout. Open the Debug View Hierarchy, and check whether any cell on tableView.
func getProfile() {
let databaseRef = Database.database().reference()
databaseRef.child("users").queryOrderedByKey().observeSingleEvent(of: .childAdded, with: {
snapshot in
self.firstName = ((snapshot.value as? NSDictionary)!["firstname"] as? String)!
self.lastName = ((snapshot.value as? NSDictionary)!["lastname"] as? String)!
self.email = ((snapshot.value as? NSDictionary)!["email"] as? String)!
self.phoneNumber = ((snapshot.value as? NSDictionary)!["phone number"] as? String)!
print(self.firstName)
self.tableView.dataSource = self
self.tableView.delegate = self
self.tableView.reloadData()
})}
You seem to show the email instead of the first name here.
cell.emailLabel?.text = "email: " + firstName
cell.phoneNumberLabel?.text = "phone number: " + lastName
cell.firstNameLabel?.text = "first name: " + email
cell.lastNameLabel?.text = "last name: " + phoneNumber
I have my firebase database structured like this:
Snap (-KWLSAIh5WJvNJOkxBEr) {
beschrijving = "description";
image = "link to image";
title = "title";
}
Snap (-KWLSTak0H20X_2Qnanv) {
beschrijving = "description";
image = "link to image";
title = "title";
}
This is the code I am using to display this in a TableView:
import UIKit
import Firebase
class NieuwsTableViewController: UITableViewController {
var users = [UsersII]()
let cellId = "IdCell"
override func viewDidLoad() {
super.viewDidLoad()
fetchUser()
}
func fetchUser() {
Database.database().reference().child("Blog").observe(.childAdded, with: { (snapshot) in
if let dictionary = snapshot.value as? [String: AnyObject] {
let user = UsersII(dictionary: dictionary)
self.users.append(user)
print(snapshot)
DispatchQueue.main.async(execute: {
self.tableView.reloadData()
})
}
}, withCancel: nil)
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return users.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> lllTableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath)
let user = users.reversed()[indexPath.row]
cell.textLabel?.text = user.name
return cell as! lllTableViewCell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let message = users.reversed()[indexPath.row]
guard let beschrijving = message.beschrijving else {
return
}
guard let image = message.plaatje else {
return
}
guard let titel = message.name else {
return
}
UserDefaults.standard.set(beschrijving, forKey: "nieuwsBeschrijving")
UserDefaults.standard.set(image,forKey: "nieuwsPlaatje")
UserDefaults.standard.set(titel, forKey: "nieuwsTitel")
self.performSegue(withIdentifier: "gotonews", sender: nil)
}
}
And I don't know if you will need this to answer this question but I'll also post the "UsersII" (defined as users just above the viewDidLoad method) in case this is needed to answer the question.
import UIKit
class UsersII: NSObject {
var name: String?
var beschrijving: String?
var plaatje: String?
init(dictionary: [String: Any]) {
self.name = dictionary["title"] as? String ?? ""
self.beschrijving = dictionary["beschrijving"] as? String ?? ""
self.plaatje = dictionary["image"] as? String ?? ""
}
}
so what I want to achieve is that if you click on one of the cells, you get the parent id of the article, so in this case that would be the "-KWLSAIh5WJvNJOkxBEr or -KWLSTak0H20X_2Qnanv" I mentioned above in my firebase database structure.
Here is what i was saying you to do:
Your model class:
class UsersII: NSObject {
var parentId: String?
var name: String?
var beschrijving: String?
var plaatje: String?
init(dictionary: [String: Any],parentId:String) {
self.name = dictionary["title"] as? String ?? ""
self.beschrijving = dictionary["beschrijving"] as? String ?? ""
self.plaatje = dictionary["image"] as? String ?? ""
self.parentId = parentId
}
}
Fetch user method:
func fetchUser() {
Database.database().reference().child("Blog").observe(.childAdded, with: { (snapshot) in
if let dictionary = snapshot.value as? [String: AnyObject] {
let user = UsersII(dictionary: dictionary,parentId:snapshot.key)
self.users.append(user)
print(snapshot)
DispatchQueue.main.async(execute: {
self.tableView.reloadData()
})
}
}, withCancel: nil)
}
And finaly you didSelect:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let message = users.reversed()[indexPath.row]
guard let beschrijving = message.beschrijving else {
return
}
guard let image = message.plaatje else {
return
}
guard let titel = message.name else {
return
}
guard let parentId = message.name else
{
return
}
UserDefaults.standard.set(beschrijving, forKey: "nieuwsBeschrijving")
UserDefaults.standard.set(image,forKey: "nieuwsPlaatje")
UserDefaults.standard.set(titel, forKey: "nieuwsTitel")
UserDefaults.standard.set(parentId,forKey: "nieuwsParentId")
self.performSegue(withIdentifier: "gotonews", sender: nil)
}
}
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()
}
}
})
}
I am a swift beginner,and I want to get value from firebase database,but it always recived twice same dictionary structure,and can't put value in tableview cells when I unwrapping it crashed...
here is my JSON format
Code work
import UIKit
import Firebase
//import FirebaseAuthUI
//import FirebaseGoogleAuthUI
//import FirebaseFacebookAuthUI
let device = FIRDatabase.database().reference()
class MainTableViewController: UITableViewController
{
var dic:NSDictionary?
override func viewDidLoad()
{
super.viewDidLoad()
//獲取當前登陸用戶
FIRAuth.auth()?.addStateDidChangeListener(self.UserAlive(auth:user:))
print("主畫面viewDidLoad")
}
func UserAlive(auth: FIRAuth, user: FIRUser?)
{
if user == nil
{
self.present((self.storyboard?.instantiateViewController(withIdentifier: "SignIn"))!, animated: true, completion: nil)
}
else
{
csGolbal.g_User = user
CheckData()
}
}
func CheckData()
{
print("CHECKDATA")
let ref = device.child("USER").child(csGolbal.g_User!.email!.replacingOccurrences(of: ".", with: "_"))
ref.observeSingleEvent(of: .value, with:
{ (snapshot) in
if snapshot.exists()
{
csGolbal.g_key = ((snapshot.value as AnyObject).allKeys)!
}
ref.child(csGolbal.g_key![0] as! String).observeSingleEvent(of: .value, with:
{ (snapshot) in
// Get user value
self.dic = snapshot.value as? NSDictionary
print(self.dic)
//self.tableView.reloadData()
})
})
}
and here is I don't get it how to put in
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
if let number = csGolbal.g_key?.count
{
return number
}
else
{
return 0
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCell(withIdentifier: "MainTableViewCell", for: indexPath) as! MainTableViewCell
//put in here
// label.text and ImageView
return cell
}
please hlep me,and tell me where I am do wrong.
#dahiya_boy I try your function
func getDataFromDB()
{
DispatchQueue.main.async( execute: {
//let dbstrPath : String! = "Firebase Db path"
let ref = device.child("USER").child(csGolbal.g_User!.email!.replacingOccurrences(of: ".", with: "_"))
ref.observeSingleEvent(of: .value, with: { (snapshot) in
if !snapshot.exists()
{
print("snapshot not exists")
}
else
{
for item in snapshot.children
{
let number = item as! FIRDataSnapshot
var aDictLocal : [String : String] = number.value! as! [String : String]
aDictLocal.updateValue(number.key, forKey: "key")
print("value \(number.value!) And Key \(number.key)") // Here you got data
}
}
self.tableView.reloadData()
})
})
}
and the result feedback twice
Actually you have stored Data in DB in random key so use below func
func getDataFromDB(){
DispatchQueue.main.async( execute: {
let dbstrPath : String! = "Firebase Db path)"
dbRef.child(dbstrPath!).observeSingleEvent(of: .value, with: { (snapshot) in
if !snapshot.exists(){
print("snapshot not exists")
}
else{
self.arrEmail.removeAll() // Add this
for item in snapshot.children {
let number = item as! FIRDataSnapshot
var aDictLocal : [String : String] = number.value! as! [String : String]
aDictLocal.updateValue(number.key, forKey: "key")
self.arrEmail.append(aDictLocal) // add this
print("value \(number.value!) And Key \(number.key)") // Here you got data
}
}
// self.tblContacts.reloadData()
})
})
}
Edit
Create one global array like below in your VC
var arrEmail = [[String : String]]() // Assuming your key and value all string
In the above code work add two lines (I edited and with comment add this)
self.arrEmail.removeAll()
and
self.arrEmail.append(aDictLocal) // Now in arrEmail you have all the values for every random key.
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.