Attempt at utilizing decodable - json

I am trying to create decodable structs for the JSON that I am receiving, but I keep getting:
keyNotFound(CodingKeys(stringValue: "inquiry_quantity", intValue: nil), Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "orders", intValue: nil), _JSONKey(stringValue: "Index 0", intValue: 0)], debugDescription: "No value associated with key CodingKeys...
This is the JSON that I am running through my decoder:
{
"orders_important": 2,
"orders_inquiry": 2,
"orders": [
{
"information": {
"total_price": 12.0,
"order_stage": "CONFIRMED",
"total_cases": 333,
"scanned": false,
"inquiry": false,
"foodhub": "shdw.fh1#agg.com",
"identifier": "GPO00024",
"delivery_date": "2019-11-04"
},
"details": {
"pack_size": "40#",
"cases": 333,
"plu_code": 12434,
"crop_plan": 1633,
"commodity": "Conventional Bananas, , 40#"
}
},
{
"information": {
"inquiree": "FARMER",
"total_price": 23.5,
"order_stage": "INQUIRY",
"inquiry_quantity": 0,
"total_cases": 56,
"scanned": false,
"inquiry": true,
"foodhub": "shdw.fh1#agg.com",
"inquiry_type": "PRICE",
"identifier": "GPO00027",
"inquiry_price": 26.5,
"delivery_date": "2019-11-05",
"inquiry_details": "You have requested $26.5 per case, instead of $23.5."
},
"details": {
"pack_size": "40#",
"cases": 12,
"plu_code": 12434,
"crop_plan": 1589,
"commodity": "Conventional Bananas, , 40#"
}
},
],
"orders_count": 4
}
My decodable structs: (EDIT: Removed Init)
struct OrderBundle: Decodable{
let orders_important : Int
let orders_inquiry:Int
let orders_count: Int
let orders: [Order]
}
struct Order: Decodable {
let inquiry_quantity : Int
let inquiry_price : Int
let inquiry_info : String
let price : Int
let cropPlan : Int
let identifier: String
let deliveryDate : String
let quantity : Int
let unit : String
let status : String
let destination : String
let commodity: String
}
And lastly calling my decoder:
let orderData = try
JSONDecoder().decode(OrderBundle.self, from: data!)
print(orderData)
I have tried it with and without the Init in my Orders and both ways I am still getting the same error.

