No Data when Decoding Nested Array in Json by Swift - swift

I want to decode this data and want to display fields separately in UI. Json data I am receiving from API
{
"IsSuccess": true,
"Message": "Data Returned",
"ResponseData": [
{
"PackageId": 1025,
"PackageName": "17 OH Progesterone",
"Price": 0.00,
"DiscountedPrice": 1.0,
"Type": "Test",
"TestPackageGroupId": 3,
"SampleTypeList": [
{
"TestSampleTypeId": "50",
"SampleName": "Serum",
"ColourCode": "#FFB500"
}
]
},
{
"PackageId": 1916,
"PackageName": "24 hour Albumin creatinine ratio (ACR)",
"Price": 120.00,
"DiscountedPrice": 1.0,
"Type": "Test",
"TestPackageGroupId": 3,
"SampleTypeList": [
{
"TestSampleTypeId": "66",
"SampleName": "24 hrs Urine",
"ColourCode": "#212DC1"
}
]
},
{
"PackageId": 1914,
"PackageName": "24 Hour Microalbumin Creatinine Ratio",
"Price": 110.00,
"DiscountedPrice": 1.0,
"Type": "Test",
"TestPackageGroupId": 3,
"SampleTypeList": [
{
"TestSampleTypeId": "66",
"SampleName": "24 hrs Urine",
"ColourCode": "#212DC1"
}
]
},
{
"PackageId": 1913,
"PackageName": "24 Hours Protein Creatinine Ratio (PCR) ",
"Price": 12.00,
"DiscountedPrice": 1.0,
"Type": "Test",
"TestPackageGroupId": 3,
"SampleTypeList": [
{
"TestSampleTypeId": "66",
"SampleName": "24 hrs Urine",
"ColourCode": "#212DC1"
}
]
},
{
"PackageId": 936,
"PackageName": "24 Hours Urinary Phosphorous",
"Price": 15.00,
"DiscountedPrice": 1.0,
"Type": "Test",
"TestPackageGroupId": 3,
"SampleTypeList": [
{
"TestSampleTypeId": "66",
"SampleName": "24 hrs Urine",
"ColourCode": "#212DC1"
}
]
},
{
"PackageId": 937,
"PackageName": "24 Hours Urinary Potassium",
"Price": 2.00,
"DiscountedPrice": 1.0,
"Type": "Test",
"TestPackageGroupId": 3,
"SampleTypeList": [
{
"TestSampleTypeId": "66",
"SampleName": "24 hrs Urine",
"ColourCode": "#212DC1"
}
]
},
......
]}
Decoding Model for the above
import Foundation
struct PriceList {
let Success: Bool
let message: String
let Response: [ResponseList]?
}
extension PriceList:Codable
{
enum CodingKeys: String, CodingKey
{
case Success = "IsSuccess"
case message = "Message"
case Response = "ResponseData"
}
init(from decoder:Decoder) throws {
let container = try decoder.container(keyedBy:CodingKeys.self)
Success = try container.decode(Bool.self,forKey: .Success)
message = try container.decode(String.self,forKey: .message)
Response = try container.decode([ResponseList].self,forKey: .Response)
}
}
struct ResponseList
{
let packageID: Int
let packageName: String
let price, discountedPrice: Double
let type: String
let testPackageGroupID: Int
let SampleType: [SampleTypeList]?
}
extension ResponseList:Decodable
{
enum CodingKeys: String, CodingKey {
case packageID = "PackageId"
case packageName = "PackageName"
case price = "Price"
case discountedPrice = "DiscountedPrice"
case type = "Type"
case testPackageGroupID = "TestPackageGroupId"
case SampleType= "SampleTypeList"
}
init(from decoder:Decoder) throws
{
let container = try decoder.container(keyedBy:CodingKeys.self)
packageID = try container.decode(String.self,forKey: .packageID)
packageName= try container.decode(String.self,forKey: .packageName)
price= try container.decode(Double.self,forKey: .price)
discountedPrice= try container.decode(Double.self,forKey:.discountedPrice)
type= try container.decode(String.self,forKey: .type)
testPackageGroupID = try container.decode(String.self,forKey: .testPackageGroupID )
SampleType= try container.decode([SampleTypeList].self,forKey: .SampleType)
}
}
struct SampleTypeList
{
let testSampleTypeID, sampleName, colourCode: String
}
extension SampleTypeList:Codable {
enum SampleKeys: String, CodingKey {
case testSampleTypeID = "TestSampleTypeId"
case sampleName = "SampleName"
case colourCode = "ColourCode"
}
init(from decoder:Decoder) throws
{
let container = try decoder.container(keyedBy:SampleKeys.self)
testSampleTypeID = try container.decode(String.self,forKey: .testSampleTypeID )
sampleName = try container.decode(String.self,forKey: .sampleName )
colourCode = try container.decode(String.self,forKey: .colourCode)
}
}
This is the code written in playground:
var urlComponents = URLComponents()
urlComponents.scheme = "http"
urlComponents.host = "xx.xx.xxx.x"
urlComponents.path = "/api/test/home"
urlComponents.queryItems = [URLQueryItem(name: "pricelistGroupId",value: "12")]
let url = urlComponents.url
var request = URLRequest(url: url!)
request.addValue("application/json", forHTTPHeaderField:
"Accept")
request.addValue("Basic \(authToken)", forHTTPHeaderField:
"Authorization")
let task = URLSession.shared.dataTask(with: request)
{
(data, response, error) in
if let error = error
{
print("Error \(error)")
return
}
if let response = response as? HTTPURLResponse {
print("Response \(response.statusCode)")
}
if let data = data
{
let dataString = String(data:data, encoding: .utf8)
print(dataString)
let json = try? JSONDecoder().decode(PriceList.self,from:data)
print(json)
}
}
print(dataString) is printing the data. However, no data for print(json) it is showing nil in playground.
ResponseList init is stuck at 259 times (right side playground tab showing all process) whereas SampleTypeList is stuck at 346 times.
If I remove ? (optional) from [ResponseList]? and [SampleTypeList]? it showing "Cannot get unkeyed decoding container -- found null value instead."
Please, ignore typo errors.
Program is stuck at where it is finding null for eg two instances mainly
SampleTypeList = null (occurs many times in JSON) testPackageGroupID = null

