Reputation: 121
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.
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()
}
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() }
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
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
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