The main issue is that any dictionary must be decoded to an extra struct if init(from decoder is not provided. So basically the structure of the model must be
struct OrderBundle: Decodable {
struct Information : Decodable {
struct Details : Decodable {
The complete structs are as follows, many struct members must be declared as optional and inquiryPrice is Double, not Int. convertFromSnakeCase converts the snaked_cased keys to camelCased struct members.
struct OrderBundle : Decodable{
let ordersImportant : Int
let ordersInquiry : Int
let ordersCount : Int
let orders : [Order]
}
struct Order : Decodable {
let information : Information
let details : Details
struct Information : Decodable {
let inquiryQuantity : Int?
let inquiryPrice : Double?
let inquiryInfo : String?
let price : Int?
let cropPlan : Int?
let identifier : String
let deliveryDate : String
let quantity : Int?
let unit : String?
let status : String?
let destination : String?
let commodity: String?
}
struct Details : Decodable {
let packSize : String
let cases : Int
let pluCode : Int
let cropPlan : Int
let commodity : String
}
}
And the code to decode it
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let orderData = try decoder.decode(OrderBundle.self, from: data!)
print(orderData)

Related

JSONDecoder returning nil while parsing

I have the following function:
func executeGet( completion: #escaping (Data?, Error?) -> Void) {
AF.request("https:URL",
method:.get,
headers:headers).response{ response in
debugPrint(response)
if let error = response.error {
completion(nil, error)
}
else if let jsonArray = response.value as? Data{
completion(jsonArray, nil)
}
}
}
Which is being called as follows:
executeGet() { (json, error) in
if let error = error{
print(error.localizedDescription)
}
else if let json = json {
print(type(of:json))
print(json)
let welcome = try? JSONDecoder().decode(Welcome.self, from: json)
print(welcome)
}
}
But for some reason, my 'welcome' value always returns nil. Can anyone suggest what could've gone wrong? When I print(json) I'm getting '294 Bytes' for some reason so clearly something went wrong before decoding, right?
EDIT: Upon Udi's request here's the Welcome struct
// MARK: - Welcome
struct Welcome: Codable {
let statusCode: Int
let messageCode: String
let result: Result
}
// MARK: - Result
struct Result: Codable {
let id: String
let inputParameters: InputParameters
let robotID: String
let runByUserID, runByTaskMonitorID: JSONNull?
let runByAPI: Bool
let createdAt, startedAt, finishedAt: Int
let userFriendlyError: JSONNull?
let triedRecordingVideo: Bool
let videoURL: String
let videoRemovedAt: Int
let retriedOriginalTaskID: String
let retriedByTaskID: JSONNull?
let capturedDataTemporaryURL: String
let capturedTexts: CapturedTexts
let capturedScreenshots: CapturedScreenshots
let capturedLists: CapturedLists
enum CodingKeys: String, CodingKey {
case id, inputParameters
case robotID = "robotId"
case runByUserID = "runByUserId"
case runByTaskMonitorID = "runByTaskMonitorId"
case runByAPI, createdAt, startedAt, finishedAt, userFriendlyError, triedRecordingVideo
case videoURL = "videoUrl"
case videoRemovedAt
case retriedOriginalTaskID = "retriedOriginalTaskId"
case retriedByTaskID = "retriedByTaskId"
case capturedDataTemporaryURL = "capturedDataTemporaryUrl"
case capturedTexts, capturedScreenshots, capturedLists
}
}
// MARK: - CapturedLists
struct CapturedLists: Codable {
let companies: [Company]
}
// MARK: - Company
struct Company: Codable {
let position, name, location, description: String
enum CodingKeys: String, CodingKey {
case position = "Position"
case name, location, description
}
}
// MARK: - CapturedScreenshots
struct CapturedScreenshots: Codable {
}
// MARK: - CapturedTexts
struct CapturedTexts: Codable {
let productName, width, patternRepeat, construction: String
let fiber: String
let color: JSONNull?
let mainImage: String
enum CodingKeys: String, CodingKey {
case productName = "Product Name"
case width = "Width"
case patternRepeat = "Pattern Repeat"
case construction = "Construction"
case fiber = "Fiber"
case color = "Color"
case mainImage = "Main Image"
}
}
// MARK: - InputParameters
struct InputParameters: Codable {
let originURL: String
let companiesSkip, companiesLimit: Int
enum CodingKeys: String, CodingKey {
case originURL = "originUrl"
case companiesSkip = "companies_skip"
case companiesLimit = "companies_limit"
}
}
// MARK: - Encode/decode helpers
class JSONNull: Codable, Hashable {
public static func == (lhs: JSONNull, rhs: JSONNull) -> Bool
{
return true
}
public var hashValue: Int {
return 0
}
public init() {}
public required init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
if !container.decodeNil() {
throw DecodingError.typeMismatch(JSONNull.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for JSONNull"))
}
}
public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try container.encodeNil()
}
}
and here's a sample of JSON response
{
"statusCode": 200,
"messageCode": "success",
"result": {
"id": "f6fb62b6-f06a-4bf7-a623-c6a35c2e70b0",
"inputParameters": {
"originUrl": "https://www.ycombinator.com/companies/airbnb",
"companies_skip": 0,
"companies_limit": 10
},
"robotId": "4f5cd7ff-6c98-4cac-8cf0-d7d0cb050b06",
"runByUserId": null,
"runByTaskMonitorId": null,
"runByAPI": true,
"createdAt": 1620739118,
"startedAt": 1620739118,
"finishedAt": 1620739118,
"userFriendlyError": null,
"triedRecordingVideo": true,
"videoUrl": "https://prod-browseai-captured-data.s3.amazonaws.com/1fae674a-2788-46a8-83c8-95c4664c6d25/6326b3c1-7b16-4256-a323-7d8d8954bd4e/1061671f-7f71-42ac-bb9a-207d126d1f3a/system-1620230966-b1a9688b-05d3-4682-beeb-9ce035e482b1.mp4",
"videoRemovedAt": 1620739118,
"retriedOriginalTaskId": "673da019-bf0c-476e-9c4f-d35252a151dc",
"retriedByTaskId": null,
"capturedDataTemporaryUrl": "https://prod-browseai-captured-data.s3.amazonaws.com/1fae674a-2788-46a8-83c8-95c4664c6d25/6326b3c1-7b16-4256-a323-7d8d8954bd4e/1061671f-7f71-42ac-bb9a-207d126d1f3a/system-1620230966-b1a9688b-05d3-4682-beeb-9ce035e482b1.json?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=ASIAQVG3TPBVXHSCAX63%2F20221031%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20221031T185642Z&X-Amz-Expires=1800&X-Amz-Security-Token=IQoJb3JpZ2luX2VjEJP%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FwEaCXVzLWVhc3QtMSJIMEYCIQDfX8VNAl5kBgttrCU85U5wc1ZtSOmshO6%2FPilXOv8nvgIhAIveFfsk%2B2CnEkrMZWriodEPsj0osO5a5zV6eVu%2FXfuZKp8DCHwQAhoMMDQ1NTU3NzA4OTA3IgyrbhVK0MP1WMFBXh0q%2FAJulP5qfaV5mn3NRbINqZN4hy4Dg3IujNrZjw8ef32sWE1Gj2D%2Fc0YTJUzvx%2Fnm7LxyNO6AR35mrVy%2FBm9Q80UIspkcLMl45EK%2FoUDO0fAvoUF8g6iZ905qS3MvnOTxXkObhM1PVmpFeJFMw3jksnOPfKE4X7Ut%2FJXNwD%2F5QzdkQCXkGem%2BlrYSSSf8jB8lihTAjT%2FNXmOKMv3jktmZ13T8J1R8F8zeuLPMQf7QphUzlKn5joPb28cConluQC97y%2BjwxqIYjvIFKXY9cZEoaHGh4c6FbXsia714zG3CQp8NSGLbqCCu93oJI1Z61E%2BZ6PhB3vZGdBvXi61AlJcxZ7sti6i0h4VAbWspiJIgWwoZzrsTtneBNNpUW9tvtacGgEZIwAKV%2F3AhVEZu3WC1eQ9HtfjT9%2FjW99SEB8VVGXwkM%2FA9mtT%2FuiL0cAfQZRMhtbQJXXDRdkYEw%2FWuhjJ3zxEtEB2m3uH%2B%2BUEzOzGTd5Knm%2Bero%2BhMfN8X%2Botm3DDbtICbBjqcAf5Riii0XE1w2TZvpm%2FPNHTchCu7FnNz5hfvflv8scpgO5M4bGpy%2FadI4%2F7AUQqCQXFw4scF0FCCdb8AKJZsFGG18W1jjDHyR0YuxZFQ%2FJQRt0JP3yr%2BkVxjAH7qTtc0AzF%2FnGTgy3MOF%2Bm6Y7EkyCWyV2r6o1JTBQMftlf7MI8Uvw4cSZE6JoZviaFtmKVLGGgR4F3cDiyU56augA%3D%3D&X-Amz-Signature=a7bb4d7597ad37cdf1f260890c3c474f7f49334db58c9650d75302a34126f7bc&X-Amz-SignedHeaders=host",
"capturedTexts": {
"Product Name": "Alexis",
"Width": "15",
"Pattern Repeat": "PATTERN REPEAT",
"Construction": "Hand woven",
"Fiber": "100% Wool",
"Color": null,
"Main Image": "https://isteam.wsimg.com/ip/e31f7bba-252b-4669-9209-639d1c00765d/ols/258_original"
},
"capturedScreenshots": {
"top-ads": {
"id": "b4d132f3-12d9-4770-ac7d-88e481fc5b47",
"name": "Top ads",
"src": "https://prod-browseai-captured-data.s3.amazonaws.com/1fae674a-2788-46a8-83c8-95c4664c6d25/6326b3c1-7b16-4256-a323-7d8d8954bd4e/1061671f-7f71-42ac-bb9a-207d126d1f3a/00001-user-1620230947-6f113cf2-90ef-4c66-a448-9d5c6bd64873.png",
"width": 600,
"height": 120,
"x": 201,
"y": 142,
"deviceScaleFactor": 1.2,
"full": "page",
"comparedToScreenshotId": "29d742c2-6f45-4f29-9d48-ba6fe66e6e3d",
"diffImageSrc": "https://prod-browseai-captured-data.s3.amazonaws.com/1fae674a-2788-46a8-83c8-95c4664c6d25/6326b3c1-7b16-4256-a323-7d8d8954bd4e/1061671f-7f71-42ac-bb9a-207d126d1f3a/00001-user-1620230947-6f113cf2-90ef-4c66-a448-9d5c6bd64873.png",
"changePercentage": 20,
"diffThreshold": 5,
"fileRemovedAt": 1620739118
}
},
"capturedLists": {
"companies": [
{
"Position": "1",
"name": "Airbnb",
"location": "San Francisco, CA, USA",
"description": "Book accommodations around the world."
},
{
"Position": "2",
"name": "Coin base",
"location": "San Francisco, CA, USA",
"description": "Buy, sell, and manage crypto currencies."
},
{
"Position": "3",
"name": "DoorDash",
"location": "San Francisco, CA, USA",
"description": "Restaurant delivery."
}
]
}
}
}
EDIT2: Upon Rob's suggestion, I tried do-try-catch, as follows:
executeGet() { (json, error) in
if let error = error{
print(error.localizedDescription)
}
else if let json = json {
print(type(of:json)) // Data
print(json) // 2479 Bytes
do{
var welcome = try JSONDecoder().decode(Welcome.self, from: json)
print(welcome)
}
catch {
print(error)
}
}
}
Which reports the error:
keyNotFound(CodingKeys(stringValue: "companies_skip", intValue: nil), Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "result", intValue: nil), CodingKeys(stringValue: "inputParameters", intValue: nil)], debugDescription: "No value associated with key CodingKeys(stringValue: "companies_skip", intValue: nil) ("companies_skip").", underlyingError: nil))
The string response you show in your comment, means you get a valid response from the server,
and so you should be able to decode it with the following models.
Use #vadian answer to your previous question :
Unable to parse JSON data properly from Alomafire
Here are the test code and models to decode the response into a set of structs.
Note you will have to consult the server doc to determine which properties are Optional and adjust the code (i,e put ?)
where nessesary .
struct ContentView: View {
#State var welcome: WelcomeResponse?
var body: some View {
VStack {
if let response = welcome {
Text(response.messageCode)
Text("\(response.statusCode)")
ForEach(response.result.capturedLists.companies) { item in
Text(item.description)
}
}
}
.onAppear {
let json = """
{
"statusCode": 200,
"messageCode": "success",
"result": {
"id": "f6fb62b6-f06a-4bf7-a623-c6a35c2e70b0",
"inputParameters": {
"originUrl": "https://www.ycombinator.com/companies/airbnb",
"companies_skip": 0,
"companies_limit": 10
},
"robotId": "4f5cd7ff-6c98-4cac-8cf0-d7d0cb050b06",
"runByUserId": null,
"runByTaskMonitorId": null,
"runByAPI": true,
"createdAt": 1620739118,
"startedAt": 1620739118,
"finishedAt": 1620739118,
"userFriendlyError": null,
"triedRecordingVideo": true,
"videoUrl": "https://prod-browseai-captured-data.s3.amazonaws.com/1fae674a-2788-46a8-83c8-95c4664c6d25/6326b3c1-7b16-4256-a323-7d8d8954bd4e/1061671f-7f71-42ac-bb9a-207d126d1f3a/system-1620230966-b1a9688b-05d3-4682-beeb-9ce035e482b1.mp4",
"videoRemovedAt": 1620739118,
"retriedOriginalTaskId": "673da019-bf0c-476e-9c4f-d35252a151dc",
"retriedByTaskId": null,
"capturedDataTemporaryUrl": "https://prod-browseai-captured-data.s3.amazonaws.com/1fae674a-2788-46a8-83c8-95c4664c6d25/6326b3c1-7b16-4256-a323-7d8d8954bd4e/1061671f-7f71-42ac-bb9a-207d126d1f3a/system-1620230966-b1a9688b-05d3-4682-beeb-9ce035e482b1.json?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=ASIAQVG3TPBVXHSCAX63%2F20221031%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20221031T185642Z&X-Amz-Expires=1800&X-Amz-Security-Token=IQoJb3JpZ2luX2VjEJP%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FwEaCXVzLWVhc3QtMSJIMEYCIQDfX8VNAl5kBgttrCU85U5wc1ZtSOmshO6%2FPilXOv8nvgIhAIveFfsk%2B2CnEkrMZWriodEPsj0osO5a5zV6eVu%2FXfuZKp8DCHwQAhoMMDQ1NTU3NzA4OTA3IgyrbhVK0MP1WMFBXh0q%2FAJulP5qfaV5mn3NRbINqZN4hy4Dg3IujNrZjw8ef32sWE1Gj2D%2Fc0YTJUzvx%2Fnm7LxyNO6AR35mrVy%2FBm9Q80UIspkcLMl45EK%2FoUDO0fAvoUF8g6iZ905qS3MvnOTxXkObhM1PVmpFeJFMw3jksnOPfKE4X7Ut%2FJXNwD%2F5QzdkQCXkGem%2BlrYSSSf8jB8lihTAjT%2FNXmOKMv3jktmZ13T8J1R8F8zeuLPMQf7QphUzlKn5joPb28cConluQC97y%2BjwxqIYjvIFKXY9cZEoaHGh4c6FbXsia714zG3CQp8NSGLbqCCu93oJI1Z61E%2BZ6PhB3vZGdBvXi61AlJcxZ7sti6i0h4VAbWspiJIgWwoZzrsTtneBNNpUW9tvtacGgEZIwAKV%2F3AhVEZu3WC1eQ9HtfjT9%2FjW99SEB8VVGXwkM%2FA9mtT%2FuiL0cAfQZRMhtbQJXXDRdkYEw%2FWuhjJ3zxEtEB2m3uH%2B%2BUEzOzGTd5Knm%2Bero%2BhMfN8X%2Botm3DDbtICbBjqcAf5Riii0XE1w2TZvpm%2FPNHTchCu7FnNz5hfvflv8scpgO5M4bGpy%2FadI4%2F7AUQqCQXFw4scF0FCCdb8AKJZsFGG18W1jjDHyR0YuxZFQ%2FJQRt0JP3yr%2BkVxjAH7qTtc0AzF%2FnGTgy3MOF%2Bm6Y7EkyCWyV2r6o1JTBQMftlf7MI8Uvw4cSZE6JoZviaFtmKVLGGgR4F3cDiyU56augA%3D%3D&X-Amz-Signature=a7bb4d7597ad37cdf1f260890c3c474f7f49334db58c9650d75302a34126f7bc&X-Amz-SignedHeaders=host",
"capturedTexts": {
"Product Name": "Alexis",
"Width": "15",
"Pattern Repeat": "PATTERN REPEAT",
"Construction": "Hand woven",
"Fiber": "100% Wool",
"Color": null,
"Main Image": "https://isteam.wsimg.com/ip/e31f7bba-252b-4669-9209-639d1c00765d/ols/258_original"
},
"capturedScreenshots": {
"top-ads": {
"id": "b4d132f3-12d9-4770-ac7d-88e481fc5b47",
"name": "Top ads",
"src": "https://prod-browseai-captured-data.s3.amazonaws.com/1fae674a-2788-46a8-83c8-95c4664c6d25/6326b3c1-7b16-4256-a323-7d8d8954bd4e/1061671f-7f71-42ac-bb9a-207d126d1f3a/00001-user-1620230947-6f113cf2-90ef-4c66-a448-9d5c6bd64873.png",
"width": 600,
"height": 120,
"x": 201,
"y": 142,
"deviceScaleFactor": 1.2,
"full": "page",
"comparedToScreenshotId": "29d742c2-6f45-4f29-9d48-ba6fe66e6e3d",
"diffImageSrc": "https://prod-browseai-captured-data.s3.amazonaws.com/1fae674a-2788-46a8-83c8-95c4664c6d25/6326b3c1-7b16-4256-a323-7d8d8954bd4e/1061671f-7f71-42ac-bb9a-207d126d1f3a/00001-user-1620230947-6f113cf2-90ef-4c66-a448-9d5c6bd64873.png",
"changePercentage": 20,
"diffThreshold": 5,
"fileRemovedAt": 1620739118
}
},
"capturedLists": {
"companies": [
{
"Position": "1",
"name": "Airbnb",
"location": "San Francisco, CA, USA",
"description": "Book accommodations around the world."
},
{
"Position": "2",
"name": "Coin base",
"location": "San Francisco, CA, USA",
"description": "Buy, sell, and manage crypto currencies."
},
{
"Position": "3",
"name": "DoorDash",
"location": "San Francisco, CA, USA",
"description": "Restaurant delivery."
}
]
}
}
}
"""
// simulated API data from the server
let data = json.data(using: .utf8)!
do {
let results = try JSONDecoder().decode(WelcomeResponse.self, from: data)
welcome = results
print("\n---> results: \(results) \n")
} catch {
print("\n---> decoding error: \n \(error)\n")
}
}
}
}
// MARK: - WelcomeResponse
struct WelcomeResponse: Codable {
let statusCode: Int
let messageCode: String
let result: Result
}
// MARK: - Result
struct Result: Codable {
let id: String
let inputParameters: InputParameters
let robotID: String
let runByUserID, runByTaskMonitorID: String?
let runByAPI: Bool
let createdAt, startedAt, finishedAt: Int
let userFriendlyError: String?
let triedRecordingVideo: Bool
let videoURL: String
let videoRemovedAt: Int
let retriedOriginalTaskID: String
let retriedByTaskID: String?
let capturedDataTemporaryURL: String
let capturedTexts: CapturedTexts
let capturedScreenshots: CapturedScreenshots
let capturedLists: CapturedLists
enum CodingKeys: String, CodingKey {
case id, inputParameters
case robotID = "robotId"
case runByUserID = "runByUserId"
case runByTaskMonitorID = "runByTaskMonitorId"
case runByAPI, createdAt, startedAt, finishedAt, userFriendlyError, triedRecordingVideo
case videoURL = "videoUrl"
case videoRemovedAt
case retriedOriginalTaskID = "retriedOriginalTaskId"
case retriedByTaskID = "retriedByTaskId"
case capturedDataTemporaryURL = "capturedDataTemporaryUrl"
case capturedTexts, capturedScreenshots, capturedLists
}
}
// MARK: - CapturedLists
struct CapturedLists: Codable {
let companies: [Company]
}
// MARK: - Company
struct Company: Identifiable, Codable {
let id = UUID()
let position, name, location, description: String
enum CodingKeys: String, CodingKey {
case position = "Position"
case name, location, description
}
}
// MARK: - CapturedScreenshots
struct CapturedScreenshots: Codable {
let topAds: TopAds
enum CodingKeys: String, CodingKey {
case topAds = "top-ads"
}
}
// MARK: - TopAds
struct TopAds: Codable {
let id, name: String
let src: String
let width, height, x, y: Int
let deviceScaleFactor: Double
let full, comparedToScreenshotId: String
let diffImageSrc: String
let changePercentage, diffThreshold, fileRemovedAt: Int
}
// MARK: - CapturedTexts
struct CapturedTexts: Codable {
let productName, width, patternRepeat, construction: String
let fiber: String
let color: String?
let mainImage: String
enum CodingKeys: String, CodingKey {
case productName = "Product Name"
case width = "Width"
case patternRepeat = "Pattern Repeat"
case construction = "Construction"
case fiber = "Fiber"
case color = "Color"
case mainImage = "Main Image"
}
}
// MARK: - InputParameters
struct InputParameters: Codable {
let originUrl: String
let companiesSkip: Int?
let companiesLimit: Int?
enum CodingKeys: String, CodingKey {
case originUrl
case companiesSkip = "companies_skip"
case companiesLimit = "companies_limit"
}
}
Your error was reportedly:
keyNotFound(CodingKeys(stringValue: "companies_skip", intValue: nil), Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "result", intValue: nil), CodingKeys(stringValue: "inputParameters", intValue: nil)], debugDescription: "No value associated with key CodingKeys(stringValue: "companies_skip", intValue: nil) ("companies_skip").", underlyingError: nil))
That points you precisely to where the decoding failed. There is apparently no key called companies_skip in result » inputParameters. Now, you don't show us the full response you actually received, so it is hard to be precise. But we can infer from this error that the response does not precisely match your sample JSON, but rather, the companies_skip key is not present.
We might infer from the name, inputParameters, that your request URL (which, again, you have not shared with us) may possibly need to supply that parameter. Or, alternatively, perhaps that parameter name shouldn't be marked as a required sub key of the inputParameters structure (e.g., you might want to make it an optional).
Regardless of the particulars, this is the process. If decoding fails, look at the complete error object, and it will tell you where it had problems. Note, if there are multiple decoding problems, the error will only report the first one, so do not be surprised if it takes a few times and different queries to resolve all of the issues. The first time you start decoding a particular request, resolving all of the potential discrepancies may be an iterative process.