This code is enough:
struct PriceList: Decodable {
let success: Bool
let message: String
let response: [ResponseList]
enum CodingKeys: String, CodingKey {
case success = "IsSuccess"
case message = "Message"
case response = "ResponseData"
}
}
struct ResponseList: Decodable {
let packageID: Int
let packageName: String
let price, discountedPrice: Double
let type: String
let testPackageGroupID: Int?
let sampleType: [SampleTypeList]?
enum CodingKeys: String, CodingKey {
case packageID = "PackageId"
case packageName = "PackageName"
case price = "Price"
case discountedPrice = "DiscountedPrice"
case type = "Type"
case testPackageGroupID = "TestPackageGroupId"
case sampleType = "SampleTypeList"
}
}
struct SampleTypeList: Decodable {
let testSampleTypeID: String
let sampleName: String
let colourCode: String
enum CodingKeys: String, CodingKey {
case testSampleTypeID = "TestSampleTypeId"
case sampleName = "SampleName"
case colourCode = "ColourCode"
}
}
What I did fix, what I didn't like from your sample code:
Please name your variables starting with a lowercase, it's convention, and is easier to read (if everyone follow the same convention/standards).
Make your code compilable for us. It's not that hard, but if anyone would want to help you, it be much easier for us to just copy/paste your code and test it and not fix everything in it. You'll have better chances to get an answer. There is a Cannot assign value of type 'String' to type 'Int' because packageID is set as an Int and trying to be decoded as a String, there are missing spaces: variable= instead of variable =, etc. It's annoying to have us to fix that to be able to work.
You printed the JSON, that's a good point, to test it, there is no need anymore of the Web API Call, see following sample
You said that some values can be null, so please provide a JSON with these sample, cut if needed, see the JSON I used as sample, I checked on a JSON online validator that is was valid, and that's all. Since you that that 2 values could be null, I used all possibilities: one with none null, one with one null, one with the other null, and one with both null. Then, for each of these, I put the values as optional.
I removed all the init(from decoder:) as they are useless. If you decode each value with let container = try decoder.container(keyedBy:CodingKeys.self); variable = try container.decode(VariableType.self,forKey: .correspondingCodingKeyInTheEnumCase), that code is already done internally by Apple when compiling.
With sample test:
let jsonStr = """
{
"IsSuccess": true,
"Message": "Data Returned",
"ResponseData": [{
"PackageId": 1025,
"PackageName": "17 OH Progesterone",
"Price": 0.00,
"DiscountedPrice": 1.0,
"Type": "Test",
"TestPackageGroupId": 3,
"SampleTypeList": [{
"TestSampleTypeId": "50",
"SampleName": "Serum",
"ColourCode": "#FFB500"
}]
},
{
"PackageId": 1916,
"PackageName": "24 hour Albumin creatinine ratio (ACR)",
"Price": 120.00,
"DiscountedPrice": 1.0,
"Type": "Test",
"TestPackageGroupId": 3,
"SampleTypeList": null
},
{
"PackageId": 1914,
"PackageName": "24 Hour Microalbumin Creatinine Ratio",
"Price": 110.00,
"DiscountedPrice": 1.0,
"Type": "Test",
"TestPackageGroupId": null,
"SampleTypeList": [{
"TestSampleTypeId": "66",
"SampleName": "24 hrs Urine",
"ColourCode": "#212DC1"
}]
},
{
"PackageId": 1913,
"PackageName": "24 Hours Protein Creatinine Ratio (PCR) ",
"Price": 12.00,
"DiscountedPrice": 1.0,
"Type": "Test",
"TestPackageGroupId": null,
"SampleTypeList": null
}
]
}
"""
do {
let priceList = try JSONDecoder().decode(PriceList.self, from: Data(jsonStr.utf8))
print(priceList)
} catch {
print("Error while decoding: \(error)")
}

