0

I am currently working on a project with Xcode (User Interface: Storyboard) and Google Firebase. I cannot get my app to display the information like this: Desired output

The way I got this picture was by starting a test project selecting SwiftUI as my User Interface, and only having ONE single view controller. In my app a user will only arrive to this page after they have logged in and click a button that takes them to the table view.

My app currently prints the result at the bottom of Xcode: Current output

import UIKit
import FirebaseFirestore

class AssetViewController: UIViewController {

    @IBOutlet weak var assetList: UITableView!
        override func viewDidLoad() {
            super.viewDidLoad()

    // Do any additional setup after loading the view.
    let db = Firestore.firestore()
                                
    db.collection("assets").getDocuments { (snapshot, error) in
        if error != nil {
            print("error")
        } else {
    for document in (snapshot?.documents)! {
        if let brand = document.data() ["brand"] as? String {
        if let description = document.data() ["description"] as? String {
        if let date = document.data() ["date"] as? String {
        if let cost = document.data() ["cost"] as? String {
        if let serial = document.data() ["serial"] as? String {
        if let warranty = document.data() ["warranty"] as? String {
        if let warranty_cost = document.data() ["warranty_cost"] as? String {
                                            
        print("\(document.documentID) => \(document.data())") }
        }
            }
                }
            }
        }
            }
                }
            }
        }
            }
}

I have the following class:

import Foundation
import FirebaseFirestore

class AssetsViewModel: ObservableObject {
    @Published var assets = [Asset] ()
    
    private var db = Firestore.firestore()
    
    func fetchData() {
        db.collection("assets").addSnapshotListener { (QuerySnapshot, error) in
            guard let documents = QuerySnapshot?.documents else {
                print("No assets in here")
                return
            }
            self.assets = documents.map { (QueryDocumentSnapshot) -> Asset in
                let data = QueryDocumentSnapshot.data()
                
                let brand = data["brand"] as? String ?? ""
                let description = data["description"] as? String ?? ""
                let date = data["date"] as? String ?? ""
                let cost = data["cost"] as? String ?? ""
                let serial = data["serial"] as? String ?? ""
                let warranty = data["warranty"] as? String ?? ""
                let warranty_cost = data["warranty_cost"] as? String ?? ""
                
                return Asset(brand: brand, description: description, date: date, cost: cost, serial: serial, warranty: warranty, warranty_cost: warranty_cost)
            }
            
        }
    }
    
}

And I have the following structure:

import Foundation

struct Asset: Identifiable {
    var id: String = UUID().uuidString
    var brand: String
    var description: String
    var date: String
    var cost: String
    var serial: String
    var warranty: String
    var warranty_cost: String
}

My main goal is to display the information like the first picture. I would appreciate any help given.

This is the code that I used to display the first picture:

import SwiftUI

struct ContentView: View {
    
    @ObservedObject private var viewModel = AssetsViewModel()
    
    var body: some View {
        NavigationView {
            List(viewModel.assets) { asset in
                VStack(alignment: .leading) {
                    Text(asset.brand)
                        .font(.headline)
                    Text(asset.description)
                        .font(.subheadline)
                    Text(asset.date)
                        .font(.subheadline)
                    Text(asset.cost)
                    .font(.subheadline)
                    Text(asset.serial)
                    .font(.subheadline)
                    Text(asset.warranty)
                    .font(.subheadline)
                    Text(asset.warranty_cost)
                    .font(.subheadline)
                }
            }
        .navigationBarTitle("Assets")
            .onAppear() {
            self.viewModel.fetchData()
            }
        }
    }

}

struct AssetViewSwiftUIView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

The post below helped me. The error I was getting was because Swift did not like me naming the ViewTable "assetList". Once I changed the name to "tableView" and changed in the code, it worked well. I added extra code to make the cells auto adjust from here: why UITableViewAutomaticDimension not working?.

Thank you very much for your help!

Martijn Pieters
  • 889,049
  • 245
  • 3,507
  • 2,997
