swift unable to use api data in collection View - ios

here my JSON is -
{
"status": "success",
"data": [
{
"_id": "15756323",
"name": "A",
"icons": "https://sdfgsuew34j.png",
"createdAt": "2021-03-18T13:06:44.054Z",
"updatedAt": "2021-03-18T13:06:44.054Z",
"__v": 0,
"id": "6053503drm476"
},
{
"_id": "45646054821",
"name": "S",
"icons": "https://dkj/djf.png",
"createdAt": "2021-03-19T10:51:07.066Z",
"updatedAt": "2021-03-19T10:51:07.066Z",
"__v": 0,
"id": "6054821b11kd6873"
}
]
}
I'm trying to print API data in collectionView
here's some code
var places: [Post]?
func apicall() {
let url = URL(string: "http://17.550.457.84/api/category/list")!
URLSession.shared.dataTask(with: url) { (data, response, error) in
if error == nil {
do {
self.places = try JSONDecoder().decode([Post].self, from: data! )
} catch {
print("Error during JSON serialization: \(error.localizedDescription)")
}
}
}.resume()
}
I get a message in the debugger -> Error during JSON serialization: The data couldn’t be read because it isn’t in the correct format.
i also try to change -> self.places = try JSONDecoder().decode([Post].self, from: data! )` to
self.places = try JSONDecoder().decode(Post.self, from: data! )
then I got the error -> Cannot assign the value of type 'Post' to type '[Post]?'
here my model class is
struct Post : Codable {
let _id : String?
let name : String?
let icons : String?
let createdAt : String?
let updatedAt : String?
let __v : Int?
let id : String?
enum CodingKeys: String, CodingKey {
case _id = "_id"
case name = "name"
case icons = "icons"
case createdAt = "createdAt"
case updatedAt = "updatedAt"
case __v = "__v"
case id = "id"
}

The error message basically says "Your model and coming data model(JSON) are not equal". You need to change your model like :
struct BaseModel : Codable{
var status : String?
var data : [Post]?
}
And when you try to deserialize it you need to make a instance of that BaseModel so
var baseModel: BaseModel?
var places: [Post]?
when response success
do {
let responseData = try JSONDecoder().decode(BaseModel.self, from: data! )
self.places = responseData.data!
} catch {
print("Error during JSON serialization: \(error.localizedDescription
}

Related

Managing Dynamic Keys in response through Codable Protocol

I need to make the codable model for the dynamic keys of dictionary coming from response below is the response I'm getting.
{
"data" : [
{
"desc1" : null,
"file1" : "uploads\/posts\/Aug-2021\/1629271422310452767"
},
{
"desc2" : "hello",
"file2" : "uploads\/posts\/Aug-2021\/162927142279356160WhatsApp+Image+2021-07-02+at+12.09.14+PM.jpeg"
}
],
"status" : "success"
}
This desc1 and file1 is dynamic till like file1, file2 and so on, I need to have codable model for that below is my model that is not supportive.
struct ListModel: Codable {
public var data: [data]?
}
struct data: Codable {
let file : String?
let desc : String?
}
Anything support by codable protocol for that. Thanks in Advance.
You need a custom initializer. Of course this will only work if your json will always come formatted as described:
struct File {
var file: String? = ""
var desc: String? = ""
}
struct Response {
let files: [File]
let status: String
enum CodingKeys: String, CodingKey {
case files = "data", status
}
}
extension Response: Decodable {
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.status = try container.decode(String.self, forKey: .status)
let elements = try container.decode([[String: String?]].self, forKey: .files)
self.files = elements.reduce(into: []) {
var file = File()
for (key, value) in $1 {
if key.hasPrefix("file") {
file.file = value
} else if key.hasPrefix("desc") {
file.desc = value
}
}
$0.append(file)
}
}
}
Playground testing:
let json = """
{
"data" : [
{
"desc1" : null,
"file1" : "uploads/posts/Aug-2021/1629271422310452767"
},
{
"desc2" : "hello",
"file2" : "uploads/posts/Aug-2021/162927142279356160WhatsApp+Image+2021-07-02+at+12.09.14+PM.jpeg"
}
],
"status" : "success"
}
"""
do {
let response = try JSONDecoder().decode(Response.self, from: Data(json.utf8))
print(response)
} catch {
print(error)
}
This will print:
Response(files: [File(file: Optional("uploads/posts/Aug-2021/1629271422310452767"), desc: nil), File(file: Optional("uploads/posts/Aug-2021/162927142279356160WhatsApp+Image+2021-07-02+at+12.09.14+PM.jpeg"), desc: Optional("hello"))], status: "success")

Struggling to decode a JSON

I have this json I get from an API:
{
"product_id": "id",
"name": "name",
"manufacturer": "manufacturer",
"image_url": "url",
"additional_info": "",
"store_id": "id",
"store_name": "name",
"owner_id": "id",
"quantities": [
{
"ml": 1,
"price": 2
},
{
"ml": 1,
"price": 2
},
{
"ml": 1,
"price": 2
},
{
"ml": 1,
"price": 2
}
]
}
This is the model I have:
struct Quantity: Codable {
var ml: Int
var price: Float
enum CodingKeys : String, CodingKey {
case ml = "ml"
case price = "price"
}
}
struct Product: Codable {
let uuid: String
let productName: String
let productManufacturer: String
let productImage: String
let quantities: [Quantity]
let additionalInfo: String?
let storeID: String
let storeName: String
let ownerID: String
enum CodingKeys : String, CodingKey {
case uuid = "product_id"
case productName = "name"
case productManufacturer = "manufacturer"
case productImage = "image_url"
case quantities = "quantities"
case additionalInfo = "additional_info"
case storeID = "store_id"
case storeName = "store_name"
case ownerID = "owner_id"
}
}
I'm trying to decode this, but I get this error:
Failed to decode json: typeMismatch(Swift.Array<Any>, Swift.DecodingError.Context(codingPath: [], debugDescription: "Expected to decode Array<Any> but found a dictionary instead.", underlyingError: nil))
This is how I decode the JSON:
func fetchGenericData<T: Decodable>(request: URLRequest, completion: #escaping(T) -> ()) {
URLSession.shared.dataTask(with: request) { (data, response, error) in
if let error = error {
//TODO: Handle error.
print(error)
return
}
guard let data = data else { return }
do {
let object = try self.decoder.decode(T.self, from: data)
completion(object)
}catch let jsonError{
print("Failed to decode json: ", jsonError)
}
}.resume()
}
I understand the error, but I don't know how to fix it.
I don't know what is wrong here, I thought maybe the Quantity, but, to me, it looks like it should decode it just fine, just as "product_id" has value, so each quantity object has it's key and it's value, that doesn't look like a dictionary to me.
Is the problem even in the Quantity object?
My guess is You seem to be trying to decode an array of products but actually it only returning a dictionary that contains only one product. Try this code which tries to decode your JSON into a single product and it should fix the issue:
fetchGenericData(request: request) { (product: Product) in
print(product)
}
Instead of what you might be doing right now which could look like this:
fetchGenericData(request: request) { (products: [Product]) in
print(product)
}
You should use try JSONDecoder().decode(Product.self, from: data)
I think you pass [Product].self

Parse plain json string value inside a response swift codable

I want to parse this response. Everything else is parsed - except Images. I receive it in a string but then I cannot convert it into a dictionary. This is my model.
struct PropertyList: Decodable {
let result: Property?
let success: Bool = false
struct Property: Decodable {
let name: String?
let description: String?
let propertyType: PropertyType
let latitude, longitude: String
let images: String?
let areaSize:Int?
let threeSixtyView: String?
let threeDModel: String?
enum CodingKeys: String, CodingKey {
case name
case propertyDescription = "description"
case propertyType, latitude, longitude
case threeDModel = "threeDModel"
case images = "images"
}
}
}
struct PropertyType: Codable {
let id: Int
let name, propertyTypeDescription: String
enum CodingKeys: String, CodingKey {
case id, name
case propertyTypeDescription = "description"
}
}
API Response :
"name": "Al Deyar",
"description": "Al Deyar Villa",
"propertyType": {
"id": 709415277471,
"name": "villa",
"description": "villa"
},
"latitude": "1",
"longitude": "2",
"viewOfWater": null,
"threeDModel": "https://viewer.archilogic.com/?sceneId=80d4b3bb-98de-4279-a867-633bf67c6e72&s=m2fss0p3slst",
"images": "[{\"id\": 1, \"image\":\"https://neigborhood-images.s3.amazonaws.com/property/1BEEA0B6-A2B1-4D5E-837B-9C2B00F46EE4_2048x2048.jpg\"},\n{\"id\": 2, \"image\":\"https://neigborhood-images.s3.amazonaws.com/property/984D2D29-2448-4B68-827F-EC912AB9AF14_2048x2048.jpg\"},\n{\"id\": 3, \"image\":\"https://neigborhood-images.s3.amazonaws.com/property/EBARZA-_0002_Layer_11_2048x2048.jpg\"},\n{\"id\": 4, \"image\":\"https://neigborhood-images.s3.amazonaws.com/property/EBARZA-_0002_Layer_10_ad2d92e2-3740-4d1d-8e9c-ed41cf89c3b2_2048x2048.jpg\"},\n{\"id\": 5, \"image\":\"https://neigborhood-images.s3.amazonaws.com/property/EBARZA-furniture_0001_Layer_21_2048x2048.jpg\"},\n{\"id\": 6, \"image\":\"https://neigborhood-images.s3.amazonaws.com/property/EBARZA-furniture_0044_Layer_5_2048x2048.jpg\"},\n{\"id\": 7, \"image\":\"https://neigborhood-images.s3.amazonaws.com/property/EBARZA-furniture_0042_Layer_3_2048x2048.jpg\"}]"
> Blockquote
images is a nested JSON string which has to be decoded separately.
A solution is to declare the value containing the nested JSON string as struct (ImageJSON) with a singleValueContainer and decode the string on second level.
I left out the properties which are not in the JSON
let json = """
{
"success" : true,
"result" : {
"name": "Al Deyar",
"description": "Al Deyar Villa",
"propertyType": {
"id": 709415277471,
"name": "villa",
"description": "villa"
},
"latitude": "1",
"longitude": "2",
"viewOfWater": null,
"threeDModel": "https://viewer.archilogic.com/?sceneId=80d4b3bb-98de-4279-a867-633bf67c6e72&s=m2fss0p3slst",
"images":"[{\\"id\\": 1, \\"image\\":\\"https://neigborhood-images.s3.amazonaws.com/property/1BEEA0B6-A2B1-4D5E-837B-9C2B00F46EE4_2048x2048.jpg\\"},{\\"id\\": 2,\\"image\\":\\"https://neigborhood-images.s3.amazonaws.com/property/984D2D29-2448-4B68-827F-EC912AB9AF14_2048x2048.jpg\\"},{\\"id\\": 3,\\"image\\":\\"https://neigborhood-images.s3.amazonaws.com/property/EBARZA-_0002_Layer_11_2048x2048.jpg\\"},{\\"id\\": 4,\\"image\\":\\"https://neigborhood-images.s3.amazonaws.com/property/EBARZA-_0002_Layer_10_ad2d92e2-3740-4d1d-8e9c-ed41cf89c3b2_2048x2048.jpg\\"},{\\"id\\": 5,\\"image\\":\\"https://neigborhood-images.s3.amazonaws.com/property/EBARZA-furniture_0001_Layer_21_2048x2048.jpg\\"},{\\"id\\": 6,\\"image\\":\\"https://neigborhood-images.s3.amazonaws.com/property/EBARZA-furniture_0044_Layer_5_2048x2048.jpg\\"},{\\"id\\": 7,\\"image\\":\\"https://neigborhood-images.s3.amazonaws.com/property/EBARZA-furniture_0042_Layer_3_2048x2048.jpg\\"}]"
}
}
"""
struct Response : Decodable {
let result: Property
let success: Bool
}
struct Property: Decodable {
let name: String
let description: String
let propertyType: PropertyType
let latitude, longitude: String
let images: ImageJSON
let threeDModel: URL
}
struct PropertyType: Codable {
let id: Int
let name, description: String
}
struct Image : Decodable {
let id : Int
let image : URL
}
struct ImageJSON : Decodable {
let images : [Image]
init(from decoder : Decoder) throws {
let container = try decoder.singleValueContainer()
let imageJSONString = try container.decode(String.self)
let imageJSONData = Data(imageJSONString.utf8)
images = try JSONDecoder().decode([Image].self, from: imageJSONData)
}
}
let data = Data(json.utf8)
do {
let decoder = JSONDecoder()
let response = try decoder.decode(Response.self, from: data)
let images = response.result.images.images
print(images)
} catch {
print(error)
}
Try the below code :
Update your json :
let json = """
{
"name": "Al Deyar",
"description": "Al Deyar Villa",
"propertyType": {
"id": 709415277471,
"name": "villa",
"description": "villa"
},
"latitude": "1",
"longitude": "2",
"viewOfWater": null,
"threeDModel":"https://viewer.archilogic.com/?sceneId=80d4b3bb-98de-4279-a867-633bf67c6e72&s=m2fss0p3slst",
"images": [
{"id": 1, "image": "https://neigborhood-images.s3.amazonaws.com/property/1BEEA0B6-A2B1-4D5E-837B-9C2B00F46EE4_2048x2048.jpg"},
{"id": 2, "image": "https://neigborhood-images.s3.amazonaws.com/property/984D2D29-2448-4B68-827F-EC912AB9AF14_2048x2048.jpg"},
{"id": 3, "image": "https://neigborhood-images.s3.amazonaws.com/property/EBARZA-_0002_Layer_11_2048x2048.jpg"},
{"id": 4, "image": "https://neigborhood-images.s3.amazonaws.com/property/EBARZA-_0002_Layer_10_ad2d92e2-3740-4d1d-8e9c-ed41cf89c3b2_2048x2048.jpg"},
{"id": 5, "image": "https://neigborhood-images.s3.amazonaws.com/property/EBARZA-furniture_0001_Layer_21_2048x2048.jpg"},
{"id": 6, "image": "https://neigborhood-images.s3.amazonaws.com/property/EBARZA-furniture_0044_Layer_5_2048x2048.jpg"},
{"id": 7, "image": "https://neigborhood-images.s3.amazonaws.com/property/EBARZA-furniture_0042_Layer_3_2048x2048.jpg"}
]
}
"""
Update your codable class :
struct Property : Codable {
let descriptionField : String?
let images : [Images]?
let latitude : String?
let longitude : String?
let name : String?
let propertyType : PropertyType?
let threeDModel : String?
let viewOfWater : String?
enum CodingKeys: String, CodingKey {
case descriptionField = "description"
case images = "images"
case latitude = "latitude"
case longitude = "longitude"
case name = "name"
case propertyType = "propertyType"
case threeDModel = "threeDModel"
case viewOfWater = "viewOfWater"
}
}
struct PropertyType : Codable {
let descriptionField : String?
let id : Int?
let name : String?
enum CodingKeys: String, CodingKey {
case descriptionField = "description"
case id = "id"
case name = "name"
}
}
struct Images : Codable {
let id : Int?
let image : String?
}
if let jsonData = json.data(using: .utf8) {
let decoder = JSONDecoder()
do {
let jsonModel = try decoder.decode(Property.self, from: jsonData)
print(jsonModel)
} catch {
print("Error = \(error)")
}
}
Create an image type as below,
struct PropertyImage: Decodable {
var id: Int
var image: String?
}
Now, get data from the images string and decode an array of property images as below,
if let data = property.images.data(using: .utf8) {
do {
let images = try JSONDecoder().decode([PropertyImage].self, from: data)
images.forEach { image in
print(image.id)
print(image.image)
}
} catch {
print(error)
}
}

How to parse nested json? Swift

i have a local json string and i am trying to parse it but when i try to do so, i am constantly getting an error. I have also seen this error in nested dictionary but couldnt find an error.
Below is the json string
let jsonNestedString = "{\"meta\":{\"page\":1,\"total_pages\":4,\"per_page\":10,\"total_records\" : 38}, \"reweries\":[\"id\":1234,\"name\":\"saintArnold\"},{\"id\":52892,\"name\":\"buffalo bayou\"]}"
i am doing this process via Codable and below is the struct i have created for the same
struct PagedBreweries:Codable{
struct Meta:Codable{
let page : Int
let total_pages:Int
let per_page:Int
let total_records: Int
enum CodingKeys: String, CodingKey{
case page
case total_pages
case per_page
case total_records
}
}
struct Brewery :Codable{
let id:Int
let name:String
}
let meta:Meta
let breweries :[Brewery]
}
then this data is parsed to a function as below
func jsonNested(){
let jsonData = jsonNestedString.data(using: .utf8)
let decoder = JSONDecoder()
let data = try! decoder.decode(PagedBreweries.Meta.self, from: jsonData!)
print(data)
}
and when i try to build, the error i get is present in try! decoder.decode
command
and the error is
Thread 1: Fatal error: 'try!' expression unexpectedly raised an error: Swift.DecodingError.keyNotFound(CodingKeys(stringValue: "page", intValue: nil), Swift.DecodingError.Context(codingPath: [], debugDescription: "No value associated with key CodingKeys(stringValue: \"page\", intValue: nil) (\"page\").", underlyingError: nil))
Could any one provide a solution?
Thanks in advance
Correct json
{
"meta": {
"page": 1,
"total_pages": 4,
"per_page": 10,
"total_records": 38
},
"reweries": [{
"id": 1234,
"name": "saintArnold"
},
{
"id": 52892,
"name": "buffalo bayou"
}
]
}
struct Root: Codable {
let meta: Meta
let reweries: [Rewery]
}
struct Meta: Codable {
let page, totalPages, perPage, totalRecords: Int
enum CodingKeys: String, CodingKey { // snake case may be used
case age = "page"
case totalPages = "total_pages"
case perPage = "per_page"
case totalRecords = "total_records"
}
}
struct Rewery: Codable {
let id: Int
let name: String
}
let jsonNestedString = """
{\"meta\":{\"page\":1,\"total_pages\":4,\"per_page\":10,\"total_records\":38}, \"reweries\":[{\"id\":1234,\"name\":\"saintArnold\"},{\"id\":52892,\"name\":\"buffalo bayou\"}]}
"""
// or
let jsonNestedString = """
{
"meta": {
"page": 1,
"total_pages": 4,
"per_page": 10,
"total_records": 38
},
"reweries": [{
"id": 1234,
"name": "saintArnold"
},
{
"id": 52892,
"name": "buffalo bayou"
}
]
}
"""
do {
let jsonData = jsonNestedString.data(using: .utf8)
let decoder = JSONDecoder()
let data = try decoder.decode(Root.self, from: jsonData!)
print(data)
} catch {
print(error)
}
The JSON is corrupt and you are decoding the wrong root object.
This is the correct JSON being decoded with your structs and conforming to the Swift naming convention
let jsonNestedString = """
{"meta":{"page":1,"total_pages":4,"per_page":10,"total_records" : 38}, "breweries":[{"id":1234,"name":"saintArnold"},{"id":52892,"name":"buffalo bayou"}]}
"""
struct PagedBreweries : Codable {
struct Meta : Codable {
let page : Int
let totalPages:Int
let perPage:Int
let totalRecords: Int
}
struct Brewery : Codable {
let id:Int
let name:String
}
let meta:Meta
let breweries :[Brewery]
}
func jsonNested(){
let jsonData = Data(jsonNestedString.utf8)
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let data = try! decoder.decode(PagedBreweries.self, from: jsonData)
print(data)
}
jsonNested()
// Printed result:
// PagedBreweries(meta: __lldb_expr_1.PagedBreweries.Meta(page: 1, totalPages: 4, perPage: 10, totalRecords: 38), breweries: [__lldb_expr_1.PagedBreweries.Brewery(id: 1234, name: "saintArnold"), __lldb_expr_1.PagedBreweries.Brewery(id: 52892, name: "buffalo bayou")])

Swift ObjectMapper: How to parse array inside of an array

This is my JSON response:
[
[
{
"id": 22,
"request_id": "rqst5c12fc9e856ae1.06631647",
"business_name": "Code Viable",
"business_email": "code#viable.com",
"title": "Apache Load/Ubuntu",
}
],
[
{
"id": 24,
"request_id": "rqst5c130cae6f7609.41056231",
"business_name": "Code Viable",
"business_email": "code#viable.com",
"title": "Load",
}
]
]
This JSON structure got an array inside of an array, the object of the inner array is what I am trying to parse. Here is the my mapper:
struct JobResponseDataObject: Mappable {
init?(map: Map) {
}
var id: Int?
var requestId: String?
var businessName: String?
var businessEmail: String?
mutating func mapping(map: Map) {
id <- map["id"]
requestId <- map["request_id"]
businessName <- map["business_name"]
businessEmail <- map["business_email"]
}
}
I have tried create another mapper struct to hold the array of objects [JobResponseDataObject] and use Alamofire's responseArray with it, but it didn't work. I have also tried prefixing my json id with 0. but that didn't work too. Please help
Thank
So here's the deal...Codable is a pretty cool protocol from Apple to handle parsing JSON responses from APIs. What you're getting back is an array of arrays, so your stuff's gonna be look like this:
[[ResponseObject]]
So anyway, you'd make a struct of your object, like so:
struct ResponseObject: Codable {
let id: Int?
let requestId: String?
let businessName: String?
let businessEmail: String?
let title: String?
}
You'll note I changed the key name a bit (instead of request_id, I used requestId). The reason is JSONDecoder has a property called keyDecodingStrategy which presents an enum of canned decoding strategies you can select from. You'd do convertFromSnakeCase.
Here's code you can dump into a playground to tinker with. Basically, declare your struct, match it up to whatever the keys are in your JSON, declare a decoder, feed it a decoding strategy, and then decode it.
Here's how you could do an Alamofire call:
private let backgroundThread = DispatchQueue(label: "background",
qos: .userInitiated,
attributes: .concurrent,
autoreleaseFrequency: .inherit,
target: nil)
Alamofire.request(url).responseJSON(queue: backgroundThread) { (response) in
guard response.result.error == nil else {
print("💥KABOOM!💥")
return
}
if let data = response.data {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
do {
let parsedResponse = try decoder.decode([[ResponseObject]].self, from: data)
print(parsedResponse)
} catch {
print(error.localizedDescription)
}
}
}
Here's code you can chuck in a playground.
import UIKit
let json = """
[
[
{
"id": 22,
"request_id": "rqst5c12fc9e856ae1.06631647",
"business_name": "Code Viable",
"business_email": "code#viable.com",
"title": "Apache Load/Ubuntu",
}
],
[
{
"id": 24,
"request_id": "rqst5c130cae6f7609.41056231",
"business_name": "Code Viable",
"business_email": "code#viable.com",
"title": "Load",
}
]
]
"""
struct ResponseObject: Codable {
let id: Int?
let requestId: String?
let businessName: String?
let businessEmail: String?
let title: String?
}
if let data = json.data(using: .utf8) {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
do {
let parsedResponse = try decoder.decode([[ResponseObject]].self, from: data)
print(parsedResponse)
} catch {
print(error.localizedDescription)
}
}
You should use this JobResponseDataObject struct as [[JobResponseDataObject]] instead of [JobResponseDataObject] - where you are making a property using this struct in your parent struct or class.
You can use Codable here for mapping the JSON response, The JobResponseDataObject struct should look like,
struct JobResponseDataObject: Codable {
var id: Int?
var requestId: String?
var businessName: String?
var businessEmail: String?
var title: String?
private enum CodingKeys: String, CodingKey {
case id = "id"
case requestId = "request_id"
case businessName = "business_name"
case businessEmail = "business_email"
case title = "title"
}
}
let json = JSON(responseJSON: jsonData)
do {
if let value = try? json.rawData(){
let response = try! JSONDecoder().decode([[JobResponseDataObject]].self, from: value)
}
} catch {
print(error.localizedDescription)
}

Resources