try this, works for me:
extension ResponseList: Codable { // <-- here not Decodable
enum CodingKeys: String, CodingKey {
case packageID = "PackageId"
case packageName = "PackageName"
case price = "Price"
case discountedPrice = "DiscountedPrice"
case type = "Type"
case testPackageGroupID = "TestPackageGroupId"
case SampleType = "SampleTypeList"
}
init(from decoder:Decoder) throws {
let container = try decoder.container(keyedBy:CodingKeys.self)
packageID = try container.decode(Int.self,forKey: .packageID) // <-- here Int
packageName = try container.decode(String.self,forKey: .packageName)
price = try container.decode(Double.self,forKey: .price)
discountedPrice = try container.decode(Double.self,forKey:.discountedPrice)
type = try container.decode(String.self,forKey: .type)
testPackageGroupID = try container.decode(Int.self,forKey: .testPackageGroupID ) // <-- here Int
SampleType = try container.decode([SampleTypeList].self,forKey: .SampleType)
}
}
EDIT:
Here is the code I used to show decoding the given json data works with my answer.
import SwiftUI
import Foundation
#main
struct TestApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
struct PriceList {
let Success: Bool
let message: String
let Response: [ResponseList]?
}
extension PriceList: Codable {
enum CodingKeys: String, CodingKey {
case Success = "IsSuccess"
case message = "Message"
case Response = "ResponseData"
}
init(from decoder:Decoder) throws {
let container = try decoder.container(keyedBy:CodingKeys.self)
Success = try container.decode(Bool.self,forKey: .Success)
message = try container.decode(String.self,forKey: .message)
Response = try container.decode([ResponseList].self,forKey: .Response)
}
}
struct ResponseList {
let packageID: Int
let packageName: String
let price, discountedPrice: Double
let type: String
let testPackageGroupID: Int
let SampleType: [SampleTypeList]?
}
extension ResponseList: Codable { // <-- here not Decodable
enum CodingKeys: String, CodingKey {
case packageID = "PackageId"
case packageName = "PackageName"
case price = "Price"
case discountedPrice = "DiscountedPrice"
case type = "Type"
case testPackageGroupID = "TestPackageGroupId"
case SampleType = "SampleTypeList"
}
init(from decoder:Decoder) throws {
let container = try decoder.container(keyedBy:CodingKeys.self)
packageID = try container.decode(Int.self,forKey: .packageID) // <-- here Int
packageName = try container.decode(String.self,forKey: .packageName)
price = try container.decode(Double.self,forKey: .price)
discountedPrice = try container.decode(Double.self,forKey:.discountedPrice)
type = try container.decode(String.self,forKey: .type)
testPackageGroupID = try container.decode(Int.self,forKey: .testPackageGroupID ) // <-- here Int
SampleType = try container.decode([SampleTypeList].self,forKey: .SampleType)
}
}
struct SampleTypeList {
let testSampleTypeID, sampleName, colourCode: String
}
extension SampleTypeList:Codable {
enum SampleKeys: String, CodingKey {
case testSampleTypeID = "TestSampleTypeId"
case sampleName = "SampleName"
case colourCode = "ColourCode"
}
init(from decoder:Decoder) throws {
let container = try decoder.container(keyedBy:SampleKeys.self)
testSampleTypeID = try container.decode(String.self,forKey: .testSampleTypeID )
sampleName = try container.decode(String.self,forKey: .sampleName )
colourCode = try container.decode(String.self,forKey: .colourCode)
}
}
struct ContentView: View {
#State var priceList: PriceList?
var body: some View {
Text(priceList?.message ?? "no data")
.onAppear {
let jsonString = """
{
"IsSuccess": true,
"Message": "Data Returned",
"ResponseData": [
{
"PackageId": 1025,
"PackageName": "17 OH Progesterone",
"Price": 0.00,
"DiscountedPrice": 1.0,
"Type": "Test",
"TestPackageGroupId": 3,
"SampleTypeList": [
{
"TestSampleTypeId": "50",
"SampleName": "Serum",
"ColourCode": "#FFB500"
}
]
},
{
"PackageId": 1916,
"PackageName": "24 hour Albumin creatinine ratio (ACR)",
"Price": 120.00,
"DiscountedPrice": 1.0,
"Type": "Test",
"TestPackageGroupId": 3,
"SampleTypeList": [
{
"TestSampleTypeId": "66",
"SampleName": "24 hrs Urine",
"ColourCode": "#212DC1"
}
]
},
{
"PackageId": 1914,
"PackageName": "24 Hour Microalbumin Creatinine Ratio",
"Price": 110.00,
"DiscountedPrice": 1.0,
"Type": "Test",
"TestPackageGroupId": 3,
"SampleTypeList": [
{
"TestSampleTypeId": "66",
"SampleName": "24 hrs Urine",
"ColourCode": "#212DC1"
}
]
},
{
"PackageId": 1913,
"PackageName": "24 Hours Protein Creatinine Ratio (PCR) ",
"Price": 12.00,
"DiscountedPrice": 1.0,
"Type": "Test",
"TestPackageGroupId": 3,
"SampleTypeList": [
{
"TestSampleTypeId": "66",
"SampleName": "24 hrs Urine",
"ColourCode": "#212DC1"
}
]
},
{
"PackageId": 936,
"PackageName": "24 Hours Urinary Phosphorous",
"Price": 15.00,
"DiscountedPrice": 1.0,
"Type": "Test",
"TestPackageGroupId": 3,
"SampleTypeList": [
{
"TestSampleTypeId": "66",
"SampleName": "24 hrs Urine",
"ColourCode": "#212DC1"
}
]
},
{
"PackageId": 937,
"PackageName": "24 Hours Urinary Potassium",
"Price": 2.00,
"DiscountedPrice": 1.0,
"Type": "Test",
"TestPackageGroupId": 3,
"SampleTypeList": [
{
"TestSampleTypeId": "66",
"SampleName": "24 hrs Urine",
"ColourCode": "#212DC1"
}
]
}
]
}
"""
let data = jsonString.data(using: .utf8)
priceList = try? JSONDecoder().decode(PriceList.self, from: data!)
print("\n--> priceList: \(priceList) \n")
}
}
}
EDIT-2:
If you can have this in your json data:
"TestPackageGroupId": null,
"SampleTypeList": null
then try this approach to decode your json data:
struct ResponseList {
let packageID: Int
let packageName: String
let price, discountedPrice: Double
let type: String
let testPackageGroupID: Int? // <--- here optional
let SampleType: [SampleTypeList]? // <--- here optional
}
extension ResponseList: Codable { // <-- here not Decodable
enum CodingKeys: String, CodingKey {
case packageID = "PackageId"
case packageName = "PackageName"
case price = "Price"
case discountedPrice = "DiscountedPrice"
case type = "Type"
case testPackageGroupID = "TestPackageGroupId"
case SampleType = "SampleTypeList"
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy:CodingKeys.self)
packageID = try container.decode(Int.self,forKey: .packageID) // <-- here Int
packageName = try container.decode(String.self,forKey: .packageName)
price = try container.decode(Double.self,forKey: .price)
discountedPrice = try container.decode(Double.self,forKey:.discountedPrice)
type = try container.decode(String.self,forKey: .type)
// --- here
testPackageGroupID = try container.decodeIfPresent(Int.self,forKey: .testPackageGroupID)
SampleType = try container.decodeIfPresent([SampleTypeList].self,forKey: .SampleType)
}
}
Likewise for any other elements that can have null.

