Passing data to container view, after loading it from firebase

Let me be precise and clear with original code: Originally I have 3 view controllers

  1. SearchResultsScreenViewController (Main VC)
  2. GuidesListSearchScreenViewController (First Container VC)
  3. ServicesListSearchScreenViewController (Second Container VC)

In the Main VC i used a segmented control to see container vc’s on screen, here:


import UIKit
import Firebase

class SearchResultsScreenViewController: UIViewController
{
    
    @IBOutlet weak var GuideListView: UIView!
    @IBOutlet weak var ServicesListView: UIView!
    
    var searchQueryKeyword: String?
    var guidesDataArray = [GuideDM]()
    
    override func viewDidLoad()
    {
        super.viewDidLoad()
        
        ServicesListView.isHidden = true
        populateGuidesList()
    }
   
    
    @IBAction func SegmentChanged(_ sender: UISegmentedControl)
    {
        switch sender.selectedSegmentIndex
            {
            case 0:
                GuideListView.isHidden = false
                ServicesListView.isHidden = true
                break
            case 1:
                GuideListView.isHidden = true
                ServicesListView.isHidden = false
                break
            default:
                break
            }
    }
    
    func populateGuidesList()
    {
        let dbRef = Firestore.firestore().collection("guide")
        dbRef.getDocuments
        { (snapshot, error) in
            if let err = error
            {
                print(err.localizedDescription)
                print("Error: Unable to find guides list")
            }
            else
            {
                if let snap = snapshot
                {
                    print("List is started now")
                    for doc in snap.documents
                    {
                        if doc.exists
                        {
                            let data = doc.data()
                            let city = data["city"] as? String ?? ""
                            let province = data["province"] as? String ?? ""
                            let country = data["country"] as? String ?? ""
                            if city.localizedCaseInsensitiveContains(self.searchQueryKeyword!) || province.localizedCaseInsensitiveContains(self.searchQueryKeyword!) || country.localizedCaseInsensitiveContains(self.searchQueryKeyword!)
                            {
                                let guideId = doc.documentID
                                let guideEmail = data["email"] as? String ?? ""
                                let name = data["name"] as? String ?? ""
                                let dob = data["dob"] as? String ?? ""
                                let feeCurrency = data["feeCurrency"] as? String ?? ""
                                let status = data["status"] as? String ?? ""
                                let totalReviews = data["totalReviews"] as? Int ?? 0
                                let rating = data["rating"] as? Int ?? 0
                                let baseFee = data["baseFee"] as? Int ?? 0
                                let isGuideFeatured = data["isGuideFeatured"] as? Bool ?? false

                                //make a model of guide and append in array
                                let guide = GuideDM(id: guideId, email: guideEmail, name: name, dob: dob, city: city, province: province, country: country, feeCurrency: feeCurrency, status: status, baseFee: baseFee, rating: rating, totalReviews: totalReviews, isGuideFeatured: isGuideFeatured)
                                self.guidesDataArray.append(guide)
                            }
                        }
                    }
                    print("list is finalized now")
                }
            }
        }
    }
    
    override func prepare(for segue: UIStoryboardSegue, sender: Any?)
    {
        
       if segue.identifier == "searchScreentoGuideListSegment"
       {
           let guidesListContainerVC = segue.destination as! GuidesListSearchScreenViewController
           guidesListContainerVC.guidesDataArray = self.guidesDataArray
       }
        
    }

}

In the above class my code makes a call to function “populateGuidesList()” which makes a network call to get data from firebase, and at the same time loads up my container views. The problem is, before the network call returns data, the empty array gets passed to my “GuidesListSearchScreenViewController” i.e. (First container VC), which is a table view, and loads an empty table because the array is not filled yet.

My First container VC class:


import UIKit
import Firebase

class GuidesListSearchScreenViewController: UIViewController
{

    @IBOutlet weak var guidesListTableView: UITableView!
    var guidesDataArray = [GuideDM]()
    
    override func viewDidLoad()
    {
        super.viewDidLoad()

        guidesListTableView.delegate = self
        guidesListTableView.dataSource = self
        
        guidesListTableView.register(UINib(nibName: "GuidesListCellSearchScreenTableViewCell", bundle: nil), forCellReuseIdentifier: "guidesListCell")
    }
    
}


extension GuidesListSearchScreenViewController: UITableViewDataSource, UITableViewDelegate
{
    
    // below functions are to setup the table view
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
    {
        return guidesDataArray.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
    {
        let cell = guidesListTableView.dequeueReusableCell(withIdentifier: "guidesListCell") as! GuidesListCellSearchScreenTableViewCell
        //adding properties to cell and then returning cell
        return cell
    }
    
}

GOAL: Either load the container view, after the data is received in the array, or refresh the table by again passing the array to container VC and reloading table.

Other solution: I had tried loading up all this array data inside First container VC class, and reloading table view data from there, which works perfectly fine, but to me which is a very inefficient approach, as i need this array in both container views, so making network calls for each container vc seems very inefficient. Therefore, i am trying to get the data once and pass in both container views. Kindly correct me if you feel me wrong.

P.s I have deleted other functionality and simplified the code. And help would be highly appreciated.

I have solved, here is the solution

The container view controllers will load when the main view controller is loaded. Therefore you will have to update the container view controllers when the main view controller receives the data.

A simple way to do this is to update the arrays in the container view controllers, and use a didSet on the arrays to force the container view controllers to reload themselves.

For example, if your FirstViewController displays the array data in a table view, you might do this:

    var array: [ArrayItem] {
        didSet {
            tableView.reloadData()
        }
    }

Then in your main view controller, when the data is received, set this property:

    getData() { resultArray in 
        firstViewController.array = resultArray
    }

You should be careful not to set the array in your FirstViewController before its view has loaded or the call to tableView.reloadData() will cause your app to crash.

EDITED: For future viewers This is the correct solution, I solved the problem by applying your method. The only problem I faced again was how to access the array of container view from inside the closure, where firebase data was returned, and I did this way:

    If let vc = self.children.first as? FirstViewController
        {
            vc.array = self.array
        }

we can access the child container views by using the (self.children[index] / self.children.first / and more in documentation). After accessing we can set the properties of container view controllers.

Solution by Mike Taverne