I have json response that I need to parse that, according to the https://newsapi.org web site, should look like:
{
"status": "ok",
"totalResults": 4498,
"articles": [{
"source": {
"id": null,
"name": "Techweekeurope.co.uk"
},
"author": null,
"title": "Top ten cloud service providers for reliability and price",
"description": "In a time where the reliability and stability of cloud service providers comes into spotlight, we pick our top ten that will help you propel your business to the next level. We recently talked about building your own local network with a Raspberry Pi starter …",
"url": "https://www.techweekeurope.co.uk/top-ten-cloud-service-providers-reliability-price/",
"urlToImage": "https://www.techweekeurope.co.uk/wp-content/uploads/2020/01/Cloud-Service-Providers.gif",
"publishedAt": "2020-01-04T16:17:00Z",
"content": "In a time where the reliability and stability of cloud service providers comes into spotlight, we pick our top ten that will help you propel your business to the next level. We recently talked about building your own local network with a Raspberry Pi starter … [+4441 chars]"
}, ...]
}
I created structures for parse it
import Foundation
struct Article: Codable {
var source: Source
var author: String?
var title: String
var description: String
var url: URL
var urlToImage: URL?
var content: String
enum codingKeys: String, CodingKey {
case source
case author
case title
case description
case url
case urlToImage
case content
}
}
struct Source: Codable {
var name: String
}
struct Articles: Codable {
var articles: [Article]
}
And I created class network service class
class NetworkService {
// MARK: - Methods
func parseJSON(from url: URL) {
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
do {
let decoder = JSONDecoder()
let articleData = try decoder.decode(Articles.self, from: data!)
print(articleData.articles.description)
} catch {
print(error)
}
}
task.resume()
}
}
In console I have this:
keyNotFound(CodingKeys(stringValue: "articles", intValue: nil), Swift.DecodingError.Context(codingPath: [], debugDescription: "No value associated with key CodingKeys(stringValue: \"articles\", intValue: nil) (\"articles\").", underlyingError: nil))
Your error is telling you that the JSON was valid, but that it was unable to find any articles
key in the response.
I’d suggest including status
and message
in your definition of Articles
and make articles
property an optional. The status
might not be "ok"
. Regardless, articles
is obviously absent.
struct Articles: Codable {
let articles: [Article]?
let status: String
let message: String?
let code: String?
}
For example, if you don’t supply a valid key, you’ll get a response like
{
"status": "error",
"code": "apiKeyInvalid",
"message": "Your API key is invalid or incorrect. Check your key, or go to https://newsapi.org to create a free API key."
}
You may want to handle the case where articles
may be absent if there is an error, by making it optional.
Unrelated, but the forced unwrapping operator for data
is dangerous. If you have some network error, your app will crash. I’d suggest unwrapping it, e.g.:
func parseJSON(from url: URL) {
let task = URLSession.shared.dataTask(with: url) { data, response, error in
guard let data = data, error == nil else {
print(error ?? "Unknown error")
return
}
do {
let articleData = try JSONDecoder().decode(Articles.self, from: data)
guard let articles = articleData.articles else {
print("No articles", articleData.status, articleData.message ?? "No message")
return
}
for article in articles {
print(article.description)
}
} catch {
print(error)
}
}
task.resume()
}