swiftuitableviewmvvmprepared-statementdetailsview

I tried to make Swift MVC to MVVM but I can't get the data when I switch to the detail page?


I'm trying to make MVVM from MVC, but I can't transfer the information that I could transfer before, through prepare. What is the reason? How can I fix ?

MainViewController

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "showDetail", let detailVC = segue.destination as? DetailViewController, let indexPath = tableView.indexPathForSelectedRow {
            let pokemon = viewModel.pokemons[indexPath.row]
            detailVC.viewModel?.pokemonId = "\(indexPath.row + 1)"
            detailVC.viewModel?.name = pokemon.name
            detailVC.viewModel?.imageURL = viewModel.getPokemonSpritesURL(for: indexPath.row)
        }
    }

DetailViewModel


class DetailViewModel {
    
    // MARK: - Properties
    
    var pokemonId: String
    let apiService: PokeAPIService
    
    var name: String?
    var abilities: [Ability]?
    var imageURL: URL?
    
    // MARK: - Initializers
    
    init(pokemonId: String, apiService: PokeAPIService) {
        self.pokemonId = pokemonId
        self.apiService = apiService
    }
    
    // MARK: - Public Methods
    
    func fetchPokemonDetails(completion: @escaping () -> Void) {
        apiService.getPokemon(by: pokemonId) { [weak self] response in
            guard let self = self else { return }
            self.name = response.name
            self.abilities = response.abilities
            if let urlString = response.sprites?.frontDefault {
                self.imageURL = URL(string: urlString)
            }
            completion()
        }
    }
}

DetailViewController

var viewModel: DetailViewModel?

@IBOutlet weak var imageView: UIImageView!
    @IBOutlet weak var nameLabel: UILabel!
    @IBOutlet weak var abilitiesNameLabel: UILabel!
    @IBOutlet weak var abilitiesNameLabel2: UILabel!
// MARK: - Private Methods
    
     func configureView() {
        viewModel?.fetchPokemonDetails(completion: { [weak self] in
            guard let self = self else { return }
            DispatchQueue.main.async {
                self.nameLabel.text = self.viewModel?.name
                self.abilitiesNameLabel.text = self.viewModel?.abilities?[0].ability?.name
                self.abilitiesNameLabel2.text = self.viewModel?.abilities?[1].ability?.name
                if let imageURL = self.viewModel?.imageURL {
                    self.loadImage(from: imageURL)
                }
            }
        })
    }
    
     func loadImage(from url: URL) {
        URLSession.shared.dataTask(with: url) { [weak self] (data, response, error) in
            guard let self = self, let data = data else { return }
            DispatchQueue.main.async {
                self.imageView.image = UIImage(data: data)
            }
        }.resume()
    }

When I switch back to MVC I don't have any problems. I can't find where I'm doing wrong It seems like I'm missing something small but I can't figure it out.


Solution

  • Your problem is here

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "showDetail", let detailVC = segue.destination as? DetailViewController, let indexPath = tableView.indexPathForSelectedRow {
            let pokemon = viewModel.pokemons[indexPath.row]
            detailVC.viewModel?.pokemonId = "\(indexPath.row + 1)"
            detailVC.viewModel?.name = pokemon.name
            detailVC.viewModel?.imageURL = viewModel.getPokemonSpritesURL(for: indexPath.row)
        }
    }
    

    at this step detailVC.viewModel is nil and setting to VM some properties doesn't have any result. So in this method you need to create instance of DetailViewModel with all required data and set it to detailVC like this:

    detailVC.viewModel = DetailViewModel(id: id, name: name, url: url)