'Filtering Image Data with Search Bar

I am trying to filter the data from API. The is successful loaded into view controller with table view cell . This is a movie applications . I am trying to filter the data based on the user type into the text box . I mentioned in the code filter my the title of the movie but The code is only able to filter the title and overview of the movie but the Image fields remain unfiltered such as image , overview etc. Here is the struct model .

import Foundation

struct Movie: Decodable {
    let originalTitle: String
    let overview: String
    let posterPath: String
    
    enum CodingKeys: String, CodingKey {
        case originalTitle = "original_title"
        case overview
        case posterPath = "poster_path"
    }
    
} 

Here is the protocol class code .

import Foundation

class MoviePresenter: MoviePresenterProtocol {
    
    private let view: MovieViewProtocol
    private let networkManager: NetworkManager
    var movies = [Movie]()
    private var cache = [Int: Data]()
    var rows: Int {
        return movies.count
    }
    
    init(view: MovieViewProtocol, networkManager: NetworkManager = NetworkManager()) {
        self.view = view
        self.networkManager = networkManager
    }
    
    
    func getMovies() {
        let url = "https://api.themoviedb.org/3/movie/popular?language=en-US&page=3&api_key=6622998c4ceac172a976a1136b204df4"
        
        networkManager.getMovies(from: url) { [weak self] result in
            switch result {
            case .success(let response):
                self?.movies = response.results
                self?.downloadImages()
                DispatchQueue.main.async {
                    self?.view.resfreshTableView()
                }
            case .failure(let error):
                
                DispatchQueue.main.async {
                    self?.view.displayError(error.localizedDescription)
                }
            }
        }
    }
    
    func getTitle(by row: Int) -> String? {
        return movies[row].originalTitle
    }
    
    func getOverview(by row: Int) -> String? {
        return movies[row].overview
    }
    
    func getImageData(by row: Int) -> Data? {
        return cache[row]
    }
    
   
    
    private func downloadImages() {
        let baseImageURL = "https://image.tmdb.org/t/p/w500"
        let posterArray = movies.map { "\(baseImageURL)\($0.posterPath)" }
        
        let group = DispatchGroup()
        group.enter()
        for (index, url) in posterArray.enumerated() {
            networkManager.getImageData(from: url) { [weak self] data in
                if let data = data {
                    self?.cache[index] = data
                }
            }
        }
        group.leave()
        group.notify(queue: .main) { [weak self] in
            self?.view.resfreshTableView()
        }
    }
    
    
}

Here is the controller code .

import UIKit

class MovieViewController: UIViewController, UISearchBarDelegate {
    
    
    @IBOutlet weak var userName: UILabel!
    @IBOutlet weak var tableView: UITableView!
    @IBOutlet weak var searchBar: UISearchBar!
    
    private var presenter: MoviePresenter!
    
    var finalname = ""
    
    
    override func viewDidLoad() {
        super.viewDidLoad()

       userName.text = "Hello: " + finalname
       setUpUI()
       presenter = MoviePresenter(view: self)
       searchBarText()
       
    }
    private func setUpUI() {
        tableView.dataSource = self
        tableView.delegate = self
    }
    
    
   private func searchBarText() {
        searchBar.delegate = self
    }
    
    @IBAction func selectSegment(_ sender: UISegmentedControl) {
        
        if sender.selectedSegmentIndex == 1{
         setUpUI()
        presenter = MoviePresenter(view: self)
        presenter.getMovies()
        }
    }
    func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
        if searchText == ""{
            presenter.getMovies()
        }
        else {
            presenter.movies =  presenter.movies.filter({ movies  in
                return movies.originalTitle.lowercased().contains(searchText.lowercased())
               
            })
        }
        
        tableView.reloadData()
    }
   
    
}

extension MovieViewController: MovieViewProtocol {
    
    func resfreshTableView() {
        tableView.reloadData()
    }
    
    func displayError(_ message: String) {
        let alert = UIAlertController(title: "Error", message: message, preferredStyle: .alert)
        let doneButton = UIAlertAction(title: "Done", style: .default, handler: nil)
        alert.addAction(doneButton)
        present(alert, animated: true, completion: nil)
    }
    
}

extension MovieViewController: UITableViewDataSource {
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        presenter.rows
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        
        let cell = tableView.dequeueReusableCell(withIdentifier: MovieViewCell.identifier, for: indexPath) as! MovieViewCell
        
        let row = indexPath.row
        let title = presenter.getTitle(by: row)
        let overview = presenter.getOverview(by: row)
        let data = presenter.getImageData(by: row)
        cell.configureCell(title: title, overview: overview, data: data)
        return cell
    }
    
   func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        let dc = storyboard?.instantiateViewController(withIdentifier: "MovieDeatilsViewController") as! MovieDeatilsViewController
        
        let row = indexPath.row
       dc.titlemovie = presenter.getTitle(by: row) ?? ""
       dc.overview = presenter.getOverview(by: row) ?? ""
       dc.imagemovie = UIImage(data: presenter.getImageData(by: row)!)
        self.navigationController?.pushViewController(dc, animated: true)
   }
    
    
}


extension MovieViewController: UITableViewDelegate {
    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return UITableView.automaticDimension
    }
}

Here is the screenshot of the result .

result



Solution 1:[1]

Caching image in tableview is a little bit tricky, and you may get problem when the cell changes or reusing itself,
that's cause you see same image when texts are different.

there are 2 famous package you can use it for you're problem and it's easy to use with a lot of options.

1- Kingfisher

2- SDWebImage

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Reza Khonsari