Search code examples
jsonswiftcodable

How to decode struct in swift


I have this JsonResponse from the server

{
  "status": "ok",
  "totalResults": 0,
  "articles": [
    {
      "title": "United places order for 200 Boeing planes, giving two troubled jets a vote of confidence - CNN",
      "author": "Chris Isidore",
      "source": {
        "Id": "cnn",
        "Name": "CNN"
      },
      "publishedAt": "2022-12-13T14:33:00Z",
      "url": "https://www.cnn.com/2022/12/13/business/united-boeing-order/index.html"
    },
    {
      "title": "November CPI: Inflation rose at annual 7.1% over last year - Yahoo Finance",
      "author": "Alexandra Semenova",
      "source": {
        "Id": null,
        "Name": "Yahoo Entertainment"
      },
      "publishedAt": "2022-12-13T14:17:04Z",
      "url": "https://finance.yahoo.com/news/november-cpi-inflation-rises-71-over-last-year-141704528.html"
    },
    {
      "title": "Gold prices jump higher as U.S. CPI rises 7.1% in November - Kitco NEWS",
      "author": "http://www.facebook.com/kitconews",
      "source": {
        "Id": null,
        "Name": "Kitco NEWS"
      },
      "publishedAt": "2022-12-13T13:37:00Z",
      "url": "https://www.kitco.com/news/2022-12-13/prices-jump-higher-as-U-S-CPI-rises-7-1-in-November.html"
    },
]

And here is my model struct

struct Article: Codable {
    let articles: [ArticleElement]
}

// MARK: - ArticleElement
struct ArticleElement: Codable {
    let title: String
    let author: String?
    let source: Source
    let publishedAt: Date
    let url: String
}

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

    enum CodingKeys: String, CodingKey {
        case id = "Id"
        case name = "Name"
    }
}

I get the error that the codingKey ("articles") is not found, here's the full error message:

keyNotFound(CodingKeys(stringValue: "articles", intValue: nil), Swift.DecodingError.Context(codingPath: [], debugDescription: "No value associated with key CodingKeys(stringValue: "articles", intValue: nil) ("articles").", underlyingError: nil))

I have tried to fetch this API and it's returning this error. I have all the required keys and Idk why I am receiving this error.

here is the API I am fetching "https://newsapi.org/v2/top-headlines?country=us&category=business&apiKey=(apiKey)" Here's the function that does the API call to the server and decodes the JSON

func fetch<T: Decodable>(request: Request, type:T.Type,
                     callback: @escaping (Result<T, Error>)->Void) {
        var urlComponnets = URLComponents(string: request.baseUrl + request.path)
        var queryItems:[URLQueryItem] = []
        for (k, v) in request.params {
            queryItems.append( URLQueryItem(name: k, value: v))
        }
        urlComponnets?.queryItems = queryItems

        let urlsesson = URLSession.shared
        guard let url = urlComponnets?.url else { return }
        let urlReuqest = URLRequest(url: url)

         let dataTask = urlsesson.dataTask(with: urlReuqest) { data, responce, error in
            let jsonDecoder = JSONDecoder()
             jsonDecoder.dataDecodingStrategy = .deferredToData
             do {
                 let responce = try jsonDecoder.decode(type, from: data!)
                 callback(.success(responce))
             } catch {
                 callback(.failure(error))
             }
        }
        dataTask.resume()
    }

And I am calling the fetch function from the viewModel as such

func getArticles(){
        let request = Request(baseUrl:"https://newsapi.org", path:"/v2/top-headlines?country=us&category=business&apiKey=\(ApiKey)", params: [:], type: "GET", header: [:])
        
    
        networkManager.fetch(request: request, type: Article.self) { result in
            switch result {
            case .success(let article):
               
                self.articles = article.articles
                
            case .failure(let error):
                print(error)
                
            }
        }
        
    }

where

var articles: [ArticleElement] = []

Solution

  • Solved by passing all parameters using the params argument (instead of hardcoding them in the path argument):

    let request = Request(baseUrl:"https://newsapi.org", path:"/v2/top-headlines?", params: ["country":"us", "category":"business", "apiKey":"ee55178ed2fb43b0a2a721d43d99a5b0"], type: "GET", header: [:])