Displaying Firebase data in a tableview - swift

I am trying to retrieve information stored in a Firebase Database, but my tableview is not displaying the information. I have tried using print functions to see if the data is being retrieved, and it shows that this is the case, but the tableview shows up as blank when I run the simulator.
I am using Xcode 11 and every tutorial that I have looked at is not working for some reason.
Here is my code:
import UIKit
import Firebase
import FirebaseDatabase
import SwiftKeychainWrapper
import FirebaseAuth
class FeedVC: UITableViewController {
var currentUserImageUrl: String!
var posts = [postStruct]()
var selectedPost: Post!
override func viewDidLoad() {
super.viewDidLoad()
getUsersData()
getPosts()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func getUsersData(){
guard let userID = Auth.auth().currentUser?.uid else { return }
Database.database().reference().child("users").child(userID).observeSingleEvent(of: .value) { (snapshot) in
if let postDict = snapshot.value as? [String : AnyObject] {
self.tableView.reloadData()
}
}
}
struct postStruct {
let firstName : String!
let lastName : String!
}
func getPosts() {
let databaseRef = Database.database().reference()
databaseRef.child("profiles").queryOrderedByKey().observeSingleEvent(of: .childAdded, with: {
snapshot in
let firstName = (snapshot.value as? NSDictionary)!["profileForename"] as? String
let lastName = (snapshot.value as? NSDictionary
)!["profileSurname"] as? String
print(firstName)
self.posts.append(postStruct(firstName: firstName, lastName: lastName))
print(self.posts)
DispatchQueue.main.async {
self.tableView.reloadData()
}
})
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return posts.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "PostCell") as? PostCell else { return UITableViewCell() }
let nameLabel = cell.viewWithTag(1) as! UILabel
nameLabel.text = posts[indexPath.row].firstName
return cell
}
}
Any help would be much appreciated!

Update: Since PostCell is created in the storyboard within the table view it's registered and dequeued successfully. So the issue is being narrowed down to the label with tag 1. Try creating an #IBOutlet for the label and use that to set the text of UILabel.
Then in cellForRowAt:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "PostCell") as? PostCell else { return UITableViewCell() }
cell.firstNameLabel.text = posts[indexPath.row].firstName
return cell
}
Previous: You seem to have forgotten to register the PostCell.
override func viewDidLoad() {
super.viewDidLoad()
//...
tableView.register(PostCell.self, forCellReuseIdentifier: "PostCell")
}
Note: If you've created PostCell in Xib use nib registry method.
Update: If you want to register with Nib method use:
tableView.register(UINib(nibName: <#T##String#>, bundle: nil), forCellReuseIdentifier: "PostCell") // provide the xib file name at the placeholder

Related

Firebase tableview not populating, Swift

I have data in my db and can search for an individual record, that's working fine. But when I try to simply populate a tableview with all of the db records its not receiving/displaying any data.
here is my code:
struct drinkStruct {
let pub: String!
let rating: String!
let price: String!
}
override func viewDidLoad() {
super.viewDidLoad()
loadDrinks()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
#IBAction func homeClicked(_ sender: Any) {
homeClicked()
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return posts.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
let label1 = cell.viewWithTag(1) as! UILabel
label1.text = posts[indexPath.row].pub
let label2 = cell.viewWithTag(2) as! UILabel
label2.text = posts[indexPath.row].rating
let label3 = cell.viewWithTag(3) as! UILabel
label3.text = posts[indexPath.row].price
return cell
}
func loadDrinks(){
let databaseRef = Database.database().reference().child("Drinks")
ref = Database.database().reference()
databaseRef.queryOrderedByKey().observe(.childAdded, with: { (snapshot) in
if let valueDictionary = snapshot.value as? [AnyHashable:String]
{
let pub = valueDictionary["pub"]
let rating = valueDictionary["rating"]
let price = valueDictionary["price"]
self.posts.insert(drinkStruct(pub: pub, rating: rating, price: price), at: 0)
}
})
self.tableview.reloadData()
}
And here is my db structure:
Am I doing something blatantly obviously wrong? Or can anyone see what's causing no data to load?
There are no errors/unused variables etc etc.
Thanks in advance!
I think the following should do the job.
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
override func viewDidLoad() {
super.viewDidLoad()
//getting a reference to the node //
databaseRef = Database.database().reference().child("Drinks")
//observing the data changes
databaseRef.observe(DataEventType.value, with: { (snapshot) in
if snapshot.childrenCount > 0 {
// clearing the list //
self.posts.removeAll()
// iterating through all the values //
for drinks in snapshot.children.allObjects as! [DataSnapshot] {
let drinkObject = drinks.value as! [String: AnyObject]
let drinkPub = drinkObject["pub"]
let drinkRating = drinkObject["rating"]
let drinkPrice = drinkObject["price"]
//creating a drinkStruct object with the model //
let drinkModel = drinkStruct(pub: drinkPub as! String?, rating: drinkRating as! String?, price: drinkPrice as! String?)
//appending it to list
self.posts.append(drinkModel)
}
// reloading data //
self.tableView.reloadData()
}
})
}
var posts = [drinkStruct]()
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return posts.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! YourCustomTableViewCell
let drink: drinkStruct
drink = posts[indexPath.row]
cell.label1.text = drink.pub
cell.label2.text = drink.rating
cell.label3.text = drink.price
return cell
}
}
For the newbie that's here in my footsteps, I solved this by doing a lot of things.
You need to create the tableview & cell layout in the storyboard. Then you need a cell class that dictates/assigns what's happening in each cell(imageviews, labels etc) as well as a model class for whatever you're looking up, whatever the object may be.
This is the code I used for my function in which I populate the info in the cells with the data from Firebase:
func loadDrinks(){
Database.database().reference().child("Drinks").observe(.childAdded) { (snapshot: DataSnapshot) in
if let dict = snapshot.value as? [String: Any] {
let pub = dict["pub"] as! String
let rating = dict["rating"] as! String
let price = dict["price"] as! String
let drink = Drink(pub: pub.capitalized, rating: rating.capitalized, price: price.capitalized)
self.drinks.append(drink)
print(self.drinks)
self.tableview.reloadData()
}
}
}
This was a Newbie 101 question - my bad.

