Json Parsing in swift 4.2 using Codable - ios

I am working on one project.I got one response from API.Now I want to fetch that response through JsonDecode().decod funcion with structure using decodable class.
I have made codable structure but it gives me error like :
The data couldn’t be read because it isn’t in the correct format.
My API Response is:
{
auth = 1;
data = {
"dealer_rating" = {
average = "3.5";
"average_width" = "70%";
count = 2;
dislikes = 0;
likes = 2;
rate1 = 4;
"rate1_label" = "Atenci\U00f3n al cliente";
"rate1_width" = "80%";
rate2 = "3.5";
"rate2_label" = "Proceso de compra";
"rate2_width" = "70%";
rate3 = 3;
"rate3_label" = "Experiencia general";
"rate3_width" = "60%";
};
review = (
{
average = 3;
"post_author" = 16;
"post_content" = "Test of iOS developer.";
"post_title" = "iOS Test";
rate1 = 3;
"rate1_label" = "Atenci\U00f3n al cliente";
rate2 = 3;
"rate2_label" = "Proceso de compra";
rate3 = 3;
"rate3_label" = "Experiencia general";
"stm_recommended" = yes;
username = "";
},
{
average = 4;
"post_author" = 22;
"post_content" = vvvvvvvvvv;
"post_title" = vvvvvvvvvv;
rate1 = 5;
"rate1_label" = "Atenci\U00f3n al cliente";
rate2 = 4;
"rate2_label" = "Proceso de compra";
rate3 = 3;
"rate3_label" = "Experiencia general";
"stm_recommended" = yes;
username = Vnn;
}
);
};
message = "Review found.";
status = 1;
}
My Decodable Structure is :
struct resDelerReview : Codable {
struct struDataString : Codable {
var average:String?
var rate1:Int
var rate1_label:String?
var rate2:Int
var rate2_label:String?
var rate3:Int
var rate3_label:String?
var likes:Int
var dislikes:Int
var count:Int
var rate1_width:String?
var rate2_width:String?
var rate3_width:String?
var average_width:String?
}
struct struReviewList : Codable {
var post_author:String?
var username:String?
var post_content:String?
var post_title:String?
var rate1:String?
var rate1_label:String?
var rate2:String?
var rate2_label:String?
var rate3:String?
var rate3_label:String?
var stm_recommended:String?
var average:Int
}
struct reviewRating : Codable {
let dealer_rating:struDataString
let review:[struReviewList]
}
let data:reviewRating
let status:Bool
let auth:Bool
let message:String?
}
Postman response in text
{
"data": {
"dealer_rating": {
"average": "3.5",
"rate1": 4,
"rate1_label": "Atención al cliente",
"rate2": 3.5,
"rate2_label": "Proceso de compra",
"rate3": 3,
"rate3_label": "Experiencia general",
"likes": 2,
"dislikes": 0,
"count": 2,
"rate1_width": "80%",
"rate2_width": "70%",
"rate3_width": "60%",
"average_width": "70%"
},
"review": [
{
"post_author": "16",
"username": "",
"post_content": "Test of iOS developer.",
"post_title": "iOS Test",
"rate1": "3",
"rate1_label": "Atención al cliente",
"rate2": "3",
"rate2_label": "Proceso de compra",
"rate3": "3",
"rate3_label": "Experiencia general",
"stm_recommended": "yes",
"average": 3
},
{
"post_author": "22",
"username": "Vnn",
"post_content": "vvvvvvvvvv",
"post_title": "vvvvvvvvvv",
"rate1": "5",
"rate1_label": "Atención al cliente",
"rate2": "4",
"rate2_label": "Proceso de compra",
"rate3": "3",
"rate3_label": "Experiencia general",
"stm_recommended": "yes",
"average": 4
}
]
},
"status": true,
"auth": true,
"message": "Review found."
}
My Postman Response is:
Please help me guys I am getting stuck.
Thanks in advance

The error is quite clear if you read the whole of it, it is the property rate2 that is declared as an Int but contains 3.5 for one of the elements
So change it in the struDataString struct to
var rate2: Double
Note There are several other similar fields like rate2 in the struDataString and those are very likely that they may contain double values to like the other rateX fields. You might want to check out the documentation for all of them

Related

iOS Swift: How to change the Response from server to Specific Format?

