'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 storyboards: enter image description here

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