TableViewCell.xib doesn't appear on tableview

Here is my code, it is supposed to display the users inside firebase database in customs tableviewcell.xib but when launching the app the tableview stays empty, I really don't know what's wrong in the code or what's missing, I think it is a really simple mistake but I can't figure it out.
Thanks in advance for those who will answer.
import UIKit
import Firebase
class UsersTableView: UIViewController, UITableViewDelegate, UITableViewDataSource {
// Outlets.
#IBOutlet weak var tableview: UITableView!
// Var.
var user = [User]()
override func viewDidLoad() {
super.viewDidLoad()
retrieveUsers()
// Do any additional setup after loading the view.
}
func retrieveUsers() {
let ref = Database.database().reference()
ref.child("users").queryOrderedByKey().observeSingleEvent(of: .value, with: { DataSnapshot in
let users = DataSnapshot.value as! [String: AnyObject]
self.user.removeAll()
for (_, value) in users{
//let uid = Auth.auth().currentUser!.uid
if let uid = value["userID"] as? String{
if uid != Auth.auth().currentUser!.uid {
let userToShow = User()
if let fullName = value["username"] as? String , let imagePath = value["photoURL"] as? String {
userToShow.username = fullName
userToShow.imagePath = imagePath
userToShow.userID = uid
self.user.append(userToShow)
}
}
}
}
self.tableview.reloadData()
})
ref.removeAllObservers()
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
let cell = tableview.dequeueReusableCell(withIdentifier: "UserCell", for: indexPath) as! UserTableViewCell
cell.nameLabel.text = self.user[indexPath.row].username
cell.userID = self.user[indexPath.row].userID
cell.userImage.downloadImage(from: self.user[indexPath.row].imagePath!)
//checkFollowing(indexPath: indexPath)
return cell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return user.count ?? 0
}
}
extension UIImageView{
func downloadImage(from imgURL: String!) {
let url = URLRequest(url: URL(string: imgURL)!)
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
if error != nil{
print(error)
return
}
DispatchQueue.main.async {
self.image = UIImage(data: data!)
}
}
task.resume()
}
}
If you have not set delegate and datasource for your tableView in your storyboard do it programmatically:
override func viewDidLoad() {
super.viewDidLoad()
//Add these 2 lines, might be missed.
self.tableview.delegate = self
self.tableview.dataSource = self
retrieveUsers()
}
CheckList:
Set tableView Delegate and Datasource
self.tableview.delegate = self
self.tableview.dataSource = self
Registered custom tableViewCell?
let cellNIb = UINib.init(nibName:"Identifier_Name", bundle: nil)
register(cellNIb, forCellReuseIdentifier: identifier)
check the return count of func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
​
There are two things that you forgot:
Set dataSource of your table view as your controller. If you don't do it, your tableView won't want any data. So set it in controller's viewDidLoad
tableview.dataSource = self
If you created custom xib for your cell, don't forget to register your custom cell for table view (also you can do it in viewDidLoad)
tableview.register(UINib(nibName: "UserCardTableViewCell", bundle: nil), forCellReuseIdentifier: "UserCell")
If you need UITableViewDelegate methods like didSelectRowAt too, don't forget to set delegate of your table view as your controller too
tableview.delegate = self