Related

How can i parse an Json array of a list of different object using Codable?

I have an json array with a list of item, that has different property.
{
"items": [
{
"id": "1",
"name": "name",
"propertyOfA": "1243",
"productype": "a"
},
{
"id": "2",
"name": "name",
"propertyOfA": "12",
"productype": "a"
},
{
"id": "3",
"name": "name",
"propertyOfA": "1243",
"productype": "a"
},
{
"id": "1",
"name": "name",
"propertyOfB": "1243",
"productype": "b"
},
{
"id": "1",
"name": "name",
"propertyOfC": "1243",
"propertyOfC2": "1243",
"productype": "c"
}
]
}
What i usually do is parse it into something like :
struct RequestData: Codable {
let items: [Item]
}
enum ProductType: String, Codable {
case a = "A"
case b = "B"
case c = "C"
}
struct Item: Codable {
let id, name: String
let propertyOfA: String?
let productype: String
let propertyOfB: String?
let propertyOfC: String?
let propertyOfC2: String?
}
But if this array keep growing, its gonna end up with a Item with a huge amount of optional property,
so i want each object have its own dedicated class
adding some kind of bridge inside codable parsings,
what i want to archive is something like :
struct RequestData: Codable {
let items: Items
}
struct items: Codable {
let arrayA: [A]
let arrayB = [B]
let arrayC = [C]
}
struct A: Codable {
let id: String,
let name: String,
let propertyOfA: String,
let producttype: Productype
}
struct B {
...
}
struct C {
...
}
can i do this with Codable ?
A reasonable solution is an enum with associated values because the type can be determined by the productype key. The init method first decodes the productype with a CodingKey then in a switch it decodes (from a singleValueContainer) and associates the proper type/value to the corresponding case.
enum ProductType: String, Codable {
case a, b, c
}
struct Root : Codable {
let items : [Product]
}
struct ProductA : Codable {
let id, name: String
let productype: ProductType
let propertyOfA : String
}
struct ProductB : Codable {
let id, name: String
let productype: ProductType
let propertyOfB : String
}
struct ProductC : Codable {
let id, name: String
let productype: ProductType
let propertyOfC, propertyOfC2 : String
}
enum Product : Codable {
case a(ProductA), b(ProductB), c(ProductC)
enum CodingKeys : String, CodingKey { case productype }
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let type = try container.decode(ProductType.self, forKey: .productype)
let singleContainer = try decoder.singleValueContainer()
switch type {
case .a : self = .a(try singleContainer.decode(ProductA.self))
case .b : self = .b(try singleContainer.decode(ProductB.self))
case .c : self = .c(try singleContainer.decode(ProductC.self))
}
}
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
switch self {
case .a(let productA): try container.encode(productA)
case .b(let productB): try container.encode(productB)
case .c(let productC): try container.encode(productC)
}
}
}
And decode
let jsonString = """
{
"items": [
{
"id": "1",
"name": "name",
"propertyOfA": "1243",
"productype": "a"
},
{
"id": "2",
"name": "name",
"propertyOfA": "12",
"productype": "a"
},
{
"id": "3",
"name": "name",
"propertyOfA": "1243",
"productype": "a"
},
{
"id": "1",
"name": "name",
"propertyOfB": "1243",
"productype": "b"
},
{
"id": "1",
"name": "name",
"propertyOfC": "1243",
"propertyOfC2": "1243",
"productype": "c"
}
]
}
"""
do {
let result = try JSONDecoder().decode(Root.self, from: Data(jsonString.utf8))
print(result)
} catch { print(error)}
To read the enum values use also a switch.

