swiftuitableviewsdwebimagedidselectrowatindexpath

How to pass API image from Table View into another View Controller using didselectrowat


I am a newbie in Swift and I am trying to build an app in which I retrieve plant images and information from this api "https://rapidapi.com/mnai01/api/house-plants2".

I managed to implement a table view in which I display the name and image of each plant in the api, and when I click on any cell in the table view I displayed that certain plant's information in a new view controller.

My problem is that no matter what I tried I couldn't also display the image of that plant in that view controller and I don't know what to do to make it work.

It is also worth to mention that the links for the images are of this format:

This is the class of the view controller where the image and information should be displayed:

import UIKit
import SDWebImage

class PlantDetailsViewController: UIViewController {
    
    // image view for the plant
    @IBOutlet weak var plantImage: UIImageView!
    
    // labels for the plant information
    @IBOutlet weak var commonNameLabel: UILabel!
    @IBOutlet weak var latinNameLabel: UILabel!
    @IBOutlet weak var otherNamesLabel: UILabel!
    @IBOutlet weak var categoryLabel: UILabel!
    @IBOutlet weak var useLabel: UILabel!
    @IBOutlet weak var styleLabel: UILabel!
    @IBOutlet weak var familyLabel: UILabel!
    @IBOutlet weak var bloomSeasonLabel: UILabel!
    @IBOutlet weak var wateringLabel: UILabel!
    @IBOutlet weak var idealLightLabel: UILabel!
    @IBOutlet weak var growthLabel: UILabel!
    @IBOutlet weak var climatLabel: UILabel!
    @IBOutlet weak var diseaseLabel: UILabel!
    @IBOutlet weak var insectsLabel: UILabel!
    @IBOutlet weak var leafColourLabel: UILabel!
    @IBOutlet weak var bloomsColourLabel: UILabel!
    @IBOutlet weak var availabilityLabel: UILabel!
    @IBOutlet weak var bearingLabel: UILabel!
    @IBOutlet weak var appealLabel: UILabel!
    
    var plants: Plant?
    
    var strCommonName = ""
    var strLatinName = ""
    var strOtherNames = ""
    var strCategory = ""
    var strUse = ""
    var strStyle = ""
    var strFamily = ""
    var strBloomSeason = ""
    var strWatering = ""
    var strIdealLight = ""
    var strGrowth = ""
    var strClimat = ""
    var strDisease = ""
    var strInsects = ""
    var strLeafColour = ""
    var strBloomsColour = ""
    var strAvailability = ""
    var strBearing = ""
    var strAppeal = ""
    
    override func viewDidLoad() {
        super.viewDidLoad()
    
        commonNameLabel.text = strCommonName
        latinNameLabel.text = strLatinName
        otherNamesLabel.text = strOtherNames
        categoryLabel.text = strCategory
        useLabel.text = strUse
        styleLabel.text = strStyle
        familyLabel.text = strFamily
        bloomSeasonLabel.text = strBloomSeason
        wateringLabel.text = strWatering
        idealLightLabel.text = strIdealLight
        growthLabel.text = strGrowth
        climatLabel.text = strClimat
        diseaseLabel.text = strDisease
        insectsLabel.text = strInsects
        leafColourLabel.text = strLeafColour
        bloomsColourLabel.text = strBloomsColour
        availabilityLabel.text = strAvailability
        bearingLabel.text = strBearing
        appealLabel.text = strAppeal
    }
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
}

This is is the didSelectRowAt function for the table view:

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        
        let detail:PlantDetailsViewController = self.storyboard?.instantiateViewController(withIdentifier: "showDetails") as! PlantDetailsViewController
        
        detail.strCommonName = plants[indexPath.row].common_name?.first  ?? "N/A"
        detail.strLatinName = plants[indexPath.row].latin_name ?? "N/A"
        detail.strOtherNames = plants[indexPath.row].other_names ?? "N/A"
        detail.strCategory = plants[indexPath.row].categories ?? "N/A"
        detail.strUse = plants[indexPath.row].use?.first ?? "N/A"
        detail.strStyle = plants[indexPath.row].style ?? "N/A"
        detail.strFamily = plants[indexPath.row].family ?? "N/A"
        detail.strBloomSeason = plants[indexPath.row].blooming_season ?? "N/A"
        detail.strWatering = plants[indexPath.row].watering ?? "N/A"
        detail.strIdealLight = plants[indexPath.row].light_ideal ?? "N/A"
        detail.strGrowth = plants[indexPath.row].growth ?? "N/A"
        detail.strClimat = plants[indexPath.row].climat ?? "N/A"
        detail.strDisease = plants[indexPath.row].disease ?? "N/A"
        detail.strInsects = plants[indexPath.row].insects?.first ?? "N/A"
        detail.strLeafColour = plants[indexPath.row].color_of_leaf?.first ?? "N/A"
        detail.strBloomsColour = plants[indexPath.row].color_of_blooms ?? "N/A"
        detail.strAvailability = plants[indexPath.row].availability ?? "N/A"
        detail.strBearing = plants[indexPath.row].bearing ?? "N/A"
        detail.strAppeal = plants[indexPath.row].appeal ?? "N/A"
        
        self.navigationController?.pushViewController(detail, animated: true)
    }