I stored the data from server to NSArray, Here I want change the current format to new format. But I don't know how to change the current format to new.Please help me for make the format.
Here I have share the old format and new format as following.
This is old format received from Server.
{
{
“class” = “12”,
“section” = “A”,
“name” = “aathi”,
“mark” = “850”,
“school” = “ab matriculation school”,
“place” = “Chennai”
},
{
“class” = “12”,
“section” = “B”,
“name” = “ram”,
“mark” = “904”,
“school” = “ab matriculation school”,
“place” = “delhi”
},
{
“class” = “10”,
“section” = “C”,
“name” = “ashok”,
“mark” = “389”,
“school” = “psg hr sec school”,
“place” = “coimbatore”
},
{
“class” = “8”,
“section” = “B”,
“name” = “sundar”,
“mark” = “267”,
“school” = “govt hr sec school”,
“place” = “Madurai”
}
}
I want to change the new format like below.
{
{
“class” = “12”,
“section” = “A”,
“school” = “ab matriculation school”,
noncommondetails:{
{
“name” = “aathi”,
“mark” = “850”,
“place” = “Chennai”
},
{
“name” = “ram”,
“mark” = “904”,
“place” = “delhi”
}
}
},
{
“class” = “10”,
“section” = “C”,
“school” = “psg hr sec school”,
noncommondetails:{
{
“name” = “ashok”,
“mark” = “389”,
“place” = “coimbatore”
}
},
},
{
“class” = “8”,
“section” = “B”,
“school” = “govt hr sec school”,
noncommondetails:{
{
“name” = “sundar”,
“mark” = “267”,
“place” = “Madurai”
},
}
}
}
You must try https://github.com/Hearst-DD/ObjectMapper for json object mapping.
The best design for what you are doing is to make models for your server response and then save those model objects in an array. That way you can change what you want.
Model for your old format:
struct Item {
var aClass:String?
var section:String?
var name:String?
var mark:String?
var school:String?
var place:String?
}
Now models for your new updated server response:
struct Item {
var aClass:String?
var section:String?
var school:String?
var nonCommonDetails : [NonCommenItem]?
}
struct NonCommenItem {
var name:String?
var mark:String?
var place:String?
}
And then you can use it like:
let nonCommenItem1 = NonCommenItem(name: "aathi", mark: "850", place: "Chennai")
let nonCommenItem2 = NonCommenItem(name: "ram", mark: "904", place: "delhi")
let item1 = Item(aClass: "12", section: "B", school: "ab matriculation school", nonCommonDetails: [nonCommenItem1,nonCommenItem2])
I hope this solve your problem. If there is any confusion please ask. Good Luck :)

Read out specific JSON from NSArray

This is the JSON object I'm getting from the openweathermap - API:
["main": {
humidity = 12;
pressure = 922;
temp = "271.13";
"temp_max" = "171.15";
"temp_min" = "291.15";
}, "name": mycity, "id": 299129219, "coord": {
lat = "92.1211";
lon = "182.1211";
}, "weather": <__NSArrayI 0x1c042e820>(
{
description = "light snow";
icon = 13n;
id = 120;
main = Snow;
},
{
description = mist;
icon = 50n;
id = 722;
main = Mist;
}
)
, "clouds": {
all = 12;
}, "dt": 211, "base": stations, "sys": {
country = XXX;
id = 4891;
message = "0.02221";
sunrise = 1221122112;
sunset = 4343344343;
type = 1;
}, "cod": 100, "visibility": 3200, "wind": {
speed = 3;
}]
Because I like to readout some information (like the current temperature, the weather description, etc.) I tried to use this few lines:
let temperature = (result["main"] as! [String:Double])["temp"]!
The code above is working fine but I got massive problems reading out the description of the first Weather element (called "light snow"):
let description = (result["weather"] as! [String:Any]).first["description"]! //(result should be : "light snow")
... doesn't seems working at all.
So how can I fix this issue?
Thanks a million in advance.
Also used this API :)
This worked for me:
guard let weathersArray = json["weather"] as? [[String: Any]],
let weatherJson = weathersArray.first,
let description = weatherJson["description"] as? String
else { return }
Update: in case you want all the array elements just loop over the weathersArray and get all the descriptions.

Why SwiftyJSON cannot parse Array String in swift 3