How to create a struct to match this Json

I am trying to fetch a random user from an api and I created a struct called result to match the json from the api.
This is what I came up with but I am sure this is not the right way to do it!
When I run this I get [] as a result so I want to know how I can change this code in order to match the json
#State var results : result?
struct result : Codable {
var results : [user]
struct user : Codable{
var gender : String
var name : name
var location : location
var email:String
struct name : Codable{
var title : String
var first : String
var last : String
}
struct location : Codable {
let street : street
let city : String
let state : String
let country : String
let postcode : Int
struct street :Codable {
var number : String
var name : String
}
}
struct login : Codable {
var uuid : String
var username : String
var password : String
}
}
}
This is how the Json looks
{
"results": [
{
"gender": "male",
"name": {
"title": "mr",
"first": "brad",
"last": "gibson"
},
"location": {
"street": "9278 new road",
"city": "kilcoole",
"state": "waterford",
"postcode": "93027",
"coordinates": {
"latitude": "20.9267",
"longitude": "-7.9310"
},
"timezone": {
"offset": "-3:30",
"description": "Newfoundland"
}
},
"email": "brad.gibson#example.com",
"login": {
"uuid": "155e77ee-ba6d-486f-95ce-0e0c0fb4b919",
"username": "silverswan131",
"password": "firewall",
"salt": "TQA1Gz7x",
"md5": "dc523cb313b63dfe5be2140b0c05b3bc",
"sha1": "7a4aa07d1bedcc6bcf4b7f8856643492c191540d",
"sha256": "74364e96174afa7d17ee52dd2c9c7a4651fe1254f471a78bda0190135dcd3480"
},
"dob": {
"date": "1993-07-20T09:44:18.674Z",
"age": 26
},
"registered": {
"date": "2002-05-21T10:59:49.966Z",
"age": 17
},
"phone": "011-962-7516",
"cell": "081-454-0666",
"id": {
"name": "PPS",
"value": "0390511T"
},
"picture": {
"large": "https://randomuser.me/api/portraits/men/75.jpg",
"medium": "https://randomuser.me/api/portraits/med/men/75.jpg",
"thumbnail": "https://randomuser.me/api/portraits/thumb/men/75.jpg"
},
"nat": "IE"
}
],
"info": {
"seed": "fea8be3e64777240",
"results": 1,
"page": 1,
"version": "1.3"
}
}
You want to learn how to do this on your own but there are tools on the web that can easily help you create a struct from a JSON. This is a really good one.
You were close so make sure you dissect the code and notice the differences
import SwiftUI
class ResultsViewModel: ObservableObject{
let json = """
{
"results": [
{
"gender": "male",
"name": {
"title": "mr",
"first": "brad",
"last": "gibson"
},
"location": {
"street": "9278 new road",
"city": "kilcoole",
"state": "waterford",
"postcode": "93027",
"coordinates": {
"latitude": "20.9267",
"longitude": "-7.9310"
},
"timezone": {
"offset": "-3:30",
"description": "Newfoundland"
}
},
"email": "brad.gibson#example.com",
"login": {
"uuid": "155e77ee-ba6d-486f-95ce-0e0c0fb4b919",
"username": "silverswan131",
"password": "firewall",
"salt": "TQA1Gz7x",
"md5": "dc523cb313b63dfe5be2140b0c05b3bc",
"sha1": "7a4aa07d1bedcc6bcf4b7f8856643492c191540d",
"sha256": "74364e96174afa7d17ee52dd2c9c7a4651fe1254f471a78bda0190135dcd3480"
},
"dob": {
"date": "1993-07-20T09:44:18.674Z",
"age": 26
},
"registered": {
"date": "2002-05-21T10:59:49.966Z",
"age": 17
},
"phone": "011-962-7516",
"cell": "081-454-0666",
"id": {
"name": "PPS",
"value": "0390511T"
},
"picture": {
"large": "https://randomuser.me/api/portraits/men/75.jpg",
"medium": "https://randomuser.me/api/portraits/med/men/75.jpg",
"thumbnail": "https://randomuser.me/api/portraits/thumb/men/75.jpg"
},
"nat": "IE"
}
],
"info": {
"seed": "fea8be3e64777240",
"results": 1,
"page": 1,
"version": "1.3"
}
}
""".data(using: .utf8)
var result: JSONObject? {
var decoded: JSONObject? = nil
do{
decoded = try JSONDecoder().decode(JSONObject.self, from: json!)
}catch{
print(error)
}
return decoded
}
}
struct ResultsView: View {
#StateObject var vm: ResultsViewModel = ResultsViewModel()
var body: some View {
if vm.result == nil || vm.result?.results.first == nil{
Text("nil")
}else{
VStack{
Text(vm.result!.results.first!.name.first)
Text(vm.result!.results.first!.name.last)
}
}
}
}
// MARK: - JSONObject
struct JSONObject: Codable {
let results: [Result]
let info: Info
}
// MARK: - Info
struct Info: Codable {
let seed: String
let results, page: Int
let version: String
}
// MARK: - Result
struct Result: Codable {
let gender: String
let name: Name
let location: Location
let email: String
let login: Login
let dob, registered: Dob
let phone, cell: String
let id: ID
let picture: Picture
let nat: String
}
// MARK: - Dob
struct Dob: Codable {
let date: String
let age: Int
}
// MARK: - ID
struct ID: Codable {
let name, value: String
}
// MARK: - Location
struct Location: Codable {
let street, city, state, postcode: String
let coordinates: Coordinates
let timezone: Timezone
}
// MARK: - Coordinates
struct Coordinates: Codable {
let latitude, longitude: String
}
// MARK: - Timezone
struct Timezone: Codable {
let offset, timezoneDescription: String
enum CodingKeys: String, CodingKey {
case offset
case timezoneDescription = "description"
}
}
// MARK: - Login
struct Login: Codable {
let uuid, username, password, salt: String
let md5, sha1, sha256: String
}
// MARK: - Name
struct Name: Codable {
let title, first, last: String
}
// MARK: - Picture
struct Picture: Codable {
let large, medium, thumbnail: String
}

