Search code examples
iosswiftstructsegue

Sending JSON data stored in a struct through a segue


I'm wanting to store idMeal on the cell I click to send it through a segue to my DetailViewController and use it to access an API endpoint for the respective meal.

API

API with meal details that I'm trying to access for EACH meal when clicked on.

I believe I'm very close but I've been here for several hours.

Here are my structs:

//struct for the dictionary holding the desserts
struct DessertDictionary: Codable {
    var meals: [Desserts]
}

//struct for all desserts
struct Desserts: Codable {
    var strMeal: String?
    var strMealThumb: String?
    var idMeal: String?

}
//struct for dictionary holding the details
struct MealDataDictionary: Codable {
    var meals: IndependentMealData
}

//struct for dessert details
struct IndependentMealData: Codable {
    var idMeal: String
    var strMeal: String
    var strInstructions: String
}

Here is my first ViewController:

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
    
    var mealList = [Desserts]()

    @IBOutlet weak var tableView: UITableView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        getApiData {
            print("data loaded")
            self.tableView.reloadData()
        }
        tableView.delegate = self
        tableView.dataSource = self
        }
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return mealList.count
    }
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = UITableViewCell(style: .default, reuseIdentifier: nil)
        cell.textLabel?.text = "\(mealList[indexPath.row].strMeal!)"
        return cell
    }
    
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        performSegue(withIdentifier: "showDetails", sender: self)
    }
    
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if let destination = segue.destination as? DetailTableViewController {
            destination.idMeal = //NEED DATA HERE//
        }
    }
    
    
//    MealApi.shared.getApiData(onCompletion: anonFunction)
    func getApiData(completed: @escaping () -> ()) {
        let url = URL(string: "https://www.themealdb.com/api/json/v1/1/filter.php?c=Dessert")
        
        URLSession.shared.dataTask(with: url!) { (data, response, error) in
            
            if error == nil {
                do {
                    // need to use the TopMeal struct to decode the json because it matches the structure of the json.
                    let jsonData = try JSONDecoder().decode(DessertDictionary.self, from: data!)
                    // after you decode the json you access the array within it to setup your array of meals instance variable that is being used by your table view.
                    self.mealList = jsonData.meals
                    
                    

                    DispatchQueue.main.async {
                        completed()
                    }
                }
                catch {
                    print(error)
                }
            }
        }.resume()
    }
}

Here is my DetailViewController where I am trying to pass the "idMeal" and use for the API pull for each cell, when clicked.

class DetailTableViewController: UITableViewController {

    var idMeal: String = ""
    var mealDatails: IndependentMealData?
    
    
    //UI Oulets (Image, Dessert Name, Instructions, Ingredents)
    @IBOutlet weak var nameLabel: UILabel!
    @IBOutlet weak var imageView: UIImageView!
    
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        
        func getApiDetailData(completed: @escaping () -> ()) {
            let urlString = "https://www.themealdb.com/api/json/v1/1/lookup.php?i=\(idMeal)"
            let url = URL(string: urlString)
            
            URLSession.shared.dataTask(with: url!) { (data, response, error) in
                
                if error == nil {
                    do {
                        // need to use the TopMeal struct to decode the json because it matches the structure of the json.
                        let jsonData = try JSONDecoder().decode(MealDataDictionary.self, from: data!)
                        // after you decode the json you access the array within it to setup your array of meals instance variable that is being used by your table view.
                        self.mealDatails = jsonData.meals
//                        print(self.mealDatails?.idMeal)
                       
                        DispatchQueue.main.async {
                            completed()
                        }
                    }
                    catch {
                        print(error)
                    }
                }
            }.resume()
        }
    }

If you need any extra information let me know, I've tried a number of things to access that data.


Solution

  • I have to define the index the user tapped. E.g., just save the row and use it.

        class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
            
            var mealList = [Desserts]()
            var indexTapped: Int?
    
     func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    indexTapped = indexPath.row
            performSegue(withIdentifier: "showDetails", sender: self)
        }
         override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
                if let destination = segue.destination as? DetailTableViewController {
                    destination.idMeal = mealList[indexTapped!].idMeal
                }
            }
        }