maytime
maytime

Reputation: 121

How to make one tableView with two dataSources in Swift?

I made a SearchViewController with tableView. I put some data in tableView from server (pictures of cats). But now I want to show not only all pictures, but also filtered by certain categories images. For this I suppose I need to push my second array with category's images into the tableView. I guess that to achieve this goal I need to reload tableView and change its dataSource, but how to realize it correctly I don't understand.

enter image description here

  1. Here is the method to get all pictures of cats

     func fetchData() {
     guard let endPoint = // myEndpoint else { return }
    
     endpoint.queryItems = queryParameters.map({ (key, value) in
         URLQueryItem(name: key, value: value) // some parameters
     })
    
     guard let url = endpoint.url else {
         return
     }
    
     var request = URLRequest(url: url)
     request.setValue(ApiClient.Identifiers.apiKey, forHTTPHeaderField: "x-api-key")
    
     let task = URLSession.shared.dataTask(with: request) { data, response, error in
         DispatchQueue.main.async {
             if error != nil {
                 print(error.debugDescription)
             } else {
                 do {
                     let myData = try JSONDecoder().decode([CatModel].self, from: data!)
                     self.catsModel = myData // catsModel is an array with struct with cats info (breed, category, id, etc.)
                     self.tableView.dataSource = myData as? any UITableViewDataSource
                     self.tableView.reloadData()
                 } catch let error {
                     print(error)
                 }
             }
         }
     }
     task.resume()
    

}

  1. Here is the method for filter by categories id :

    func fetchCategoryData(categoryID: Int) {
         let endpoint = URLComponents // url components
         guard let url = endpoint?.url else {
             return
         }
         var request = URLRequest(url: url)
         request.setValue(ApiClient.Identifiers.apiKey, forHTTPHeaderField: "x-api-key")
    
         let task = URLSession.shared.dataTask(with: request) { data, response, error in
             DispatchQueue.main.async {
                 if error != nil {
                     print(error.debugDescription)
                 } else {
                     do {
                         let myData = try JSONDecoder().decode([CatModel].self, from: data!)
                         self.catsModel = myData
                         self.tableView.reloadData()
                     } catch let error {
                         print(error)
                     }
                 }
             }
         }
         task.resume()
    
     }
    
    
  2. In CategoryViewController I made this method:

    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { let searchViewController = SearchViewController() let cat = catsModel[indexPath.row] searchViewController.fetchCategoryData(categoryID: cat.id) // this line works, but in fetchCategoryData doesn't happenings the reload of tableView with new data

    }

Thank you very much!

Upvotes: 0

Views: 696

Answers (2)

DonMag
DonMag

Reputation: 77423

There are various ways to filter data - but the code you posted doesn't make much sense.

You say:

"self.catsModel = myData // catsModel is an array with struct with cats info"

and:

"self.tableView.dataSource = myData as? any UITableViewDataSource"

but, you cannot assign an Array as a table view data source.

One approach is to do this:

var allCats: [CatModel] = []
var filteredCats: [CatModel] = []

then set self as the data source:

self.tableView.dataSource = self

To start, download / retrieve ALL Cats data and "store" it in allCats.

When the user selects a "filter," fill filteredCats:

self.filteredCats = self.allCats.filter({ $0.id == selectedFilterID })

Now your data source funcs can look something like this:

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    if selectedFilterID == nil {
        return allCats.count
    }
    return filteredCats.count
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "c", for: indexPath) as! CatCell

    var thisCat: CatModel!

    if selectedFilterID == nil {
        thisCat = allCats[indexPath.row]
    } else {
        thisCat = filteredCats[indexPath.row]
    }

    // set the cell's values / properties

    return cell
}

IF your API has a database of lots and lots of Cats (such as 50,000), or you ALWAYS have a Filter selected, you may not want to download all of the data.

In that case, you don't need a filteredCats array...

Instead, when a Filter is selected, fill allCats by requesting only the matching records from your API and call reload data on the table view.

Your data source funcs are then simpler:

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return allCats.count
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "c", for: indexPath) as! CatCell

    let thisCat = allCats[indexPath.row]

    // set the cell's values / properties

    return cell
}

Upvotes: 2

teja_D
teja_D

Reputation: 438

I'm not sure but issue may get fixed with one line change

Instead of this -

self.tableView.dataSource = myData as? any UITableViewDataSource

Try writing this -

self.tableView.dataSource = self

Upvotes: 0

Related Questions