Youtube App not displaying the cells & "UITableView.reloadData() must be used from main thread only"

when runnig the app when i added the UITableView and the cell it appears a purple warning saying: “UITableView.reloadData() must be used from main thread only”

i tried DispatchQueue but nothing different happened :frowning:

GitHub Code: https://github.com/CarlosCardonaM/Youtube

import UIKit

class ViewController: UIViewController {

@IBOutlet weak var tableView: UITableView!

var model = Model()
var videos = [Video]()

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view.
    
    tableView.delegate = self
    tableView.dataSource = self
    
    // Set it's self as the delegate of the model
    model.delegate = self
    
    model.getVideos()

}

}

// MARK: - Model Delegate Methods

extension ViewController: ModelDelegate {

func videosFetched(_ videos: [Video]) {
    // Set the returned videos to our videos property
    self.videos = videos
    
    // Refresh the tableview
    
    tableView.reloadData()
}

}

// MARK: - TableView Delegate and Datasource Methods

extension ViewController: UITableViewDelegate, UITableViewDataSource {

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return videos.count
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "VideoCell", for: indexPath)
    
    // Configure the cell
    
    // Get the title for the video in question
    let title = self.videos[indexPath.row].title
    cell.textLabel?.text = title
    
    // Return the cell
    return cell
}

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    
}

}

MODEL STRUCT

protocol ModelDelegate {
func videosFetched(_ videos:[Video])
}

class Model {

var delegate:ModelDelegate?

func getVideos() {
    
    // Create a url object
    let url = URL(string: Constants.API_URL)
    guard url != nil else {
        return
    }
    
    // Get a url session object
    let session = URLSession.shared
    
    // Get a data task from the url session
    let dataTask = session.dataTask(with: url!) { (data, response, error) in
        
        // Check if there were any errors
        if error != nil || data == nil {
            return
        }
        
        do {
            // Parsing the data into video objects
            let decoder = JSONDecoder()
            decoder.dateDecodingStrategy = .iso8601
            
            let response = try decoder.decode(Response.self, from: data!)
            
            if response.items != nil {
                // Call the "videosFetched" methods of the delegate
                self.delegate?.videosFetched(response.items!)
            }
            
            dump(response)
        }
        catch {
            
        }

    }
    
    // Kick off the task
    dataTask.resume()
}

}

VIDEO STRUCT

struct Video : Decodable {

var videoID = ""
var title = ""
var description = ""
var thumbnail = ""
var published = Date()

enum CodingKeys: String, CodingKey {
    
    case snippet
    case thumbnails
    case high
    case resourceId
    
    case published = "publishedAt"
    case title
    case description
    case thumbnail = "url"
    case videoId
}

init (from decoder: Decoder) throws {
    
    let container = try decoder.container(keyedBy: CodingKeys.self)
    let snippetContainer = try container.nestedContainer(keyedBy: CodingKeys.self, forKey: .snippet)
    
    // Parse the title
    self.title = try snippetContainer.decode(String.self, forKey: .title)
    
    // Parse the description
    self.description = try snippetContainer.decode(String.self, forKey: .description)
    
    // Parse the punlished date
    self.published = try snippetContainer.decode(Date.self, forKey: .published)
    
    // Parse thumbnails
    let thumbnailContainer = try snippetContainer.nestedContainer(keyedBy: CodingKeys.self, forKey: .thumbnails)
    let highContainer = try thumbnailContainer.nestedContainer(keyedBy: CodingKeys.self, forKey: .high)
    self.thumbnail = try highContainer.decode(String.self, forKey: .thumbnail)
    
    // Parse video ID
    let resourceIdContainer = try snippetContainer.nestedContainer(keyedBy: CodingKeys.self, forKey: .resourceId)
    self.videoID = try resourceIdContainer.decode(String.self, forKey: .videoId)
    
}

}

struct Response: Decodable {

var items: [Video]?

enum CodingKeys: String, CodingKey {
    case items
}

init (from decoder: Decoder) throws {
    
    let container = try decoder.container(keyedBy: CodingKeys.self)
    
    self.items = try container.decode([Video].self, forKey: .items)
}

}

@cardonacarlos97.06
Hi Carlos,

In your Model where you have the code:

if response.items != nil {
    // Call the "videosFetched" methods of the delegate
    self.delegate?.videosFetched(response.items!)
}

you should add the DispatchQueue like this:

if response.items != nil {
    // Call the "videosFetched" methods of the delegate
    DispatchQueue.main.async {
        self.delegate?.videosFetched(response.items!)
    }
}

so that the delegate passes back the data on the main thread.