Why is my decoder not able to decode my api call with Alamofire in swift

So I'm trying to to process my API call data into an object I'm calling PlaceModel.
The API returns a lot of data; but I only need name, and coordinate data for my purposes at the moment.
example of API data
--- redacted for brevity ---
{
"html_attributions" : [],
"next_page_token" : "AeJbb3dLZZ4oYIangr8wO9j6SE4_5L1NcYQC-qCKt1Y0saPFOzNc90E7bG8IDtj8DNlEeM6nWlClZgFd-YMtB4kGQYBLexZgjlX-nH9rN4UQHk9hk-Ha9zLpjY0GYiUsayMZKW5x7oAkQOXlmbARFxu2tR-dW5VB_dd0VkJkf7_6zeGeeje0UjQeqdk1Czr-gBgNxzmMj0adrj2_G0CxeJxk5eN645sdbZ8QJ-BChqkUegJ8l9V7dO4nRCfPz5u_paqoCEcVOJqYMyiZsc3ibBxNowY-uH2PCbpyXcM186CenqBIG088TiyulBFL4Dq1zDxXbZ5asSH4r-u8UtWpdpEcpzAJMjIT4W64vvXYZUT3_918VgENIyCEkPQVebjZUF0X2USykC-ZdlsB2BMvxjKlwx5Q_7_yKYy3Gnxq6T6jTgGisAvNXQ",
"results" : [
{
"business_status" : "OPERATIONAL", // FIRST BUSINESS
"geometry" : {
"location" : {
"lat" : 49.24665,
"lng" : -122.7504486 // HERE IS SOME LOCATION INFO
},
"viewport" : {
"northeast" : {
"lat" : 49.24808953029149,
"lng" : -122.7490256697085
},
"southwest" : {
"lat" : 49.2453915697085,
"lng" : -122.7517236302915
}
}
},
"icon" : "https://maps.gstatic.com/mapfiles/place_api/icons/v1/png_71/school-71.png",
"icon_background_color" : "#7B9EB0",
"icon_mask_base_uri" : "https://maps.gstatic.com/mapfiles/place_api/icons/v2/school_pinlet",
"name" : "Ascension Martial Arts", // HERE IS A NAME
"photos" : [
{
"height" : 1668,
"html_attributions" : [
"\u003ca href=\"https://maps.google.com/maps/contrib/115539551484221381565\"\u003eAscension Martial Arts\u003c/a\u003e"
],
"photo_reference" : "AeJbb3dunz49PLKAwwhcbLid5d2yWHftgLOY5sdMPLXhCKohiOcfvinKkmFxIht8ZBSNgxTFVJCCbnHxOG7ROlt093iHntMlX4O80ihQ0MI80u7u_TvvKMcwu3os0tgfr84KaNVP1KzNrzTiS1GUF0S82OM43NEuWoMjBW5o2PmhH-Ke-tj4",
"width" : 2500
}
],
"place_id" : "ChIJST1805B2hlQRASo6CA1ejKw",
"plus_code" : {
"compound_code" : "66WX+MR Port Coquitlam, BC, Canada",
"global_code" : "84XV66WX+MR"
},
"rating" : 4.9,
"reference" : "ChIJST1805B2hlQRASo6CA1ejKw",
"scope" : "GOOGLE",
"types" : [ "gym", "health", "point_of_interest", "establishment" ],
"user_ratings_total" : 107,
"vicinity" : "109-1320 Kingsway Avenue, Port Coquitlam"
},
{
"business_status" : "OPERATIONAL", // NEXT BUSINESS
--- redacted for brevity ---
So I really only need those 2 pieces of data for marking spots on my map.
Now I'm just trying to debug it and get it to work making Struct objects but I'm stuck.
networkingLayerAF.swift
import Foundation
import Alamofire
class NetworkingLayerAF{
func getPlaces() {
let longitudeX = "-122.735960"
let latitudeY = "49.252400"
let url = URL(string:"https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=\(latitudeY),\(longitudeX)&radius=150000&types=gym&key=NFS")!
// AF.request(url).response{ response in
// debugPrint(response)
// }
print("LAUNCH ALAMO")
AF.request(url).responseDecodable(of: [PlaceModel].self){ response in
switch response.result {
case .success(let items):
print(items)
case .failure(let error):
print(error.localizedDescription)
}
}
}//
}// end class
Error I'm getting when it runs in console.
LAUNCH ALAMO
2022-07-31 17:06:24.217509-0700 MyAPP[14661:1355709] [boringssl] boringssl_metrics_log_metric_block_invoke(153) Failed to log metrics
Response could not be decoded because of error:
The data couldn’t be read because it isn’t in the correct format.
PlaceModel
import Foundation
struct PlaceModel: Decodable {
var name: String
var coordinate: Coordinate
enum CodingKeys: String, CodingKey {
case name = "name"
case coordinate
}
}
struct Coordinate {
var latitude: Double
var longitude: Double
enum CodingKeys: String, CodingKey {
case latitude
case longitude
}
}
extension Coordinate: Decodable {
public init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: Coordinate.CodingKeys.self)
latitude = try values.decode(Double.self, forKey: .latitude)
longitude = try values.decode(Double.self, forKey: .longitude)
}
}
Earlier I was trying to use this websites json to swift converter which gave me a different looking struct class
placeModel - I added quite a few decodable tags onto the converter's code.
import Foundation
import CoreLocation
struct Welcome1: Decodable{
let nextPageToken: String
let results: [Result]
let status: String
}
// MARK: - Result
struct Result : Decodable{
let businessStatus: BusinessStatus
let geometry: Geometry
let icon: String
let iconBackgroundColor: IconBackgroundColor
let iconMaskBaseURI: String
let name: String
let photos: [Photo]?
let placeID: String
let plusCode: PlusCode
let rating: Double?
let reference: String
let scope: Scope
let types: [TypeElement]
let userRatingsTotal: Int?
let vicinity: String
let openingHours: OpeningHours?
}
enum BusinessStatus: CodingKey , Decodable{
case operational
}
// MARK: - Geometry
struct Geometry : Decodable {
let location: Location
let viewport: Viewport
}
// MARK: - Location
struct Location : Decodable{
let lat, lng: Double
}
// MARK: - Viewport
struct Viewport : Decodable{
let northeast, southwest: Location
}
enum IconBackgroundColor: CodingKey, Decodable {
case the7B9Eb0
}
// MARK: - OpeningHours
struct OpeningHours : Decodable{
let openNow: Bool
}
// MARK: - Photo
struct Photo : Decodable{
let height: Int
let htmlAttributions: [String]
let photoReference: String
let width: Int
}
// MARK: - PlusCode
struct PlusCode : Decodable{
let compoundCode, globalCode: String
}
enum Scope: CodingKey , Decodable{
case google
}
enum TypeElement: CodingKey , Decodable {
case establishment
case gym
case health
case pointOfInterest
case school
}
But I kept getting pretty much the same error
LAUNCH ALAMO
Response could not be decoded because of error:
The data couldn’t be read because it isn’t in the correct format.
One thing I will note; when I use print(error) instead of LocalizedDescription I get this error:
LAUNCH ALAMO
2022-07-31 17:33:09.489244-0700 Body Fat Calculator[15033:1371997] [boringssl] boringssl_metrics_log_metric_block_invoke(153) Failed to log metrics
responseSerializationFailed(reason: Alamofire.AFError.ResponseSerializationFailureReason.decodingFailed(error: Swift.DecodingError.typeMismatch(Swift.Array<Any>, Swift.DecodingError.Context(codingPath: [], debugDescription: "Expected to decode Array<Any> but found a dictionary instead.", underlyingError: nil))))
So I edited the code a little to:
AF.request(url).responseDecodable(of: Welcome1.self) // Not [Welcome1] now.
and I'm getting some new errors
Welcome1
LAUNCH ALAMO
responseSerializationFailed(reason: Alamofire.AFError.ResponseSerializationFailureReason.decodingFailed(error: Swift.DecodingError.keyNotFound(CodingKeys(stringValue: "nextPageToken", intValue: nil), Swift.DecodingError.Context(codingPath: [], debugDescription: "No value associated with key CodingKeys(stringValue: \"nextPageToken\", intValue: nil) (\"nextPageToken\").", underlyingError: nil))))
PlaceModel
LAUNCH ALAMO
2022-07-31 17:43:32.518882-0700 Body Fat Calculator[15209:1379339] [boringssl] boringssl_metrics_log_metric_block_invoke(153) Failed to log metrics
responseSerializationFailed(reason: Alamofire.AFError.ResponseSerializationFailureReason.decodingFailed(error: Swift.DecodingError.keyNotFound(CodingKeys(stringValue: "name", intValue: nil), Swift.DecodingError.Context(codingPath: [], debugDescription: "No value associated with key CodingKeys(stringValue: \"name\", intValue: nil) (\"name\").", underlyingError: nil))))
Ok this getting a bit long, I'm going to keep trying stuff any insights appreciated happy to provide more info if needed :)
Thank you :D
this example code does not use Alamofire, but should give you enough to
get your data decoded and extract the name, and coordinate as per your question.
Since I do not have your key, I included the json you show as a simulated response.
The crux here is to match your struct models to the JSON data, otherwise you cannot decode it without errors. Works well for me.
You will need to find from the server docs, which fields are optional and adjust the various struct models accordingly.
import Foundation
import SwiftUI
struct ContentView: View {
#StateObject var viewModel = BusinessService()
var body: some View {
List (viewModel.businessList) { bisnes in
VStack {
Text(bisnes.name)
Text("lat: \(bisnes.geometry.location.lat)")
Text("lon: \(bisnes.geometry.location.lng)")
}
}
.task {
do {
try await viewModel.getData(lat: -122.735960, lon: 49.252400)
} catch{
print("---> ContentView error: \(error)")
}
}
}
}
class BusinessService: ObservableObject{
#Published var businessList: [Business] = []
let nfs = "xxxxxxx" // <--- here your key
func getData2(lat: Double, lon: Double) async throws {
if let url = URL(string: "https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=\(lat),\(lon)&radius=150000&types=gym&key=\(nfs)") {
let (data, _) = try await URLSession.shared.data(from: url)
Task{#MainActor in
let responseData = try JSONDecoder().decode(ServerResponse.self, from: data)
self.businessList = responseData.results
// print(businessList) // <-- for testing
}
}
}
// simulated
func getData(lat: Double, lon: Double) async throws {
let json = """
{
"html_attributions" : [],
"next_page_token" : "AeJbb3dLZZ4oYIangr8wO9j6SE4_5L1NcYQC-qCKt1Y0saPFOzNc90E7bG8IDtj8DNlEeM6nWlClZgFd-YMtB4kGQYBLexZgjlX-nH9rN4UQHk9hk-Ha9zLpjY0GYiUsayMZKW5x7oAkQOXlmbARFxu2tR-dW5VB_dd0VkJkf7_6zeGeeje0UjQeqdk1Czr-gBgNxzmMj0adrj2_G0CxeJxk5eN645sdbZ8QJ-BChqkUegJ8l9V7dO4nRCfPz5u_paqoCEcVOJqYMyiZsc3ibBxNowY-uH2PCbpyXcM186CenqBIG088TiyulBFL4Dq1zDxXbZ5asSH4r-u8UtWpdpEcpzAJMjIT4W64vvXYZUT3_918VgENIyCEkPQVebjZUF0X2USykC-ZdlsB2BMvxjKlwx5Q_7_yKYy3Gnxq6T6jTgGisAvNXQ",
"results" : [
{
"business_status" : "OPERATIONAL",
"geometry" : {
"location" : {
"lat" : 49.24665,
"lng" : -122.7504486
},
"viewport" : {
"northeast" : {
"lat" : 49.24808953029149,
"lng" : -122.7490256697085
},
"southwest" : {
"lat" : 49.2453915697085,
"lng" : -122.7517236302915
}
}
},
"icon" : "https://maps.gstatic.com/mapfiles/place_api/icons/v1/png_71/school-71.png",
"icon_background_color" : "#7B9EB0",
"icon_mask_base_uri" : "https://maps.gstatic.com/mapfiles/place_api/icons/v2/school_pinlet",
"name" : "Ascension Martial Arts",
"photos" : [
{
"height" : 1668,
"html_attributions" : ["Arts"],
"photo_reference" : "AeJbb3dunz49PLKAwwhcbLid5d2yWHftgLOY5sdMPLXhCKohiOcfvinKkmFxIht8ZBSNgxTFVJCCbnHxOG7ROlt093iHntMlX4O80ihQ0MI80u7u_TvvKMcwu3os0tgfr84KaNVP1KzNrzTiS1GUF0S82OM43NEuWoMjBW5o2PmhH-Ke-tj4",
"width" : 2500
}
],
"place_id" : "ChIJST1805B2hlQRASo6CA1ejKw",
"plus_code" : {
"compound_code" : "66WX+MR Port Coquitlam, BC, Canada",
"global_code" : "84XV66WX+MR"
},
"rating" : 4.9,
"reference" : "ChIJST1805B2hlQRASo6CA1ejKw",
"scope" : "GOOGLE",
"types" : [ "gym", "health", "point_of_interest", "establishment" ],
"user_ratings_total" : 107,
"vicinity" : "109-1320 Kingsway Avenue, Port Coquitlam"
}
]
}
"""
let data = json.data(using: .utf8)!
Task{#MainActor in
let responseData = try JSONDecoder().decode(ServerResponse.self, from: data)
self.businessList = responseData.results
print(businessList)
}
}
}
struct ServerResponse: Codable {
let htmlAttributions: [String]
let nextPageToken: String
let results: [Business]
enum CodingKeys: String, CodingKey {
case htmlAttributions = "html_attributions"
case nextPageToken = "next_page_token"
case results
}
}
// MARK: - Business
struct Business: Codable, Identifiable {
let id = UUID()
let businessStatus: String
let geometry: Geometry
let icon: String
let iconBackgroundColor: String
let iconMaskBaseURI: String
let name: String
let photos: [Photo]?
let placeID: String
let plusCode: PlusCode
let rating: Double?
let reference, scope: String
let types: [String]
let userRatingsTotal: Int?
let vicinity: String
enum CodingKeys: String, CodingKey {
case businessStatus = "business_status"
case geometry, icon
case iconBackgroundColor = "icon_background_color"
case iconMaskBaseURI = "icon_mask_base_uri"
case name, photos
case placeID = "place_id"
case plusCode = "plus_code"
case rating, reference, scope, types
case userRatingsTotal = "user_ratings_total"
case vicinity
}
}
// MARK: - Geometry
struct Geometry: Codable {
let location: Location
let viewport: Viewport
}
// MARK: - Location
struct Location: Codable {
let lat, lng: Double
}
// MARK: - Viewport
struct Viewport: Codable {
let northeast, southwest: Location
}
// MARK: - Photo
struct Photo: Codable {
let height: Int
let htmlAttributions: [String]
let photoReference: String
let width: Int
enum CodingKeys: String, CodingKey {
case height
case htmlAttributions = "html_attributions"
case photoReference = "photo_reference"
case width
}
}
// MARK: - PlusCode
struct PlusCode: Codable {
let compoundCode, globalCode: String
enum CodingKeys: String, CodingKey {
case compoundCode = "compound_code"
case globalCode = "global_code"
}
}
Using https://app.quicktype.io/ as you probably did, is a good idea.
However, you need to adjust what it gives you. For example, best
not to use the word Result (because Swift already has this struct declared), I used Business.

Parse nested data from JSON using Codable

I am trying to parse nested data from JSON response but not getting success. Below is what i tried so far and the json response trying to parse.
// JSON
{
"statusCode": 200,
"success": true,
"data": {
"tDetail": [
{
"roleId": null,
"id": 34,
"userId": 126,
"catId": null,
"importId": null,
"name": "My task from postman",
"myday": 1,
"important": 0,
"completed": 0,
"dateCreated": "2020-02-10T09:05:04.000Z",
"dateModified": "2020-02-10T09:05:04.000Z"
}
],
"steps": [],
"files": [],
}
}
// Struct
struct MyDayAndTaskDetails: Codable
{
let data : [MyTaskDetail]
}
struct MyTaskDetail : Codable {
let roleId, taskId, userId, catId, important, completed, recurring, myday : Int?
let repeatType, name, duedate, reminder, frequency, weekdays, notes, baseurl : String?
let steps : [Steps]
let files : [Files]
private enum CodingKeys: String, CodingKey {
case taskId = "id"
case userId = "userId"
case roleId = "roleId"
case catId = "catId"
case myday = "myday"
case name = "name"
case notes = "notes"
case duedate = "duedate"
case reminder = "reminder"
case recurring = "recurring"
case repeatType = "repeatType"
case important = "important"
case completed = "completed"
case frequency = "frequency"
case weekdays = "weekdays"
case baseurl = "baseurl"
case steps = "Steps"
case files = "Files"
}
}
struct Steps : Codable {
let stepName : String?
let status, stepId : Int?
private enum CodingKeys: String, CodingKey {
case stepName = "stepName"
case status = "status"
case stepId = "stepId"
}
}
struct Files : Codable {
let fileName : String?
private enum CodingKeys: String, CodingKey {
case fileName = "fileName"
}
}
You missed one level
struct MyDayAndTaskDetails: Codable {
let data : Detail
}
struct Detail: Codable {
let tDetail: [MyTaskDetail]
let steps : [Steps]
let files : [Files]
}
and so on
Use this structs, below structs are sufficient for given json. If you have more keys in your json then you can add them into their respective structs.
struct MyDayAndTaskDetails : Codable {
let data : Task?
let statusCode : Int?
let success : Bool?
}
struct Task : Codable {
let files : [Files]?
let steps : [Steps]?
let tDetail : [TDetail]?
}
struct TDetail : Codable {
let catId : String?
let completed : Int?
let dateCreated : String?
let dateModified : String?
let id : Int?
let importId : String?
let important : Int?
let myday : Int?
let name : String?
let roleId : String?
let userId : Int?
}
struct Steps : Codable {
let stepName : String?
let status: Int?
let stepId : Int?
}
struct Files : Codable {
let fileName : String?
}
And decode data with
let decoder = JSONDecoder()
let response = try decoder.decode(MyDayAndTaskDetails.self, from: data)

Parsing Json Show Error keyNotFound Swift

i want to Parsing a json but Xcode Show this message : keyNotFound(CodingKeys(stringValue: "Id", intValue: nil), Swift.DecodingError.Context(codingPath: [], debugDescription: "No value associated with key CodingKeys(stringValue: \"Id\", intValue: nil) (\"Id\").", underlyingError: nil))
JSON :
{
"result": [
{
"Id": 5,
"Title": "Test1",
"EnTitle": "Story and Novel"
},
{
"Id": 38,
"Title": "Test2",
"EnTitle": " Motivational"
}
],
"status": {
"message": "Confirm",
"success": true,
"systemDateTime": "2019-11-01T12:07:05+03:30",
"httpStatusCode": 200
}
}
Model :
struct Result : Decodable {
let Id : Int
let Title : String
let EnTitle : String
}
class ResultInitialiser {
let Id : Int
let Title : String
let EnTitle : String
init(Id:Int, Title:String, EnTitle: String) {
self.Id = Id
self.Title = Title
self.EnTitle = EnTitle
}
}
View Controller :
var genresFetch = [Result]()
var structGenresFetch = [ResultInitialiser]()
let headers : HTTPHeaders = ["Token" : "6f8652e3-d9d9-4b34-9455-0fa32e82ec58"]
AF.request(BASE_URL!, method: .get, headers: headers).response { (response) in
do {
self.genresFetch = [try JSONDecoder().decode(Result.self, from: response.data!)]
for eachProduct in self.genresFetch {
let recived_Data = ResultInitialiser(Id: eachProduct.Id, Title: eachProduct.Title, EnTitle: eachProduct.EnTitle)
self.structGenresFetch.append(recived_Data)
print(eachProduct.Title)
DispatchQueue.main.async {
self.tvMainApi.reloadData()
}
}
}catch {
print(error)
}
}
JSONDecoder will try to decode the JSON from the root. At the root level, there are only two keys, results and status. The Decodable type that you have passed to it, Result, has neither of those keys.
You need to create another struct:
struct Response {
let results: [Result]
}
And decode this struct instead:
do {
// notice the change to this line
self.genresFetch = try JSONDecoder().decode(Response.self, from: response.data!).results
...
}catch {
print(error)
}
IMO, you don't need the extra class called ResultInitialiser. What's wrong with just using the struct as your model?
"No value associated with key CodingKeys(stringValue: \"Id\", intValue: nil)
The root of the json contains result key only and doesn't have id or the other keys you submit with your Result struct , so You need
self.genresFetch = try JSONDecoder().decode(Root.self, from: response.data!)
print(self.genresFetch.result)
struct Root : Decodable {
let result : [Result]
}
struct Result: Decodable {
let id: Int
let title, enTitle: String
enum CodingKeys: String, CodingKey {
case id = "Id"
case title = "Title"
case enTitle = "EnTitle"
}
}

Creating a list from Nested JSON using Decodable in Swift 4

I have been able to convert JSON to structs using Swift 4's Decodable, unfortunately i have been unable to do so with a JSON key called "list" , which is a list ([]) that contains other structs.
{
"cod":"200",
"message":0.0061,
"cnt":5,
"list":[
{
"dt":1522605600,
"main":{ },
"weather":[ ],
"clouds":{ },
"wind":{ },
"rain":{ },
"sys":{ },
"dt_txt":"2018-04-01 18:00:00"
},
{
"dt":1522616400,
"main":{ },
"weather":[ ],
"clouds":{ },
"wind":{ },
"rain":{ },
"sys":{ },
"dt_txt":"2018-04-01 21:00:00"
},
{
"dt":1522627200,
"main":{
"temp":277.21,
"temp_min":277.21,
"temp_max":277.506,
"pressure":1016.3,
"sea_level":1023.98,
"grnd_level":1016.3,
"humidity":84,
"temp_kf":-0.3
},
These are my structs, my approach was to make ForecastInstance the overall container which holds a property "list" (like the JSON) that is of type ForecastList (which holds the nested structs).
struct ForecastInstance : Decodable {
let list: [ForecastList]?
}
struct ForecastList : Decodable {
let dt : Int?
let weather : [Weather]?
let main : Main?
let wind : Wind?
}
struct Wind : Decodable {
let speed: Float
}
struct Coord : Decodable {
let lon : Float
let lat : Float
}
struct Main : Decodable{
let temp : Double
let pressure : Int
let humidity : Int
let temp_min: Double
let temp_max: Double
}
struct Weather : Decodable{
let id : Int
let main: String
let description: String
let icon: String
}
When i do the following in the view controller , it fails.
self.currentForecast = try
JSONDecoder().decode(ForecastInstance.self,from:data!)
Any help will be greatly appreciated.
This was my error
debugDescription: "Parsed JSON number <1017.42> does not fit in Int.", underlyingError: nil))
Changing the property pressure of type Int to float did the trick.
It works in Playground!
Here is a nice article about Codable:
Swift 4 Decodable: Beyond The Basics.
I hope it will be helpful.
let weatherJSON = """
{
"list": [{
"dt": 1522605600,
"main": {
"temp": 30,
"humidity": 50,
"pressure": 40
},
"weather": ["cloudly"],
"clouds": {},
"wind": {},
"rain": {},
"sys": {},
"dt_txt": "2018-04-01 18:00:00"
}]
}
""".data(using: .utf8)!
struct Main: Codable {
let temp: Int?
let humidity: Int?
let pressure: Int?
}
struct Weather: Codable {
let dt: Int?
let main: Main?
let weather: [String]?
}
struct WeatherList: Codable {
let list: [Weather]?
}
let weather = try JSONDecoder().decode(WeatherList.self, from: weatherJSON)
let count = "\(weather.list?.count ?? 0)"
print(count)
Your top level struct needs to look like this to mimic your JSON structure. The rest of your structs look fine if you are still getting errors I suggest you relook at those structs as well.
struct ForecastInstance : Decodable {
let cod: String?
let message: Int?
let cnt: Int?
let list: [ForecastList]?
}