{
"item": [
{
"pid": 89334,
"productsname": "Long Way",
"address": "B-4/7, Malikha Housing, Yadanar St., Bawa Myint Ward,",
"telephone": "[\"01570269\",\"01572271\"]"
},
{
"pid": 2,
"productsname": "Myanmar Reliance Energy Co., Ltd. (MRE)",
"address": "Bldg, 2, Rm# 5, 1st Flr., Hninsi St., ",
"telephone": "[\"202916\",\"09-73153580\"]"
}
],
"success": true
}
I cannot parse telephone value from above JSON object with following code.
for item in swiftyJsonVar["item"].array! {
if let jsonDict = item.dictionary {
let pid = jsonDict["pid"]!.stringValue
let productsname = jsonDict["productsname"]!.stringValue
var telephones = [String]()
for telephone in (jsonDict["telephone"]?.array)! {
telephones.append(telephone.stringValue)
}
}
}
I want to get and display one by one phone number of above JSON. I'm not sure why above code is not working. Please help me how to solve it, thanks.
Because telephone is a string that looks like an array, not an array itself. The server encoded this array terribly. You need to JSON-ify it again to loop through the list of telephone numbers:
for item in swiftyJsonVar["item"].array! {
if let jsonDict = item.dictionary {
let pid = jsonDict["pid"]!.stringValue
let productsname = jsonDict["productsname"]!.stringValue
var telephones = [String]()
let telephoneData = jsonDict["telephone"]!.stringValue.data(using: .utf8)!
let telephoneJSON = JSON(data: telephoneData)
for telephone in telephoneJSON.arrayValue {
telephones.append(telephone.stringValue)
}
}
}

Append multiple NSMutableArray data to another NSMutableArray in swift 3

Let's say, there is shipping nsdictionary value is required to send to server, and there is items NSMutableArray inside shipping and, again there is sub-items NSMutableArray inside items.
That's why I've coded like that:
for cake in cakes {
for item in cakeItemsOpt {
let optionalItem = [
"item_req_mod_p_id": item.cakeItemModId,
"l_req_mod_p_id": item.cakeModId,
"mod_id": item.cakeModId,
"mod_qty": item.cakeQty
]
itemsOptional.add(optionalItem)
}
for item in cakeItemsReq {
let requiredItem = [
"item_req_mod_p_id": item.cakeItemModId,
"l_req_mod_p_id": item.cakeModId,
"mod_id": item.cakeModId,
"mod_qty": "1"
]
itemsRequired.add(requiredItem)
}
let cakeParam: NSDictionary = [
"id": cake.cakeId,
"qty": cake.cakeQty,
"type": cake.cakeType,
"name": cake.cakeName,
"image": cake.cakeImage,
"req_modifiers": itemsRequired,
"opt_modifiers": itemsOptional
]
items.add(cakeParam)
}
let param: NSDictionary = [
"user_id": user_id,
"token": token,
"delivery": "del",
"delivery_remark": delivery_remark,
"delivery_date": delivery_date,
"delivery_time": delivery_time,
"delivery_charge": delivery_charge,
"item": items,
"payment_method_id": payment_id,
] as [String : Any]
param is main object to send to server.
But the result is not what I've expected,
["delivery_remark": "heavy", "payment_method_id": "4", "delivery": "del", "token": "0l8BwedwkZQltcFYFhCpicQZeQOhikyYLxa8R3u2", "item": <__NSArrayM 0x60800044ea90>(
{
id = 11;
image = "";
name = "Red Velvet 16cm";
"opt_modifiers" = (
{
"item_req_mod_p_id" = 49;
"l_req_mod_p_id" = 3;
"mod_id" = 3;
"mod_qty" = "";
},
{
"item_req_mod_p_id" = 49;
"l_req_mod_p_id" = 4;
"mod_id" = 4;
"mod_qty" = "";
}
);
qty = 2;
"req_modifiers" = (
{
"item_req_mod_p_id" = 55;
"l_req_mod_p_id" = 8;
"mod_id" = 8;
"mod_qty" = 1;
},
{
"item_req_mod_p_id" = 56;
"l_req_mod_p_id" = 3;
"mod_id" = 3;
"mod_qty" = 1;
}
);
"run_no" = "6260-1c86";
"small_candles" = "";
"sponge_color_id" = "";
"theme_id" = "";
"theme_name" = "";
type = item;
"word_color_id" = "";
}
)
, "delivery_charge": "15.00", "user_id": "21", "delivery_time": "3", "delivery_date": "2017-02-27"]
There is <__NSArrayM 0x60800044ea90> included in my object. Please let me know how to correct my coding to get correct result. Thanks.
Then i guess you want something like
let data: [String : [String : [String : [String]]]]
data = [
"shipping" : ["items" : ["subitemKey" : ["subitemArray"]]]
]
So you have a dictionary that contains a dictionary for a key "shipping" that contains a dictionary for key "items" that contains arrays for keys of different subitems

