Reputation: 51
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.
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
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