I have a problem retrieving all the data from firebase firestore (which keeps track of some user info, i.e. title, description and imageURL), then downloading the image from firebaseStorage, creating an object called Cella(title: String, description: String, image: UIImage). This process should happen in a loop, which then creates an object for every user, and returns an array of Cella objects to be passed to another ViewController and displayed on its tableView.
I've tried fixing the completion handlers because I thought they were the problem, and then I've added an IBAction which gets triggered when the segue is performed.
ViewController where I try to retrieve the data (notice that it is inside a Navigation View Controller, and the segue is the only one, which gets triggered when that button gets pressed).
class ViewController: UIViewController {
let firestoreUsersReference = Firestore.firestore().collection("users")
let storageReference = Storage.storage()
var cellaObjects : [Cella] = []
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
super.prepare(for: segue, sender: sender)
getDocumentsFromFirestore(firestoreReference: firestoreUsersReference) { (cellaArray) in
self.cellaObjects = cellaArray
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
func getImagesDownloaded(reference: StorageReference, completion: @escaping (UIImage?,Error?)->()) {
reference.getData(maxSize: 10*1024*1024) { (data, error) in
guard error == nil, let data = data else {
completion(nil,error)
return
}
guard let image = UIImage(data: data) else {
completion(nil, FirebaseErrors.expectedImage)
return
}
completion(image,nil)
}
}
enum FirebaseErrors: Error {
case expectedImage
}
func getDocumentsFromFirestore (firestoreReference: CollectionReference, completion: @escaping ([Cella])->()) {
var cellaArray : [Cella] = []
firestoreUsersReference.getDocuments { (querySnapshot, err) in
if err != nil {
print("There has been an error \(String(describing: err?.localizedDescription))")
}
else {
for documents in querySnapshot!.documents {
print("\(documents.documentID) => \(documents.data())")
let data = documents.data()
let title = data["userTitle"] as! String
let description = data["userDescription"] as! String
let imageURL = data["userImageURL"] as! String
print("Title: \(String(describing: title)), Description: \(String(describing: description)), imageURL: \(imageURL)")
self.cellCreationProcess(title: title, description: description, imageURL: imageURL, completion: { (newCell) in
cellaArray.append(newCell)
})
}
completion(cellaArray)
}
}
}
func cellCreationProcess(title: String, description: String, imageURL: String, completion: @escaping (Cella) -> ()) {
let storagePath = Storage.storage().reference(forURL: imageURL)
self.getImagesDownloaded(reference: storagePath, completion: { (image, error) in
guard let image = image, error == nil else {
print(String(describing : error?.localizedDescription))
return
}
print("TITLE: \(String(describing: title)), IMAGE: \(image)")
let newCell = Cella(image: image, title: title, bodyMessage: description)
completion(newCell)
})
}
}
ViewController where the TableView is:
class ViewControllerForTable: UIViewController, UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let currentCell = tableView.cellForRow(at: indexPath) as! TableViewCell
let information = Information(title: currentCell.title.text!, description: currentCell.bodyText.text!, sellerImage: currentCell.CellImage.image!)
let destinationVC = self.storyboard?.instantiateViewController(withIdentifier: "ViewControllerDisplay") as! ViewControllerDisplay
destinationVC.dataPassed = information
self.navigationController?.pushViewController(destinationVC, animated: true)
}
@IBOutlet weak var UITableView: UITableView!
let image : UIImage = UIImage(named: "image1")!
var cells : [Cella] = []
override func viewDidLoad() {
super.viewDidLoad()
//states that this class is the delegate of the data and the object tableView within this VC
UITableView.delegate = self
UITableView.dataSource = self
//force sets each of the the tableView rows to have a height of 200
UITableView.rowHeight = 200
// Do any additional setup after loading the view.
}
//Function hardcoded that creates the 5 cells to test the app
//MARK - Table view settings
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
//meaning my UITableView is going to have cells.count different rows
return cells.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
//Gets each item singularly in order from the dictionary of cells
let cellFormat = cells[indexPath.row]
//You need to do this Nib thing because you created a xib file
let nib = UINib(nibName: String(describing: TableViewCell.self), bundle: nil)
tableView.register(nib, forCellReuseIdentifier: "customCell")
// Says that it is going to create a reusable cell with the identifier from the XIB file and it is going to use the class TableViewCell to access its properties
let cellObject = tableView.dequeueReusableCell(withIdentifier: "customCell", for: indexPath) as! TableViewCell
//Creates and assigns the values from the item in the dictionary to the CellFormat for them to be displayed in the custom cell
cellObject.setCell(cellFormatTaken: cellFormat)
// returns the final Cell Item
return cellObject
}
@IBAction func unwindToActivitieslList(sender: UIStoryboardSegue) {
let sourceViewController = sender.source as? ViewController
let activities = sourceViewController?.cellaObjects
cells = activities!
UITableView.reloadData()
}
}
------------------ UPDATE ------------------ I've deleted
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
super.prepare(for: segue, sender: sender)
getDocumentsFromFirestore(firestoreReference: firestoreUsersReference) { (cellaArray) in
self.cellaObjects = cellaArray
}
}
And added the Button @IBAction func sardiniaNePressed(_ sender: UIButton) {
getDocumentsFromFirestore(firestoreReference: firestoreUsersReference) { (cellaArray) in
self.cellaObjects = cellaArray
self.performSegue(withIdentifier:"sardiniaNe",sender:nil)
}
}
I've also added the dispatch as kindly suggested. Still though I cannot get the data to be displayed. And another problem I have with the button is that whenever I click it, since the VCs are embedded in a navigation controller, they create a "duplicate" of the destination ViewController, and it keeps doing that for the whole time the app is open, leaving me with many duplicates, with empty rows.
xcode
tag: "USAGE NOTE: Use this tag only for questions about the Xcode IDE itself, and not for general Mac or iOS programming topics." – Frank van Puffelen