skip to Main Content

I recieves json data from api and totally confused what should I do next to pass this data to custom cell with imageView and labels in order to update UI in tableView.

Getting JSON

import Foundation

struct Breed: Codable {
    let name: String?
    let origin: String?
    let life_span:String?
    let temperament: String?
    let description: String?
    let wikipedia_url: String?
    let image: Image?
}

struct Image: Codable {
    let url: String?
}

func getDataFromCatsApi() {
    
    let url = URL(string: "https://api.thecatapi.com/v1/breeds")
    
    let task = URLSession.shared.dataTask(with: url!) { data, _ , error in
        let decoder = JSONDecoder()
        if let data = data {
            let breed = try? decoder.decode([Breed].self, from: data)
            print (breed as Any)
        } else {
            print (error as Any)
        }
    }
    task.resume()
}

All data is printed correctly.

In ViewController I have a tableView with custom cell.

    import UIKit

class MainVC: UIViewController {
    @IBOutlet weak var tableView: UITableView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        title = "Cats"
        view.backgroundColor = .systemBackground
        getDataFromCatsApi()
        
    }
}

extension MainVC: UITableViewDelegate, UITableViewDataSource {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 5
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell",
                                                 for: indexPath) as? CustomTableViewCell
        
        return cell ?? CustomTableViewCell()
    }
}

Class for custom cell. Here I have imageView and labels for displaying data from json.

    import UIKit

class CustomTableViewCell: UITableViewCell {
    @IBOutlet weak var catImageView: UIImageView!
    @IBOutlet weak var nameLabel: UILabel!
    @IBOutlet weak var originLabel: UILabel!
    @IBOutlet weak var addToFavButton: UIButton!

}

3

Answers


  1. I have attached the code please check

    extension MainVC: UITableViewDelegate, UITableViewDataSource {
        func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
            return Breed.count
        }
        
        func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            let cell = tableView.dequeueReusableCell(withIdentifier: "cell",
                                                     for: indexPath) as? CustomTableViewCell
            cell.nameLabel.text  = Breed[indexPath.row].name // see here
            
            return cell ?? CustomTableViewCell()
        }
    }
    
    Login or Signup to reply.
  2. First of all, you are not returning anything from getDataFromCatsApi(). Since it is an asynchronous call, you have to implement a way to get the values either by using a callback or a delegate. In this case callback would suffice.

    Then once you receive a value from the api call, set those values in func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) in which you can use cell.nameLabel.text = <received value> and etc.

    Login or Signup to reply.
  3. First of all declare only those properties as optional which can be nil

    struct Breed: Decodable {
        let name, origin, lifeSpan, temperament, description: String
        let wikipediaUrl: String?
        let image: Image?
    }
    
    struct Image: Decodable {
        let url: String?
    }
    

    In getDataFromCatsApi add a completion handler

    func getDataFromCatsApi(completion:  @escaping (Result<[Breed],Error>) -> Void ) {
        
        let url = URL(string: "https://api.thecatapi.com/v1/breeds")
        
        let task = URLSession.shared.dataTask(with: url!) { data, _ , error in
            if let error = error { completion(.failure(error)); return }
            let decoder = JSONDecoder()
            decoder.keyDecodingStrategy = .convertFromSnakeCase
            completion(Result { try decoder.decode([Breed].self, from: data!) })
        }
        task.resume()
    }
    

    In MainVC declare a data source array

    var cats = [Breed]()
    

    Replace viewDidLoad with

    override func viewDidLoad() {
        super.viewDidLoad()
        title = "Cats"
        view.backgroundColor = .systemBackground
        getDataFromCatsApi {[unowned self] result in
            DispatchQueue.main.async {
                switch result {
                    case .success(let breed): 
                        self.cats = breed
                        self.tableView.reloadData()
                    case .failure(let error): print(error)
                }
            }
        } 
    }
    

    and the table view datasources methods with

    extension MainVC: UITableViewDelegate, UITableViewDataSource {
        func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
            return cats.count
        }
        
        func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            let cell = tableView.dequeueReusableCell(withIdentifier: "cell",
                                                     for: indexPath) as! CustomTableViewCell
            
            let cat = cats[indexPath.row]
            cell.nameLabel = cat.name
            cell.originLabel = cat.origin
        }
    }
    

    To load the pictures is beyond the scope of the question. There are libraries like SDWebImage or Kingfisher to load and cache images asynchronously.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search