Swift Firebase retrieve data into tableview - swift

My database tree
Hello,
I am trying to retrieve data to to tableview but although I can read data from firebase database, I cannot display them in table view. My code is below, I hope you can help me.
class Calls {
var callType: String?
var callHospital: String?
init(callType: String?, callHospital: String?) {
self.callType = callType
self.callHospital = callHospital
}
}
class myCallsViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var ref:DatabaseReference!
var myCallList = [Calls]()
#IBOutlet weak var callListTableView: UITableView!
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return myCallList.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "customcell", for: indexPath) as! myCallsViewControllerTableViewCell
let test = myCallList[indexPath.row]
cell.callType?.text = test.callType
cell.callHospital?.text = test.callHospital
return cell
}
override func viewDidLoad() {
super.viewDidLoad()
callListTableView.dataSource = self
callListTableView.delegate = self
LoadCalls()
}
func LoadCalls() {
ref = Database.database().reference()
let userID = Auth.auth().currentUser?.uid
ref.child("calls").queryOrdered(byChild: "userID").queryEqual(toValue: userID!).observe(.childAdded, with: { (snapshot) in
if snapshot.childrenCount > 0{
self.myCallList.removeAll()
for result in snapshot.children.allObjects as! [DataSnapshot]{
let results = result.value as? [String : AnyObject]
let type = results?["calltype"]
let hospital = results?["hospital"]
let myCalls = Calls(callType: type as! String?, callHospital: hospital as! String?)
self.myCallList.append(myCalls)
}
self.callListTableView.reloadData()
}
})
}

I solved the problem, thank you guys,Blake and Siyavash, so much. I registered the cell and put dispatch main queue and it worked. Here is the latest code:
class Calls {
var callType: String?
var callHospital: String?
init(callType: String?, callHospital: String?) {
self.callType = callType
self.callHospital = callHospital
}
}
class myCallsViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var ref:DatabaseReference!
var myCallList = [Calls]()
#IBOutlet weak var callListTableView: UITableView!
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return myCallList.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "customcell", for: indexPath) as! myCallsViewControllerTableViewCell
let test = myCallList[indexPath.row]
cell.callType?.text = test.callType
cell.callHospital?.text = test.callHospital
return cell
}
override func viewDidLoad() {
super.viewDidLoad()
callListTableView.dataSource = self
callListTableView.delegate = self
LoadCalls()
}
func LoadCalls() {
ref = Database.database().reference()
let userID = Auth.auth().currentUser?.uid
ref.child("calls").queryOrdered(byChild: "userID").queryEqual(toValue: userID!).observe(.childAdded, with: { (snapshot) in
let results = snapshot.value as? [String : AnyObject]
let type = results?["calltype"]
let hospital = results?["hospital"]
let myCalls = Calls(callType: type as! String?, callHospital: hospital as! String?)
self.myCallList.append(myCalls)
DispatchQueue.main.async {
self.callListTableView.reloadData()
}
})
}

Your issue probably has to do with the fact that you're calling reloadData() from a closure, which means you're updating the UI from a background thread. Check out this answer:
Swift UITableView reloadData in a closure

Related

How to read data from firebase, block with doesn't work?