Sarapepe
  • 13
  • 5

1 Answers1

1

You need something like this:

import Foundation
import UIKit
import FirebaseFirestore

class myTableCell: UITableViewCell {
    @IBOutlet weak var brand: UILabel!
    @IBOutlet weak var description: UILabel!
    @IBOutlet weak var date: UILabel!
    @IBOutlet weak var cost: UILabel!
    @IBOutlet weak var serial: UILabel!
    @IBOutlet weak var warranty: UILabel!
    @IBOutlet weak var warrantyCost: UILabel!
}

class IndexAssets {
    var brand = ""
    var description = ""
    var date = ""
    var cost = ""
    var serial = ""
    var warranty = ""
    var warrantyCost = ""
    
    init(brand: String, description: String, date: String, cost: String, serial: String, warranty: String, warrantyCost: String)
    {
        self.brand = brand
        self.description = description
        self.date = date
        self.cost = cost
        self.serial = serial
        self.warranty = warranty
        self.warrantyCost = warrantyCost
    }
}

class AssetViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

    @IBOutlet weak var assetList: UITableView!
    
    var dataArray: [IndexAssets] = [IndexAssets]()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        downloadData()
    }
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return dataArray.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
    {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! myTableCells
        
        cell.brand?.text = dataArray[indexPath.row].brand
        cell.description?.text = dataArray[indexPath.row].description
        cell.date?.text = dataArray[indexPath.row].date
        cell.cost?.text = dataArray[indexPath.row].cost
        cell.serial?.text = dataArray[indexPath.row].serial
        cell.warranty?.text = dataArray[indexPath.row].warranty
        cell.warrantyCost?.text = dataArray[indexPath.row].warrantyCost
        
        return cell
        
    }
    
    func downloadData()
    {

        let db = Firestore.firestore()
                                
        db.collection("assets").getDocuments { (snapshot, error) in
        if error != nil
        {
            print("error")
        }
        else
        {
            for document in (snapshot?.documents)!
            {
                let brand = document.data() ["brand"] as? String
                let description = document.data() ["description"] as? String
                let date = document.data() ["date"] as? String
                let cost = document.data() ["cost"] as? String
                let serial = document.data() ["serial"] as? String
                let warranty = document.data() ["warranty"] as? String
                let warrantyCost = document.data() ["warranty_cost"] as? String
                
                if let brand = brand, let description = description, let date = date, let cost = cost, let serial = serial, let warranty = warranty, let warrantyCost = warrantyCost
                {
                    let insert = IndexAssets(brand: brand, description: description, date: date, cost: cost, serial: serial, warranty: warranty, warrantyCost: warrantyCost)
                    self.dataArray.append(insert)
                }
            }
            self.assetList.reloadData()
        }}
    }

            
}

Also, remember in Storyboard to:

  • Assign "cell" to your Dynamic Cell identifier
  • Select myTableCell as Class for that cell
  • Drag labels from the class to connect them to storyboard
  • Drag your table to your View Controller (Yellow Icon on top) and select DataSource and Delegate.
Paul
  • 105
  • 7
  • Thank you. I could not get the example to work if I was not using the default view controller so I used this one (https://stackoverflow.com/questions/33234180/uitableview-example-for-swift/33234181#33234181) I see what you mean that I did not have any table functions. The sample show a static array. Will my class help when trying to show the information from Firestore? How would I do this? – Sarapepe Nov 01 '20 at 09:28
  • 1
    Have a look at my example – Paul Nov 01 '20 at 11:02
  • Hello I followed all your steps (thank you very much). Now I am getting the following error when trying to look at the table : Thread 1: Exception: "[ setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key tableView." This shows on my AppDelegate file. Any ideas what I did wrong?Also the when linking the "description" label it gives 3 errors: Cannot override 'strong' property with 'weak' property Property 'description' with type 'UILabel?' cannot override a property with type 'String' – Sarapepe Nov 01 '20 at 18:02