Amais Sheikh
Amais Sheikh

Reputation: 463

How to refresh container view (reload table view) after getting data from firebase: Swift

I have been working on an app. I am a beginner so please ignore any mistakes.

The problem is that i have a view controller, which has 2 container view controllers controlled by a segmented control.

enter image description here

All three of them have separate classes: (say)

  1. MainViewController
  2. FirstViewController
  3. SecondViewController

In the main view controller, i am getting some data from firebase, which i am storing in an array, and this array is to be passed to the first and second container views, which have their table views, which will load data based on this array which is passed.

Now before the data comes back in the MainViewController, the First and Second view controllers are already passed with an empty array, and no data loads up in their table views (obviously because the array is empty).

I want the container view controllers to load up after the data is received, and array is loaded. Any help ?, Thanks

P.s I am not performing any segue because these are container views, and they are automatically loaded as the main view container loads.

EDIT: Being more 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, 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.

Upvotes: 0

Views: 1625

Answers (2)

Mike Taverne
Mike Taverne

Reputation: 9362

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
}

Please note, since you didn't provide any code, these are just examples and you will have to adjust them to fit your specific situation.

EDIT: Per comment below, 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.

Upvotes: 0

Gayal Rupasinghe
Gayal Rupasinghe

Reputation: 144

I can only speculate without seeing the code, so here goes nothing... 😉

Try doing the following on viewDidLoad() of MainViewController:

  1. Use the property related to FirstViewController (for the sake of my explanation let’s assume it’s named ‘firstVC’). What you want to do is go, firstVC.view.isHidden = true

  2. Do the same to SecondViewController.

Why? Doing so will hide the container VC’s (ViewController) from the MainViewController’s view.

  1. Now what you want to do is, at the place where you get data from Firebase (at the closure), add the following: firstVC.view.isHidden = false
  2. Do the same to SecondViewController.

This brings it back to view with the data you fetched already populating it.

Hopefully this helps you out some way.

Upvotes: 0

Related Questions