Home > Enterprise >  How to display JSON data from API in TableView?
How to display JSON data from API in TableView?

Time:12-02

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!

}

CodePudding user response:

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.

CodePudding user response:

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.

CodePudding user response:

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()
    }
}

CodePudding user response:

I am using a structure similar to this

import UIKit

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

    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.yourLabelName.text = Breed[indexPath.row].name
            return cell
        }
    }
  • Related