How do I initialise this Swift Enumeration - or should I even be using it?

I want to initialise a list of parameters in Swift by reading text files containing eight parameter values and preserve the parameters in the same order they are read from file. Once initialised parameter values do not change. Ideally they should be accessed efficiently - and be especially clear for anyone maintaining code further down the track.
In the Playground example (below) parameter values are stored in an Array of Arrays of Strings. Raw values from the Enumeration are then used as a set of indices to address the Array. Initially I thought of using Enumeration to store parameter values as raw values to be addressed using either the parameter name or a numerical index. But from this answer I cannot see a way to assign parameter values directly to member names. In fact I'm starting to doubt whether I should be using an enumeration at all.
Can anyone offer a better way to do this ?
(This probably sounds naive as I've never used a Swift Enumeration before).
enum MapParameters: Int {
case midiChannel = 0,
sizeOfRepeatingMapPattern = 1,
firstMIDIKey = 2,
lastMIDIKey = 3,
middleKey = 4,
referenceMIDIKey = 5,
referenceFrequency = 6,
keysPerFormalOctave = 7
}
let sourceArray = ["5", "12", "0", "127", "60", "69", "440.0", "12"]
let mapIndex = MapParameters.referenceFrequency //referenceFrequency
let i = mapIndex.rawValue // 6
let parameterValue = sourceArray[i] // "440.0"
Using MapParameters enum for making what's the meaning of each index (making it more readable) of sourceArray is legal, however, that's should be valid iff the sourceArray.count is equals to 8 and its sorting is always the same.
You can use it as:
enum MapParameters: Int {
case midiChannel = 0,
sizeOfRepeatingMapPattern = 1,
firstMIDIKey = 2,
lastMIDIKey = 3,
middleKey = 4,
referenceMIDIKey = 5,
referenceFrequency = 6,
keysPerFormalOctave = 7
}
let sourceArray = ["5", "12", "0", "127", "60", "69", "440.0", "12"]
let firstMIDKeyFromSource = sourceArray[MapParameters.firstMIDIKey.rawValue] // 0
let middleKeyFromSource = sourceArray[MapParameters.middleKey.rawValue] // 60
In fact, I find the code snippet above code smell bad; There are alternatives that should be more optimized, such as: Receiving these values as a Dictionary, OR if -somehow- you have to get them as an array, you might want to map
them via a simple model, similar to:
struct MappedParameters {
// you might want to give them initial values
// or you can make them as optionals
var midiChannel = ""
var sizeOfRepeatingMapPattern = ""
var firstMIDIKey = ""
var lastMIDIKey = ""
var middleKey = ""
var referenceMIDIKey = ""
var referenceFrequency = ""
var keysPerFormalOctave = ""
init(sourceArray: [String]) {
if sourceArray.count != 8 {
fatalError("ERROR: sourceArray.count is NOT 8")
}
midiChannel = sourceArray[0]
sizeOfRepeatingMapPattern = sourceArray[1]
firstMIDIKey = sourceArray[2]
lastMIDIKey = sourceArray[3]
middleKey = sourceArray[4]
referenceMIDIKey = sourceArray[5]
referenceFrequency = sourceArray[6]
keysPerFormalOctave = sourceArray[7]
}
}
Usage:
Somewhere in your code you should implement:
let sourceArray = ["5", "12", "0", "127", "60", "69", "440.0", "12"]
let sourceMapper = MappedParameters(sourceArray: sourceArray)
and simply, use sourceMapper as:
print(sourceMapper.firstMIDIKey) // 0
print(sourceMapper.middleKey) // 60
Hope this helped.
An alternative to #Ahmad F's answer could be to use static members
struct MapParameters {
static let midiChannel = 0
static let sizeOfRepeatingMapPattern = 1
static let firstMIDIKey = 2
static let lastMIDIKey = 3
static let middleKey = 4
static let referenceMIDIKey = 5
static let referenceFrequency = 6
static let keysPerFormalOctave = 7
}
let sourceArray = ["5", "12", "0", "127", "60", "69", "440.0", "12"]
let parameterValue = sourceArray[MapParameters.referenceFrequency]

Resources