0

I have 2 Controllers: CollectionViewController and DetailViewController. In CollectionViewController I fetched Data and get coctails. Now I want get coctails?.drinks?[indexPath.row] and pass in DetailViewController, where I need to fetchRequest again but with another API and new url. But when loading DetailViewController, the method networkDataService.fetchDrink(drinkName: drinkName) does not work. What I did wrong?

struct Coctail: Codable {
   let drinks: [Drink]?
}

struct Drink: Codable {
   let strDrink : String?
   let strDrinkThumb : URL?
}

class NetworkDataService {

let networkManager = NetworkManager.shared

func fetchCoctails(completion: @escaping (Coctail?) -> Void) {
    let url = "https://www.thecocktaildb.com/api/json/v1/1/filter.php?c=Cocktail"
    networkManager.fetchData(url: url, completion: completion)
}

func fetchDrink(drinkName: String, completion: @escaping (Coctail?) -> Void) {
    let url = "https://www.thecocktaildb.com/api/json/v1/1/search.php?s=\(drinkName)"
    networkManager.fetchData(url: url, completion: completion)
   }
}

class CollectionViewController: UICollectionViewController {

var coctails: Coctail?
let networkDataService = NetworkDataService()

override func viewDidLoad() {
    super.viewDidLoad()

    networkDataService.fetchCoctails() { (coctails) in
        self.coctails = coctails

        DispatchQueue.main.async {
            self.collectionView.reloadData()
        }
    }

}

// MARK: UICollectionViewDataSource

override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    return coctails?.drinks?.count ?? 0
}

override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! CollectionViewCell

    let drink = coctails?.drinks?[indexPath.row]

    cell.config(drink: drink)

    return cell
}

// MARK: - Navigation

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {

    guard let indexPath = collectionView.indexPathsForSelectedItems?.first else { return }
    let drink = coctails?.drinks?[indexPath.row]

    let destination = segue.destination as! DetailViewController
    destination.drinkName = drink?.strDrink ?? ""
   }
}

class DetailViewController: UIViewController {

var drinkName = ""
var coctail: Coctail?
let networkDataService = NetworkDataService()

override func viewDidLoad() {
    super.viewDidLoad()

    networkDataService.fetchDrink(drinkName: drinkName) { (coctail) in
        self.coctail = coctail
    }
}

1 Answers1

2

If you are using drink name as in strDrink, then the problem is that you are sending something like:

'57 Chevy with a White License Plate

Which means that URL will be something like:

https://www.thecocktaildb.com/api/json/v1/1/search.php?s='57 Chevy with a White License Plate

And won't be translated to URL encoded URL automatically.

The problem with this approach is that you need to url encode the name before sending it to API. Something like:

networkDataService.fetchDrink(drinkName: drinkName.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)) { (coctail) in
    self.coctail = coctail
}

That way you will get URL with something like:

https://www.thecocktaildb.com/api/json/v1/1/search.php?s=%2757%20Chevy%20with%20a%20White%20License%20Plate

Referring to this URL encoded string: Swift - encode URL

On the other hand, your API returns id of the drink, idDrink which should be used to to fetch details instead of running search and then extracting first one per name specific. What could happen later on as well is that you might have two Jack Daniel's, with different values and you will always show first one.

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Miknash
  • 7,888
  • 3
  • 34
  • 46