2

I parse JSON in my application and some of the JSONs have nil values which I handled. However, the app still shows me the error that JSON contains nil values. My code:

struct Response : Decodable {
let articles: [Article]
 }

struct Article: Decodable {

let title: String
let description : String
let url : String
let urlToImage : String
}
URLSession.shared.dataTask(with: url) { (data, response, error) in
     guard let data = data else { return }
     do {
     let article =  try JSONDecoder().decode(Response.self , from : data)
     for i in 0...article.articles.count - 1 {
         if type(of: article.articles[i].title) == NSNull.self {
             beforeLoadNewsViewController.titleArray.append("")
         } else {
             beforeLoadNewsViewController.titleArray.append(article.articles[i].title)
         }
if type(of : article.articles[i].urlToImage) == NSNull.self {
                beforeLoadNewsViewController.newsImages.append(newsListViewController.newsImages[newsListViewController.newsIndex])

                    } else {
                        
                        
                    let url = URL(string: article.articles[i].urlToImage ?? "https://static.wixstatic.com/media/b77fe464cfc445da9003a5383a3e1acf.jpg")
                    
                        
                    let data = try? Data(contentsOf: url!)

                    if url != nil {

                    //make sure your image in this url does exist, otherwise unwrap in a if let check / try-catch
                        let img = UIImage(data: data!)
                    beforeLoadNewsViewController.newsImages.append(img!)

                    } else {
                beforeLoadNewsViewController.newsImages.append(newsListViewController.newsImages[newsListViewController.newsIndex])

                        }

This is the error running the app:

FC91DEECC1631350EFA71C9C561D).description], debugDescription: "Expected String value but found null instead.", underlyingError: nil))

Note:

This JSON works with other urls that don't have nil values.

and here is the json

{
    "status": "ok",
    "source": "entertainment-weekly",
    "sortBy": "top",
    "articles": [
    {
        "author": null,
        "title": "Hollywood's Original Gone Girl",
        "description": null,
        "url": "http://mariemcdonald.ew.com/",
        "urlToImage": null,
        "publishedAt": null
    },
    {
        "author": "Samantha Highfill",
        "title": "‘Supernatural’: Jensen Ackles, Jared Padalecki say the boys aren’t going ‘full-mope’",
        "description": "",
        "url": "http://ew.com/tv/2017/10/18/supernatural-jensen-ackles-jared-padalecki-season-13/",
        "urlToImage": "http://ewedit.files.wordpress.com/2017/10/supernatural-season-13-episode-1.jpg?crop=0px%2C0px%2C2700px%2C1417.5px&resize=1200%2C630",
        "publishedAt": "2017-10-18T17:23:54Z"
    },
    {
Community
  • 1
  • 1
Saeed Rahmatolahi
  • 1,317
  • 2
  • 27
  • 60

2 Answers2

16

The error is quite clear. JSONDecoder maps NSNull to nil so the decoder throws an error if it's going to decode a nil value to a non-optional type.

The solution is to declare all affected properties as optional.

let title: String
let description : String?
let url : String
let urlToImage : String?

or customize the decoder to replace nil with an empty string.

And because JSONDecoder maps NSNull to nil the check if ... == NSNull.self { is useless.

Edit:

Don't use ugly C-style index based loops use

 let response =  try JSONDecoder().decode(Response.self , from : data)
 for article in response.articles {
      beforeLoadNewsViewController.titleArray.append(article.title)
 }

PS: But why for heaven's sake do you map the article instance to – apparently – separate arrays? You got the Article instances which contain everything related to one article respectively.

vadian
  • 274,689
  • 30
  • 353
  • 361
0

I am sorry, I cannot comment yet. But why don't you test for "null" the way, you did at the URLSession data check, with the guard-statement?

It would then look like:

guard let title = article.articles[i].title else {
    beforeLoadNewsViewController.titleArray.append("")
    return
}
beforeLoadNewsViewController.titleArray.append(title)

You try to parse a JSON structure here, in case the JSON structure would not fit to your needs, you still expect it to have the according attributes.

Could you also provide your JSON structure and how your decoding

try JSONDecoder().decode(Response.self , from : data)

looks like?

  • I don't have any problems with other urls this son is for news api and in Entertainment Weekly api I have problem because the son contains nil values - one more thing I used your codes But the app won't allow me to run because of guard let line – Saeed Rahmatolahi Oct 17 '17 at 16:47
  • why not simply `beforeLoadNewsViewController.titleArray.append(article.articles[i].title ?? "")` or without the loop using map `beforeLoadNewsViewController.titleArray = article.articles.map{$0.title ?? ""} ` and if you don't want the empty strings in your array use `article.articles.flatMap{$0.title}` instead – Leo Dabus Oct 17 '17 at 16:47
  • I used beforeLoadNewsViewController.titleArray.append(article.artic‌​les[i].title ?? "") but no difference still there is problem – Saeed Rahmatolahi Oct 18 '17 at 10:36