In the Manager folder I created class called "APICaller" where I fetch the data from the API. This is the function that does that:


    func getAllPlants (completion: @escaping (Result<[Plant], Error>) -> Void) {
        guard let url = URL(string: "\(Constants.baseURL)/all/?rapidapi-key=\(Constants.API_KEY)") else {return}
        let task = URLSession.shared.dataTask(with: URLRequest(url: url)) { data, _, error in
            guard let data = data, error == nil else {return}
            do {
                let results = try JSONDecoder().decode([Plant].self, from: data)
                completion(.success(results))
            } catch {
                completion(.failure(APIError.failedTogetData))
            }
        }
        task.resume()
    }

And finally this is the Plant struct model:

struct Plant: Codable {
    
    let appeal: String?
    let availability: String?
    let bearing: String?
    let blooming_season: String?
    let categories: String?
    let climat: String?
    let color_of_blooms: String?
    let color_of_leaf: [String]?
    let common_name: [String]?
    let disease: String?
    let family: String?
    let growth: String?
    let insects: [String]?
    let latin_name: String?
    let light_ideal: String?
    let other_names: String?
    let style: String?
    let use: [String]?
    let watering: String?
    let id: String?
    let img: String?
    let url: String?
    
    
    private enum CodingKeys: String, CodingKey {
        case appeal = "Appeal"
        case availability = "Availability"
        case bearing = "Bearing"
        case blooming_season = "Blooming season"
        case categories = "Categories"
        case climat = "Climat"
        case color_of_blooms = "Color of blooms"
        case color_of_leaf = "Color of leaf"
        case common_name = "Common name"
        case disease = "Disease"
        case family = "Family"
        case growth = "Growth"
        case insects = "Insects"
        case latin_name = "Latin name"
        case light_ideal = "Light ideal"
        case other_names = "Other names"
        case style = "Style"
        case use = "Use"
        case watering = "Watering"
        case id = "id"
        case img = "Img"
        case url = "Url"
        
    }
}

I think the problem is that each image is a string which contains a link, and to be able to display it in the table view cells I used SDWebImage. The question is how do I do that to display the image in the detail view controller? Thank you for your time. Any help or piece of advice is greatly appreciated :)

UPDATE- I tried to display it like this :

I wrote this in the viewdidload function in the detail view controller:

var selectedImage: String?
if let imageToLoad = selectedImage {
plantImage.image  = UIImage(named: imageToLoad)
}

and then I added this line in the didselectrowat function:

detail.selectedImage = plants[indexPath.row].img

It still doesn't work and I don't know what I am doing wrong


Solution

  • Swift 5.5, Xcode 14.2

    plantImage.image = UIImage(named: imageToLoad) is wrong, named is to local images, when you want get images from one API, like "http://www.tropicopia.com/house-plant/thumbnails/5556.jpg", you need use :

    Github Example: https://github.com/MaatheusGois/answer-75361391

    let url = URL(string: image.url)
    
    func downloadImage(from url: URL) {
        print("Download Started")
        getData(from: url) { data, response, error in
            guard let data = data, error == nil else { return }
            print(response?.suggestedFilename ?? url.lastPathComponent)
            print("Download Finished")
            // always update the UI from the main thread
            DispatchQueue.main.async { [weak self] in
                self?.plantImage.image = UIImage(data: data)
            }
        }
    }
    
    func getData(from url: URL, completion: @escaping (Data?, URLResponse?, Error?) -> Void) {
        URLSession.shared.dataTask(with: url, completionHandler: completion).resume()
    }
    

    IMPORTANT (Common error)

    https://developer.apple.com/forums/thread/119977