Xcode tableview not displaying firebase objects

I am currently trying to display firebase objects in my tableview. However, I am only getting empty prototype cells. Thanks if you can take a glance at it!
import UIKit
import Firebase
class UsersTableViewController: UITableViewController {
var user = [User]()
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func getusers() {
let userID = Auth.auth().currentUser?.uid
let rootRef = Database.database().reference()
let query = rootRef.child("users").queryOrdered(byChild: "fullname")
query.observe(.value) { (snapshot) in
for child in snapshot.children.allObjects as! [DataSnapshot] {
if let value = child.value as? NSDictionary {
let userToShow = User()
let fullname = value["fullname"] as? String ?? "Name not found"
let uid = value["uid"] as? String ?? "uid not found"
userToShow.fullname = fullname
userToShow.userID = uid
self.user.append(userToShow)
DispatchQueue.main.async { self.tableView.reloadData() }
}
}
}
}
override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return user.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "userCell", for: indexPath) as! TableViewCell
cell.nameLabel.text = self.user[indexPath.row].fullname
cell.userID = self.user[indexPath.row].userID
cell.userImage.downloadImage(from: self.user[indexPath.row].imagePath!)
return cell
}
}
extension UIImageView {
#objc func downloadImage(from imgURL: String!) {
let url = URLRequest(url: URL(string: imgURL)!)
let task = URLSession.shared.dataTask(with: url) {
(data, response, error) in
if error != nil {
print(error!)
return
}
DispatchQueue.main.async {
self.image = UIImage(data: data!)
}
}
task.resume()
}}
Thanks again for anyone willing to help! I am currently using Swift 4 with Google's Firebase API.
p.s. I know that I have connected delegate and dataSource. I am also sure that I have properly titled my Identifier. Thanks!

tableview not displaying firebase users