Getting error "The data couldn’t be read because it is missing." during fetching data using alamofire and swift

I am fetching data from server in swift using Alamofire and codable. But i am getting error
The data couldn’t be read because it is missing.
is there anything wrong in my code? why i am getting error like this?
My code:
import Foundation
// MARK: - Welcome
struct UserDetails: Codable {
let success: Int
let message: String
let result: UserResult
enum CodingKeys: String, CodingKey {
case success = "Success"
case message = "Message"
case result = "Result"
}
}
// MARK: - Result
struct UserResult: Codable {
let userID: Int
let firstName, location, state, about: String
let totalScore: Int
let sun, rising, moon: String
let ego, communication, birthStar, sexual: Int
let intellectual, temperament, emotional, healthGenetic: Int
let profilePic: String
let numberOfPhotos: Int
let imagelist: [Imagelist]
let verified: Int
let dateOfBirth, timeOfBirth: String
let age: Int
enum CodingKeys: String, CodingKey {
case userID = "User_Id"
case firstName = "First_Name"
case location = "Location"
case state = "State"
case about = "About"
case totalScore = "TotalScore"
case sun
case rising = "Rising"
case moon = "Moon"
case ego = "Ego"
case communication = "Communication"
case birthStar = "Birth_star"
case sexual = "Sexual"
case intellectual = "Intellectual"
case temperament = "Temperament"
case emotional = "Emotional"
case healthGenetic = "Health_Genetic"
case profilePic = "Profile_Pic"
case numberOfPhotos = "NumberOfPhotos"
case imagelist, verified
case dateOfBirth = "DateOfBirth"
case timeOfBirth = "TimeOfBirth"
case age = "Age"
}
}
// MARK: - Imagelist
struct Imagelist: Codable {
let userID: Int
let imagePath: String
enum CodingKeys: String, CodingKey {
case userID = "User_Id"
case imagePath = "Image_path"
}
}
import Foundation
import Alamofire
class ApiRequest {
func getUserDetailData(_ userId: Int, _ accessToken: String, completion: #escaping UserProfileDetailsData) {
guard let url = URL(string: "\(USER_PROFILE_URL)?UserID=\(userId)&RequestingUser_id=1") else{return}
var urlRequest = URLRequest(url: url)
urlRequest.httpMethod = HTTPMethod.get.rawValue
urlRequest.addValue(accessToken, forHTTPHeaderField: "Authorization")
Alamofire.request(urlRequest).responseJSON { (response) in
if let error = response.result.error {
debugPrint(error.localizedDescription)
completion(nil)
return
}
guard let data = response.data else { return completion(nil)}
let jsonDecoder = JSONDecoder()
do {
let person = try jsonDecoder.decode(UserDetails.self, from: data)
completion(person)
} catch {
debugPrint(error.localizedDescription)
completion(nil)
}
}
}
}
In my constant class i have following two codes
let USER_PROFILE_URL = URL_BASE + "GetUserProfile/"
typealias UserProfileDetailsData = (UserDetails?) -> Void
I am calling the api in my view controller like:
func getUserDetailsData(_ userId: Int, accessToken: String) {
apiRequest.getUserDetailData(userId, accessToken) { (userDetails) in
self.userDataResult = userDetails?.result
}
}
I am getting data from server like
{
"Success": 1,
"Message": "Record Get Successfully.",
"Result": {
"User_Id": 230,
"First_Name": "Fgfdg",
"Location": "Fond du Lac County",
"State": " Wisconsin",
"About": "SDFDdsgsGgsddsSSGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG",
"TotalScore": 27,
"sun": "Scorpio",
"Rising": "Scorpio",
"Moon": "Libra",
"Ego": 100,
"Communication": 100,
"Birth_star": 99,
"Sexual": 50,
"Intellectual": 100,
"Temperament": 99,
"Emotional": 0,
"Health_Genetic": 100,
"Profile_Pic": "Images/ProfilePic/yugva0y.jpg",
"NumberOfPhotos": 8,
"imagelist": [
{
"User_Id": 230,
"Image_path": "Images/UploadImage/uUk8lUP.jpg"
},
{
"User_Id": 230,
"Image_path": "Images/UploadImage/Sf5jBIU.jpg"
},
{
"User_Id": 230,
"Image_path": "Images/UploadImage/uw8hdNt.jpg"
},
{
"User_Id": 230,
"Image_path": "Images/UploadImage/vRdKGmM.jpg"
},
{
"User_Id": 230,
"Image_path": "Images/UploadImage/vQchUIO.jpg"
},
{
"User_Id": 230,
"Image_path": "Images/UploadImage/HYHQHiL.jpg"
},
{
"User_Id": 230,
"Image_path": "Images/UploadImage/owM3Al9.jpg"
},
{
"User_Id": 230,
"Image_path": "Images/UploadImage/T4ufyc6.jpg"
}
],
"verified": 0,
"DateOfBirth": "1978-11-28",
"TimeOfBirth": "07:52:00.0000000",
"Age": 41
}
}
The error i am getting is:
Swift.DecodingError.valueNotFound(Swift.String, Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "Result", intValue: nil), CodingKeys(stringValue: "sun", intValue: nil)], debugDescription: "Expected String value but found null instead.", underlyingError: nil))