I try to read data from firebase. I've made observeSingleEvent, but block "with" not works, why?
I try to debug and I notice that block with doesn't work.
userID has correct ID
and reference also correct
var ref: DatabaseReference!
var snapData: NSDictionary?
var nameString = [String]()
override func viewDidLoad() {
super.viewDidLoad()
ref = Database.database().reference()
loadData()
table.delegate = self
table.dataSource = self
table.register(UITableViewCell.self, forCellReuseIdentifier: "indentifire")
view.addSubview(table)
// Do any additional setup after loading the view.
}
// ---------------------------------------------------
//loading data from FireBase
func loadData() {
let userID = Auth.auth().currentUser?.uid
ref.child("users").child(userID!).observeSingleEvent(of: .value, with: { (snapshot) in
self.snapData = snapshot.value as? NSDictionary
})
}
// delegate
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
var temp = 0
for (_,val) in snapData! {
if val as? String == "false" {
temp += 1
nameString.append(val as! String)
}
}
return temp
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = table.dequeueReusableCell(withIdentifier: "indentifire", for: indexPath)
cell.textLabel!.text = nameString[indexPath.row]
return cell
}```
this is my database
![photo](https://imgur.com/a/0UzOPJ7
The database operations work asynchronously. Map the data in loadData and reload the table view
func loadData() {
let userID = Auth.auth().currentUser?.uid
ref.child("users").child(userID!).observeSingleEvent(of: .value, with: { (snapshot) in
if let snapData = snapshot.value as? [String:Any] {
self.nameString = snapData.values.compactMap {$0 as? String}
DispatchQueue.main.async {
self.table.reloadData()
}
}
})
}
And in numberOfRowsInSection just return the number of items in nameString
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return nameString.count
}

How to use multi labels in UITableView?

I get data from JSON (First name, last name and email) but I'm only able to show first name in UITableView. I tried my best but I couldn't make it work. Following is my code.
import UIKit
struct User: Codable {
let firstName: String
let lastName: String
let email: String
enum CodingKeys: String, CodingKey {
case firstName = "first_name"
case lastName = "last_name"
case email = "email"
}
}
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var tableview: UITableView!
private var dataSource = [User]() {
didSet {
self.tableview.reloadData()
}
}
override func viewDidLoad() {
super.viewDidLoad()
self.tableview.register(UITableViewCell.self, forCellReuseIdentifier: "groupCell")
self.tableview.dataSource = self
self.tableview.delegate = self
let url = URL(string: "https://x.com/x.php")
URLSession.shared.dataTask(with: url!, completionHandler: { [weak self] (data, response, error) in
guard let data = data, error == nil else {
print(error?.localizedDescription ?? "An error occurred")
return
}
DispatchQueue.main.async {
self?.dataSource = try! JSONDecoder().decode([User].self, from: data)
}
}).resume()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
tableview.reloadData()
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dataSource.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "groupCell", for: indexPath)
let user = self.dataSource[indexPath.row]
cell.textLabel?.text = user.firstName
// cell.textLabel?.text = user.lastName If I write this line then it only shows last name
return cell
}
}
You can use UITableViewCell.CellStyle.subtitle, like so:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell: UITableViewCell! = tableView.dequeueReusableCell(withIdentifier: "groupCell")
if cell == nil {
cell = UITableViewCell(style: .subtitle, reuseIdentifier: "groupCell")
}
let user = self.dataSource[indexPath.row]
cell.textLabel?.text = user.firstName + " " + user.lastName
cell.detailTextLabel?.text = user.email
return cell
}
You do not need to register cell, so DELETE following line:
tableview.register(UITableViewCell.self, forCellReuseIdentifier: "groupCell")

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.

Display user info from Firebase

I want to read and display user info from Firebase.
Here is how far I've got:
class UserInfoViewController: UIViewController,UITableViewDelegate, UITableViewDataSource {
var ref: DatabaseReference!
private var gotName = [""]
#IBOutlet weak var tableView: UITableView!
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return gotName.count
}
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCell(withIdentifier: "UserInfoCell") as! UserInfoTableViewCell
cell.nameLabel.text = gotName[indexPath.row]
return cell
}
override func viewDidLoad() {
ref = Database.database().reference()
let user = Auth.auth().currentUser!.uid
ref?.child("users").child(user).observeSingleEvent(of: .value, with: { (snapshot) in
guard let userDict = snapshot.value as? [String: Any],
let name = userDict["Name"] as? String else {
return
}
//Declare variables for use
self.gotName = [name]
})
}
}
Firebase structure:
-users
-----5DzurQyzyIbXfFCbAxc4ynwizYJ2
---------John Doe
I want the current user to be displayed in the nameLabel
You have written the code correctly.
but you must reload your table view after downloading the data from firebase.
Just write following code after self.gotName = [name] in your firebase observer.
DispatchQueue.main.async {
self.tableView.reloadData()
}
Hope this helps

Why is JSON data from local path shows incorrectly in the UITableView?

I want to parse JSON data from a local file that is available in the project and then populate these data to UITableView.
My Requirements
parse the json data from local path not from URL
Populate the json data to UITableView
Facing problems
Unable to display the parsed data, ( bracket is displaying in the table view.
I am able to print the data in console using dump() but unable to print data in tableView
Updated view controller for passing data to another controller.
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return lookArrayModel.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cells = myTableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
let displayData = lookArrayModel[indexPath.row]
cells.textLabel?.text = String(describing: displayData.Lookname!)
cells.detailTextLabel?.text = String(describing: displayData.Lookdetails!)
// print(displayData.shadeModel)
return cells
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("You selected cell #\(indexPath.row)!")
// Get Cell Label
let indexPath = myTableView.indexPathForSelectedRow;
let currentCell = myTableView.cellForRow(at: indexPath!) as UITableViewCell!;
lookNameValue = currentCell?.textLabel?.text
lookDetailValue = currentCell?.detailTextLabel?.text
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
//let lookShade = LookModelData()
if (segue.identifier == "segueToLook") {
let destController:DetailsViewController = segue.destination as! DetailsViewController
//Set the selecte row index value
destController.LabelText = String(describing: lookNameValue)
destController.DetailText = String(describing: lookDetailValue)
// destController.arrayData = lookShade.shadeModel as! NSMutableArray
}
}
}
Destination view controller. Swift
class DetailsViewController:UIViewController,UITableViewDataSource,UITableViewDelegate {
var lookArrayModel = [LookModelData]()
var arrayData: NSMutableArray = []
#IBOutlet weak var secondView: UITableView!
var LabelText = String()
var DetailText = String()
var shadeText = String()
#IBOutlet weak var LookLabel: UILabel!
#IBOutlet weak var LookName: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
print(arrayData)
LookName?.text = LabelText
LookLabel?.text = DetailText
secondView.dataSource = self
secondView.delegate = self
secondView.reloadData()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return arrayData.count
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cells = secondView.dequeueReusableCell(withIdentifier: "secondCell", for: indexPath)
let displayData = arrayData
// cells.textLabel?.text = String(describing: (displayData as AnyObject))
// print(arrayData)
return cells
}
}
Please check my code :
Changed lookArrayModel type NSMutableArray to [LookModelData]. Like those I did some changes. Please check.
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var lookArrayModel = [LookModelData]()
#IBOutlet weak var myTableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
guard let Path = Bundle.main.path(forResource: "ColorShade", ofType: "json") else { return }
let url = URL(fileURLWithPath: Path)
do {
let data = try Data(contentsOf: url)
let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers)
myTableView.dataSource = self
myTableView.delegate = self
//Calling the function for adding look
createLooks(dictionary: json as! NSArray)
myTableView.reloadData()
} catch {
print(error)
}
}
func createLooks(dictionary:NSArray) {
for item in dictionary {
let item1 = item as! NSDictionary
let lookModal = LookModelData()
lookModal.Lookname = item1.value(forKey: "Lookname") as? String
lookModal.LookId = item1.value(forKey: "LookId") as? String
lookModal.Lookdetails = item1.value(forKey: "Lookdetails") as? String
lookModal.shadeModel = createshade(shades: item1.value(forKey: "shades") as! NSArray)
lookArrayModel.append(lookModal)
}
}
func createshade(shades: NSArray) -> [ShadeDescription] {
var arrayShade = [ShadeDescription]()
for item in shades
{
let item1 = item as! NSDictionary
let shadeModal = ShadeDescription()
shadeModal.comboID = item1.value(forKey: "comboID") as? String
shadeModal.shadeName = item1.value(forKey: "shadeName") as? String
shadeModal.ShadeType = item1.value(forKey: "ShadeType") as? String
shadeModal.ShadeCode = item1.value(forKey: "shadeCode") as? String
arrayShade.append(shadeModal)
}
return arrayShade
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return lookArrayModel.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cells = myTableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
let displayData = lookArrayModel[indexPath.row]
// You will get like this
// print(displayData.LookId!)
// print(displayData.Lookname!)
// print(displayData.Lookdetails!)
// print(displayData.shadeModel!)
// This is the way to get shade model data
if let shadeModels = displayData.shadeModel {
for var shadeModel in shadeModels {
print(shadeModel.comboID)
print(shadeModel.ShadeType)
print(shadeModel.shadeName)
print(shadeModel.ShadeCode)
}
}
cells.textLabel?.text = String(describing: displayData.Lookname!)
return cells
}
}
class LookModelData
{
var Lookname:String?
var LookId:String?
var Lookdetails:String?
//Shades Array
var shadeModel : [ShadeDescription]?
}
class ShadeDescription {
var ShadeType:String?
var shadeName:String?
var comboID:String?
var ShadeCode:String?
}