'The data from Firestore database is not displayed in the tableview
I'm using Swift and Firestore database to implement an app like Twitter.
I want to add sweet (it's like tweet) when button is clicked to the database. And then display it in the tableview.
The data is added to the database. But is not displayed in the tableview. So when I run an app I see empty tableview.
Please help!!
TableViewController file:
import UIKit
import FirebaseFirestore
import Firebase
class TableViewController: UITableViewController {
var db:Firestore!
var sweetArray = [Sweet]()
override func viewDidLoad() {
super.viewDidLoad()
db = Firestore.firestore()
loadData()
}
func loadData() {
db.collection("sweets").getDocuments() {
querySnapshot, error in
if let error = error {
print("Error loading documents to the db: \(error.localizedDescription)")
} else {
self.sweetArray = querySnapshot!.documents.flatMap({Sweet(dictionary: $0.data())})
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
}
@IBAction func composeSweet(_ sender: Any) {
let composeAlert = UIAlertController(title: "New Sweet", message: "Enter your name and message", preferredStyle: .alert)
composeAlert.addTextField { (textField:UITextField) in
textField.placeholder = "Your name"
}
composeAlert.addTextField { (textField:UITextField) in
textField.placeholder = "Your message"
}
composeAlert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
composeAlert.addAction(UIAlertAction(title: "Send", style: .default, handler: { (action:UIAlertAction) in
if let name = composeAlert.textFields?.first?.text, let content = composeAlert.textFields?.last?.text {
let newSweet = Sweet(name: name, content: content, timeStamp: Date())
var ref:DocumentReference? = nil
ref = self.db.collection("sweets").addDocument(data: newSweet.dictionary) {
error in
if let error = error {
print("Error adding document: \(error.localizedDescription)")
} else {
print("Document added with ID: \(ref!.documentID)")
}
}
}
}))
self.present(composeAlert, animated: true, completion: nil)
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return sweetArray.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
let sweet = sweetArray[indexPath.row]
cell.textLabel?.text = "\(sweet.name) : \(sweet.content)"
cell.detailTextLabel?.text = "\(sweet.timeStamp)"
return cell
}
}
Sweet file:
import Foundation
import FirebaseFirestore
protocol DocumentSerializable {
init?(dictionary:[String:Any])
}
struct Sweet {
var name: String
var content: String
var timeStamp: Date
var dictionary:[String:Any] {
return [
"name": name,
"content": content,
"timeStamp": timeStamp
]
}
}
extension Sweet:DocumentSerializable {
init?(dictionary: [String : Any]) {
guard let name = dictionary["name"] as? String,
let content = dictionary["content"] as? String,
let timeStamp = dictionary["timeStamp"] as? Date else {return nil}
self.init(name: name, content: content, timeStamp: timeStamp)
}
}
My running app:
Solution 1:[1]
Seems you have data in querySnapshot
but empty in sweetArray
which means only one this your are parsing and mapping the data received into structs
incorrectly. Modify this line to fix your issue:
self.sweetArray = querySnapshot!.documents.flatMap({Sweet(dictionary: $0.data())})
Solution 2:[2]
I can't provide a specific answer but I can explain how to find what the issue is.
While adding guard statements to protect your code is awesome, it can also lead to issues not being handled appropriately.
Take this piece of code from your question for example
guard let name = dictionary["name"] as? String,
let content = dictionary["content"] as? String,
let timeStamp = dictionary["timeStamp"] as? Date else {return nil}
As you can see if there's some issue with name, content or timestamp, the guard will catch it - however, returning nil means it silently fails with no indication of the problem.
Suppose for example, that a field name was accidentally called Name instead of name - well, that's going to fail but you'd never know it.
I suggest handling fields separately to catch specific problems. Like this
let name = dictionary["name"] as? String ?? "name field not found"
let name = dictionary["content"] as? String ?? "content field not found"
let name = dictionary["timesStamp"] as? Date ?? "timestamp field not found"
This is called nil coalescing and will substitute a default value in case of nil. By then examining the incoming data, you can find the document that caused the issue. You could also do this
guard let name = dictionary["name"] as? String else { //handle the error }
in either case, you then have more data about the nature of the failure.
Solution 3:[3]
private var refernceCollection: CollectionReference!
database = Firestore.firestore() refernceCollection = Firestore.firestore().collection(kMessages)
func fetchData() {
refernceCollection.addSnapshotListener{ snapshots, error in
if error != nil {
print("error --->>")
} else {
guard let snap = snapshots else { return }
var arrUser:[MDLMessages] = []
for documet in snap.documents {
let data = documet.data()
let message = data["message"] as? String ?? "This message was deleted"
let time = data["time"] as? Date ?? Date.now
let documentId = documet.documentID
let userId = data["userId"] as? String ?? ""
let details = MDLMessages(message: message, time: time, documentId: documentId, userId: userId)
arrUser.append(details)
}
DispatchQueue.main.async {
self.arrMessages = arrUser
self.tblChatDetails.reloadData()
}
}
}
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Frankenstein |
Solution 2 | Jay |
Solution 3 | user19015762 |