I am having a problem with my tableview not showing prototype cells. I have properly named each cell and Identifier. Here is my current code: (Thanks!)
import UIKit
import Firebase
import FirebaseDatabase
class UsersTableViewController: UITableViewController {
#IBOutlet var tableview: UITableView!
var ref: DatabaseReference!
var user = [User]()
override func viewDidLoad() {
super.viewDidLoad()
getusers()
// Do any additional setup after loading the view.
}
func getusers() {
let ref = Database.database().reference()
ref.child("users").child(Auth.auth().currentUser!.uid).queryOrderedByKey().observeSingleEvent(of: .value, with: { (snapshot) in
let users = snapshot.value as? [String : AnyObject] ?? [:]
for (_, value) in users
{
if let uid = users["uid"] as? String
{
if uid != Auth.auth().currentUser!.uid
{
let showUser = User()
if let fullname = users["fullname"] as? String, let imagePath = users["urlImage"] as? String
{
showUser.fullname = fullname
showUser.imagePath = imagePath
showUser.userID = uid
self.user.append(showUser)
}
}
}
}
self.tableview.reloadData()
})
ref.removeAllObservers()
}
override func numberOfSections(in tableview: UITableView) -> Int {
return 1
}
override func tableView(_ tableview: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! UserCell
cell.nameLabel.text = self.user[indexPath.row].fullname
cell.UserID = self.user[indexPath.row].userID
cell.userImage.downloadImage(from: self.user[indexPath.row].imagePath!)
checkFollowing(indexPath: indexPath)
return cell
}
override func tableView(_ tableview: UITableView, numberOfRowsInSection section: Int) -> Int {
return user.count
}
override func tableView(_ tableview: UITableView, didSelectRowAt indexPath: IndexPath) {
let uid = Auth.auth().currentUser!.uid
let ref = Database.database().reference()
let key = ref.child("users").childByAutoId().key
var isFollower = false
ref.child("users").child(uid).child("following").queryOrderedByKey().observeSingleEvent(of: .value, with: { snapshot in
if let following = snapshot.value as? [String : AnyObject] {
for (ke, value) in following {
if value as! String == self.user[indexPath.row].userID {
isFollower = true
ref.child("users").child(uid).child("following/\(ke)").removeValue()
ref.child("users").child(self.user[indexPath.row].userID).child("followers/\(ke)").removeValue()
self.tableview.cellForRow(at: indexPath)?.accessoryType = .none
}
}
}
if !isFollower {
let following = ["following/\(key)" : self.user[indexPath.row].userID]
let followers = ["followers/\(key)" : uid]
ref.child("users").child(uid).updateChildValues(following)
ref.child("users").child(self.user[indexPath.row].userID).updateChildValues(followers)
self.tableview.cellForRow(at: indexPath)?.accessoryType = .checkmark
}
})
ref.removeAllObservers()
}
func checkFollowing(indexPath: IndexPath) {
let uid = Auth.auth().currentUser!.uid
let ref = Database.database().reference()
ref.child("users").child(uid).child("following").queryOrderedByKey().observeSingleEvent(of: .value, with: { snapshot in
if let following = snapshot.value as? [String : AnyObject] {
for (_, value) in following {
if value as! String == self.user[indexPath.row].userID {
self.tableview.cellForRow(at: indexPath)?.accessoryType = .checkmark
}
}
}
})
ref.removeAllObservers()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
}
I am currently using Google's Firebase API for user storage. In this code I am trying to fetch users to my tableview, only my table view is not displaying any information. Thank you in advance if you are able to help!
Remove ref.removeAllObservers() line. Method observeSingleEvent removes observers automatically after completion execution. So in your case ref.removeAllObservers() can remove them before completion invokes and therefore any code in completion doesn't execute.

Getting data from the selected cell (TableView)

I have a TableView with several cells of data and there are 3 labels in each cell.
How can I save all 3 label.text into another variable with indexPath
let indexPath = self.tableView.indexPathForSelectedRow
Here is the full code
I've actually asked in another post that the variable "limit" becomes null after the .observe thing.
So I'm thinking if I can get the data directly from the cell.
import UIKit
import Firebase
import FirebaseDatabase
struct limitStruct{
var name : String!
var today : String!
var limit : String!
}
class CalcViewController: UITableViewController {
var limits = [limitStruct]()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
navigationController?.navigationBar.barTintColor = UIColor.black
self.title = "Calculation"
navigationController!.navigationBar.titleTextAttributes =
[NSForegroundColorAttributeName: UIColor.white]
let databaseReference = FIRDatabase.database().reference()
databaseReference.child("Limit").queryOrderedByKey().observe(.childAdded, with: {
snapshot in
var snapshotValue = snapshot.value as? NSDictionary
let name = snapshotValue!["name"] as? String
snapshotValue = snapshot.value as? NSDictionary
let today = snapshotValue!["today"] as? String
snapshotValue = snapshot.value as? NSDictionary
let limit = snapshotValue!["limit"] as? String
snapshotValue = snapshot.value as? NSDictionary
self.limits.insert(limitStruct(name:name, today:today, limit: limit), at: self.limits.count)
self.tableView.reloadData()
})
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return limits.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Limit")
let label1 = cell?.viewWithTag(1) as! UILabel
label1.text = limits[indexPath.row].name
let label2 = cell?.viewWithTag(2) as! UILabel
label2.text = limits[indexPath.row].today
let label3 = cell?.viewWithTag(3) as! UILabel
label3.text = limits[indexPath.row].limit
return cell!
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showDetails"{
let svc = segue.destination as! CirSliderViewController;
if let indexPath = self.tableView.indexPathForSelectedRow{
// svc.RsegueData =
}
}
}
}
You really don't want to be using viewWithTag(). The best way to handle this is to subclass UITableViewCell, with a property for your data model object
class LimitCell: UITableViewCell {
var limit: Limit {
didSet {
// configureCell()
}
}
}
Then in your view controller:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Limit", forIndex: indexPath) as! LimitCell
cell.limit = limits[indexPath.row]
return cell
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let svc = segue.destination as? CirSliderViewController, cell = sender as? LimitCell {
svc.RsegueData = cell.limit
}
}
It seems that you are using a callback to get the data. Does the data come from a server or is stored locally?
1) If the data comes from a server, you code cannot guarantee that var limits already got the data when the func prepare is called.
2) If the data is stored locally, and ONLY limitis nil, you must check whether or not you correctly assign limits[indexPath.row].limit to limits to the cell.(Is it nil at this moment?) I think the problem is in func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) where limit is saved.
By the way, the more practical and efficient way to implement func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) is that:
Lets say the your custom cell is call LimitCell and has three UILabels: var label1, var label2, var label3.
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Limit") as! LimitCell
cell.label1.text = limits[indexPath.row].name
cell.label2.text = limits[indexPath.row].today
cell.label3.text = limits[indexPath.row].limit
return cell
}