Stark
Stark

Reputation: 51

JSON codable error while using in Table View

I'm building a news app using newsapi.org api. I was able to get data from server and decode it using JSON decoder but the problem is in the response I get an array of article when I assign the array of articles as data source to table view by providing number of rows in section as article array count I get an error stating that Unexpectedly found nil while implicitly unwrapping an Optional value in numberofrowsinsetction method.

Error description here

I was able to print the articles.count in debug console in which aim getting the count.But when I assign it to numberrowsinsection method I get this error.

Network service code

import Foundation


class NetworkService{
    
    
    static  let  sharedobj = NetworkService()
    let session = URLSession.shared
    
    
   private let HEADLINES_URL = "https://newsapi.org/v2/top-headlines?country=in&apiKey=API_key_HERE"
    
    public func getHeadLines(onSuccess: @escaping ([Articles]) -> Void)
    {
        let datatask = session.dataTask(with: URL(string: HEADLINES_URL)!, completionHandler: { (data, response, error) in
            
            DispatchQueue.main.async {
                
                do
                {
                    let decoder = try JSONDecoder().decode(Welcome.self, from: data!)
                    onSuccess(decoder.articles!)
                }
                
                catch
                {
                    print(error.localizedDescription)
                }
                
               
            }
            
            
            
            
            
        
        })
        
        datatask.resume()
        
        
        
    }

View Controller code

import UIKit

class ViewController: UIViewController,UITableViewDelegate,UITableViewDataSource{
   
   
    var articles: [Articles]!
    var newsurl: String!
    
    @IBOutlet weak var headlinestableview: UITableView!
    override func viewDidLoad() {
        super.viewDidLoad()
       
        self.headlinestableview.delegate = self
        self.headlinestableview.dataSource = self
        
        NetworkService.sharedobj.getHeadLines { (a) in
            self.articles = a
            
            print(self.articles.count)
            
           
            
        }
       
       
     
        
            
        
     
        
       
        
        
        
        
       
        
        
    }

    
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return articles.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        
        
       if let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as? HeadLinesCellTableViewCell
        {
            
            let data = articles[indexPath.row]
            
            cell.updateCell(title:data.title ?? "Not Found", body: data.content ?? "No Body", imgurl: data.urlToImage ?? "https://en.wikipedia.org/wiki/Pages_(word_processor)#/media/File:Pages_Icon.png")
            
            newsurl = data.url
           
            return cell
            
        }
        
        
        return UITableViewCell()
        
    }
    
    
    
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        
        performSegue(withIdentifier: "segue", sender: self)
        
    }
    
    
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        
        if let destinationVC = segue.destination as? BrowserVC
        {
            destinationVC.url = newsurl
        }
        
    }

}

Model struct (generated using JSON to Swift website)

struct Welcome : Codable {
    let status : String?
    let totalResults : Int?
    let articles : [Articles]?

    enum CodingKeys: String, CodingKey {

        case status = "status"
        case totalResults = "totalResults"
        case articles = "articles"
    }

    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        status = try values.decodeIfPresent(String.self, forKey: .status)
        totalResults = try values.decodeIfPresent(Int.self, forKey: .totalResults)
        articles = try values.decodeIfPresent([Articles].self, forKey: .articles)
    }

}



struct Articles : Codable {
    let source : Source?
    let author : String?
    let title : String?
    let description : String?
    let url : String?
    let urlToImage : String?
    let publishedAt : String?
    let content : String?

    enum CodingKeys: String, CodingKey {

        case source = "source"
        case author = "author"
        case title = "title"
        case description = "description"
        case url = "url"
        case urlToImage = "urlToImage"
        case publishedAt = "publishedAt"
        case content = "content"
    }

    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        source = try values.decodeIfPresent(Source.self, forKey: .source)
        author = try values.decodeIfPresent(String.self, forKey: .author)
        title = try values.decodeIfPresent(String.self, forKey: .title)
        description = try values.decodeIfPresent(String.self, forKey: .description)
        url = try values.decodeIfPresent(String.self, forKey: .url)
        urlToImage = try values.decodeIfPresent(String.self, forKey: .urlToImage)
        publishedAt = try values.decodeIfPresent(String.self, forKey: .publishedAt)
        content = try values.decodeIfPresent(String.self, forKey: .content)
    }

}


// MARK: - Source
struct Source : Codable {
    let id : String?
    let name : String?

    enum CodingKeys: String, CodingKey {

        case id = "id"
        case name = "name"
    }

    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        id = try values.decodeIfPresent(String.self, forKey: .id)
        name = try values.decodeIfPresent(String.self, forKey: .name)
    }

}

TableView cell code

import UIKit

class HeadLinesCellTableViewCell: UITableViewCell {

    
    @IBOutlet weak var headlinesimageview: UIImageView!
    

    @IBOutlet weak var headlinestitlelbl: UILabel!
    
    @IBOutlet weak var headlinesbodylbl: UILabel!
    
    
    
    func updateCell(title: String,body: String,imgurl: String)
    {
        headlinestitlelbl.text = title
        headlinesbodylbl.text = body
        
        DispatchQueue.global(qos: .userInitiated).async {
            
            if let data = try? Data(contentsOf: URL(string: imgurl)!)
        {
                if let image = UIImage(data: data)
                {
                    DispatchQueue.main.async {
                        self.headlinesimageview.image = image
                    }
                }

            
        }
        
        
    }
    
  
    
    
}
}


    
          
                
                
      

      
            
            
            
            
                
                
      }
            
        
            
            
            
            
            
            
            
            

        
        
        
    
    
    

Upvotes: 0

Views: 61

Answers (1)

Rahul Umap
Rahul Umap

Reputation: 2859

Instead of this:

var articles: [Articles]!

Use this for declaration of articles:

var articles = [Articles]()

Update:

In your didSelectRow method do this:

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
   
    let article = articles[indexPath.row]
    performSegue(withIdentifier: "segue", sender: article)
    
}

Upvotes: 1

Related Questions