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
Related
I know the issue is regarding scope; I just dont know if theres an easy fix I can do without changing my code much. but open to anything
import UIKit
import Firebase
import FirebaseAuth
import FirebaseFirestore
class AdminViewController: UIViewController, UITableViewDelegate,
UITableViewDataSource {
#IBOutlet var custodianRunReportsTableView: UITableView!
var dbRef: DatabaseReference!
var data = [String]()
override func viewDidLoad() {
super.viewDidLoad()
startObservingDB()
custodianRunReportsTableView.delegate = self
custodianRunReportsTableView.dataSource = self
// Do any additional setup after loading the view.
}
// Gets users' names from Cloud Firestore Database
func startObservingDB() {
let db = Firestore.firestore()
let namesDocumentRef = db.collection("Users").document("Names")
namesDocumentRef.addSnapshotListener { DocumentSnapshot, error in
guard let document = DocumentSnapshot else {
print("Error fetching document: \(error!)")
return
}
guard let data = document.data() else {
print("Document data was empty.")
return
}
let values = data.values
let rowNumber = data.count
print("Current data: \(data)")
print("Current data has the values: \(values)")
print("Current data totals \(data.count) items.")
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("You tapped me!")
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return rowNumber
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = custodianRunReportsTableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = values[indexPath.row]
print("Names in cell: \(values)")
print("\(data)")
return cell
}
}
UPDATED CODE:
This is updated after an answer to the original post. The code no longer has the unresolved identified error; however, the table view does not display any cell text and is empty.
class AdminViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet var custodianRunReportsTableView: UITableView!
var valuesArray:[String] = []
var data:[String] = []
var namesDocumentRef:DocumentReference!
override func viewDidLoad() {
super.viewDidLoad()
startObservingDB()
custodianRunReportsTableView.delegate = self
custodianRunReportsTableView.dataSource = self
}
// Gets users' names from Cloud Firestore Database
func startObservingDB() {
var namesDocumentRef:DocumentReference!
let db = Firestore.firestore()
namesDocumentRef = db.collection("Users").document("Names")
namesDocumentRef.addSnapshotListener { DocumentSnapshot, error in
if error != nil{
return
}
else {
guard let snapshot = DocumentSnapshot, snapshot.exists else {return}
guard let data = snapshot.data() else { return }
self.valuesArray = Array(data.values) as! Array<String>
}
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("You tapped me!")
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return valuesArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = custodianRunReportsTableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = valuesArray[indexPath.row]
return cell
}
}
The variables data,values and rowCount are inside closure so you can't just write values[indexPath.row] because you can't return from inside closure. Usually completionHandlers are used for this purpose, but in this scenario, you should put the values inside an array and then use inside the tableview. Let me show you how to do.
At the start of your viewController, declare a string array.
var valuesArray:[String] = []
Then, inside modify your startObservingDB() function
func startObservingDB() {
var docRef:DocumentReference!
let db = Firestore.firestore()
docRef = db.collection("Users").document("Names")
docRef.addSnapshotListener { (docSnapshot, error) in
if error != nil {
return
}
else {
guard let snapshot = docSnapshot, snapshot.exists else {return}
guard let data = snapshot.data() else { return }
self.valuesArray = Array(data.values) as! Array<String>
self.tableView.reloadData()
}
}
}
In viewDidLoad call this function
startObservingDB()
Then in tableView methods
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return ValuesArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = custodianRunReportsTableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = valuesArray[indexPath.row]
return cell
}
In your class AdminViewController you can reference properties defined outside of methods with self (in your example you have dbRef that you can reference like self.dbRef in methods).
So I suggest you make properties for data, value and rowNumber and change them in startObservingDB method rather than declare them. This way you will be able to reference them in tableView methods.
I am trying to show User ID's in my app, however the UID's are very random and next to each one is a name, is it possible to show both the name of the nodes as well as its value?
My firebase database
I want to display the Name "Andy" as well as the UID "uLUnOelxABYl3lCtLz2Of5Yfnvc2"
I have set it up so that the App receives the values from my Firebase.
Here is my code:
class TestViewController: UIViewController, UITableViewDelegate, UITableViewDataSource{
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return list.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell")
cell?.textLabel?.text = list[indexPath.row]
return cell!
}
#IBOutlet weak var tableView: UITableView!
var ref: DatabaseReference!
var list = [String] ()
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
if FirebaseApp.app() == nil {
FirebaseApp.configure() }
ref = Database.database().reference()
ref?.child("users").observe(.childAdded, with: { (snapshot) in
let list1 = snapshot.value as? String
if let actualList = list1 {
self.list.append(actualList)
self.tableView.reloadData()
}
}
)
}
}
Here is what I get back. However I want to show is a list of the names with these values next to it, like this (photoshop)
results
PHOTOSHOP RESUTLS
Regarding to documentation FIRDataSnapshot has property key, so I think you can use it for matching with values, e.g:
/// e.g. use dictionary instead of Array
//var list = [String] ()
var list = [AnyHashable: String]()
...
ref?.child("users").observe(.childAdded, with: { (snapshot) in
let key = snapshot.key as? Hashable
let value = snapshot.value as? String
if let actualValue = value, let actualKey = key {
self.list[key] = actualValue
self.tableView.reloadData()
}
}
I'm trying to delete the data from firebase with no luck so far. This is the code I'm using, can anyone give me a hand with it please.
class TableViewController: UITableViewController {
var ref: FIRDatabaseReference?
var grocery = [Grocery]()
override func viewDidLoad() {
super.viewDidLoad()
loadData()
}
func loadData() {
let uid = FIRAuth.auth()?.currentUser?.uid
FIRDatabase.database().reference().child("Users").child(uid!).child("Grocery").observe(.childAdded) { (snspshot: FIRDataSnapshot) in
if let dict = snspshot.value as? [String: Any] {
let Items = dict["Item"] as! String
let Quintities = dict["Quintities"] as! String
let Done = dict["Done"] as! Bool
let themBe = Grocery(Items: Items, Quintitiess: Quintities, Dones: Done)
self.grocery.append(themBe)
print(themBe)
self.tableView.reloadData()
}
}
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return grocery.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "TasksTableViewCell") as! TasksTableViewCell
cell.titleLabel?.text = grocery[indexPath.row].Item
cell.numLabel?.text = grocery[indexPath.row].Quintities
return cell
}
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
self.grocery.remove(at: indexPath.row)
self.tableView.deleteRows(at: [indexPath], with: .automatic)
}
}
----------
import Foundation
class Grocery {
var Item: String
var Quintities: String
var Done: Bool
init(Items: String, Quintitiess: String, Dones: Bool) {
Item = Items
Quintities = Quintitiess
Done = Dones
}
}
You are only deleting data for your UITableView. The logic that you need is to delete from your UITableView and Fireabase Database. As the firebase docs says you can either call removeValue, or setValue to nil or updateChildValues.
To make the deletion easier, I'd save the key of the object where the data is saved (snapshot.keys), so when you want to delete you can just get that key and perform actions.
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
How would I fill the TableView Cell using Firebase? I'm trying to fill it using an array which would be filled by Firebase, but since the data is filled asynchronously, I don't know how to make the array filled with data outside of the getData() function. As of now the data is filled inside the completion handler, but outside of that it is nil.
import UIKit
import Firebase
class ProxiesListViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var ProxiesTableView: UITableView!
var proxyname:[String] = []
var proxyprice:[Int] = []
var proxycountry:[String] = []
var databaseRef : FIRDatabaseReference!
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return proxyname.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! ProxiesTableViewCell
cell.countryFlag.image = UIImage(named: proxycountry[indexPath.row])
cell.NameLabel.text = proxyname[indexPath.row]
cell.priceLabel.text = "$" + String(proxyprice[indexPath.row])
return(cell)
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
//add code
}
override func viewDidLoad() {
super.viewDidLoad()
ProxiesTableView.delegate = self
ProxiesTableView.dataSource = self
getData()
}
func getData() {
databaseRef = FIRDatabase.database().reference()
/*
proxyname = ["50 Proxies (24 Hours)", "100 Proxies", "10 Proxies", "60 Proxies (48 Hours)"]
proxyprice = [600, 140, 60, 10]
proxycountry = ["CA", "UK", "UK", "US"]
*/
self.databaseRef.child("ProxiesItemsFeed").observe(.childAdded, with: { (snapshot) in
if let proxynames = snapshot.key as String! {
self.proxyname.append(proxynames)
self.databaseRef.child("ProxiesItemsFeed").child(proxynames).observe(.childAdded, with: { (snapshot) in
if let country = snapshot.key as String! {
self.proxycountry.append(country)
}
if let price = snapshot.value as! Int! {
self.proxyprice.append(price)
}
})
}
})
}
}
You should add a tableView.reloadData() inside your completion handler (i.e., the one in your getData() method).
Method documentation:
Call this method to reload all the data that is used to construct the table, including cells, section headers and footers, index arrays, and so on.