Cannot convert value of type '[String : Any]' to expected argument type 'Data' Alamofire - Codable

I'm new to programming so apologies if the fix is a simple one. I'm trying to get the JSON data from the Alamofire request to show up not as an optional in the console.
I've already tried response.data which does give me the data as an optional but I don't know how to unwrap that optional in this call. I've searched and have seen that result.value might be closer to what I need. Below is what I have so far. This results in a "Cannot convert value of type '[String : Any]' to expected argument type 'Data'" error.
JSON File-->
"forecasts": [
{
"dateLabel": "今日",
"telop": "晴時々曇",
"date": "2019-08-16",
"temperature": {
"min": null,
"max": null
},
"image": {
"width": 50,
"url": "http://weather.livedoor.com/img/icon/2.gif",
"title": "晴時々曇",
"height": 31
}
},
{
"dateLabel": "明日",
"telop": "晴時々曇",
"date": "2019-08-17",
"temperature": {
"min": {
"celsius": "27",
"fahrenheit": "80.6"
},
"max": {
"celsius": "37",
"fahrenheit": "98.6"
}
},
"image": {
"width": 50,
"url": "http://weather.livedoor.com/img/icon/2.gif",
"title": "晴時々曇",
"height": 31
}
},
{
"dateLabel": "明後日",
"telop": "晴時々曇",
"date": "2019-08-18",
"temperature": {
"min": null,
"max": null
},
"image": {
"width": 50,
"url": "http://weather.livedoor.com/img/icon/2.gif",
"title": "晴時々曇",
"height": 31
}
}
],
"location": {
"city": "東京",
"area": "関東",
"prefecture": "東京都"
},
"publicTime": "2019-08-16T17:00:00+0900",
"copyright": {
"provider": [
{
"link": "http://tenki.jp/",
"name": "日本気象協会"
}
],
"link": "http://weather.livedoor.com/",
"title": "(C) LINE Corporation",
"image": {
"width": 118,
"link": "http://weather.livedoor.com/",
"url": "http://weather.livedoor.com/img/cmn/livedoor.gif",
"title": "livedoor 天気情報",
"height": 26
}
}
Data model-->
import Foundation
import Alamofire
// MARK: - WeatherData
struct WeatherData: Codable {
let forecasts: [Forecast]
}
struct Forecast: Codable {
let dateLabel, telop, date: String
let temperature: Temperature
let image: Image
enum CodingKeys: String, CodingKey {
case dateLabel = "dateLabel"
case telop = "telop"
case date = "date"
case temperature
case image
}
}
struct Image: Codable {
let width: Int
let url: String
let title: String
let height: Int
enum CodingKeys: String, CodingKey {
case width = "width"
case url = "url"
case title = "title"
case height = "height"
}
}
struct Temperature: Codable {
let min, max: Max?
enum CodingKeys: String, CodingKey {
case min = "min"
case max = "max"
}
}
struct Max: Codable {
let celsius, fahrenheit: String
enum CodingKeys: String, CodingKey {
case celsius = "celsius"
case fahrenheit = "fahrenheit"
}
}
viewcontroller-->
import UIKit
import Alamofire
class ForecastTableViewController: UITableViewController {
let WEATHER_URL = "http://weather.livedoor.com/forecast/webservice/json/v1?city=130010"
override func viewDidLoad() {
super.viewDidLoad()
Alamofire.request(WEATHER_URL).responseJSON { (response) in
if let data = response.result.value as? [String: Any]{
let decoder = JSONDecoder()
let forecast = try? decoder.decode(WeatherData.self, from: data)
print(forecast?.forecasts)
}
}
}
My ultimate goal is to print out the JSON data into a tableview, including the images and dates. I think being able to unwrap this optional is the first step before I figure out the next part.
I've already tried response.data which does give me the data as an optional but I don't know how to unwrap that optional in this call.
You definitely should learn how to unwrap optionals properly. It basically comes down to what do you want to do when the value is nil. response.data could be nil when somehow the data could not be fetched, there's no internet, the server doesn't respond, or whatever reason it might be.
Think about what you want to happen in such a situation. Do you want to show an error as an alert to the user? Do you want to try again, or just do nothing?
And then use this code:
Alamofire.request(WEATHER_URL).responseData { (response) in
guard let data = response.data else {
// do the stuff you want to do when the data failed to be fetched
return
}
let decoder = JSONDecoder()
guard let forecast = try? decoder.decode(WeatherData.self, from: data) else {
// do the stuff you want to do when the data is not in the right format
return
}
print(forecast?.forecasts)
}
If you want to access the raw Data returned from a request, you need to use responseData. responseJSON parses the response using JSONSerialization and gives you an Any value. responseData gives you the raw Data returned, so you can unwrap it as you're currently doing and use the JSONDecoder.
You can also update to Alamofire 5 (currently in beta) and use responseDecodable to be able to parse Decodable types automatically.

How do I specify more than one type to decode in the enum CodingKeys when using Codable?

I have a struct type which lists different types which must be decoded from JSON and I cannot find any information on how to specify more than one type in my enum called CodingKeys. In the innermost dictionary sometimes one key:value pair will be [String:String] and another key:value pair will be [String:Int]
I have tried specifying just String and CodingKey as in the code snippet below but Xcode reports 2 error messages.
struct JSONSiteData : Codable {
let destinationAddresses : [String]
let originAddresses : [String]
let rows : [ [String : [[String:[[String: [String:Any]]]]]] ]
let status : String
}
enum CodingKeys : String, CodingKey {
case destinationAddresses = "destination_addresses"
case originAddresses = "origin_addresses"
case rows
case status
}
I get the following error messages from Xcode;
Type 'JSONSiteData' does not conform to protocol 'Decodable'
Type 'JSONSiteData' does not conform to protocol 'Encodable'
Here is my JSON;
{
"destination_addresses": [
"1 Dunwell Ln, Bolam, Darlington DL2 2UW, UK",
"Unnamed Road, Newton Aycliffe DL5 6QZ, UK",
"Preston Manor Farm, Preston le Skerne, Newton Aycliffe DL5 6JH, United Kingdom",
"6 Middridge Farms, Middridge, Newton Aycliffe DL5 7JQ, UK",
"1 The Gardens, Hunwick, Crook DL15 0XW, UK"
],
"origin_addresses": [
"42 Drovers Way, Dunstable LU6 1AW, UK"
],
"rows": [
{
"elements": [
{
"distance": {
"text": "220 mi",
"value": 353731
},
"duration": {
"text": "3 hours 45 mins",
"value": 13475
},
"status": "OK"
},
{
"distance": {
"text": "222 mi",
"value": 356696
},
"duration": {
"text": "3 hours 45 mins",
"value": 13471
},
"status": "OK"
},
{
"distance": {
"text": "222 mi",
"value": 358053
},
"duration": {
"text": "3 hours 46 mins",
"value": 13545
},
"status": "OK"
},
{
"distance": {
"text": "225 mi",
"value": 361421
},
"duration": {
"text": "3 hours 49 mins",
"value": 13768
},
"status": "OK"
},
{
"distance": {
"text": "229 mi",
"value": 369280
},
"duration": {
"text": "3 hours 57 mins",
"value": 14238
},
"status": "OK"
}
]
}
],
"status": "OK"
}
You can try this,
// To parse the JSON, add this file to your project and do:
//
// let jsonSiteData = try? newJSONDecoder().decode(JSONSiteData.self, from: jsonData)
import Foundation
struct JSONSiteData: Codable {
let destinationAddresses, originAddresses: [String]
let rows: [Row]
let status: String
enum CodingKeys: String, CodingKey {
case destinationAddresses = "destination_addresses"
case originAddresses = "origin_addresses"
case rows, status
}
}
struct Row: Codable {
let elements: [Element]
}
struct Element: Codable {
let distance, duration: Distance
let status: String
}
struct Distance: Codable {
let text: String
let value: Int
}
Refer this link to generate models from JSON Strings.
https://app.quicktype.io/
In terms of Codable there are no different types.
Decode these structs
struct JSONSiteData : Decodable {
let destinationAddresses : [String]
let originAddresses : [String]
let rows : [Row]
let status : String
}
struct Row : Decodable {
let elements : [Element]
}
struct Element : Decodable {
let distance : Item
let duration : Item
let status : String
}
struct Item : Decodable {
let text : String
let value : Int
}