2
votes

I am trying to parse the API response using Codable class.

Below is the response :

{"status":200,"message":"","success":1,"data":[{"event_id":"26","event_name":"Mens Night","event_desc":"Hot Mens Night","from_date":"2019-02-08","to_date":"2019-03-09","bar_id":"62","bar_names":"Autumn Bar & Bistro","bar_ids":"62","offer_image":"https:\/\/www.tippler.app\/manager\/uploads\/events\/mens_night.jpg","from_time":"19:00:00","to_time":"01:30:00"},{"event_id":"36","event_name":"Karaoke Night","event_desc":"Karaoke NIght with Brian Rub","from_date":"2019-02-08","to_date":"2019-02-09","bar_id":"146","bar_names":"Amuse Resto Bar","bar_ids":"146","offer_image":"https:\/\/www.tippler.app\/manager\/uploads\/events\/Screenshot_20190208-155223__011.jpg","from_time":"21:00:00","to_time":"01:00:00"},{"event_id":"37","event_name":"Sufi Nights","event_desc":"Singers From mumbai","from_date":"2019-02-08","to_date":"2019-02-09","bar_id":"66","bar_names":"Cavalry The Lounge","bar_ids":"66","offer_image":"https:\/\/www.tippler.app\/manager\/uploads\/events\/SUFI-FEATURED.jpg","from_time":"20:00:00","to_time":"01:30:00"},{"event_id":"39","event_name":"BOLLYWOOD NIGHT","event_desc":"BHANGRA AND LIVE DHOL","from_date":"2019-02-09","to_date":"2019-02-10","bar_id":"103","bar_names":"B Desi","bar_ids":"103","offer_image":"https:\/\/www.tippler.app\/manager\/uploads\/events\/bollywood-nights-1.jpg","from_time":"21:00:00","to_time":"01:00:00"}],"error_dev":""}

I Codable class is :

struct Events: Codable {
    var event_id: String
    var event_name: String
    var event_desc: String
    var from_date: String
    var to_date: String
    var bar_id: String
    var bar_names: String
    var bar_ids: String
    var offer_image: String
    var from_time: String
    var to_time: String

    enum CodingKeys: String, CodingKey {
        case event_id = "event_id"
        case event_name = "event_name"
        case event_desc = "event_desc"
        case from_date = "from_date"
        case to_date = "to_date"
        case bar_id = "bar_id"
        case bar_names = "bar_names"
        case bar_ids = "bar_ids"
        case offer_image = "offer_image"
        case from_time = "from_time"
        case to_time = "to_time"
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(event_id, forKey: .event_id)
        try container.encode(event_name, forKey: .event_name)
        try? container.encode(event_desc, forKey: .event_desc)
        try? container.encode(from_date, forKey: .from_date)
        try container.encode(to_date, forKey: .to_date)
        try container.encode(bar_id, forKey: .bar_id)
        try container.encode(bar_names, forKey: .bar_names)
        try container.encode(bar_ids, forKey: .bar_ids)
        try container.encode(offer_image, forKey: .offer_image)
        try container.encode(from_time, forKey: .from_time)
        try container.encode(to_time, forKey: .to_time)
    }


    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        event_id = try values.decode(String.self, forKey: .event_id)
        event_name = try values.decode(String.self, forKey: .event_name)
        event_desc = try values.decode(String.self, forKey: .event_desc)
        from_date = try values.decode(String.self, forKey: .from_date)
        to_date = try values.decode(String.self, forKey: .to_date)
        bar_id = try values.decode(String.self, forKey: .bar_id)
        bar_names = try values.decode(String.self, forKey: .bar_names)
        bar_ids = try values.decode(String.self, forKey: .bar_ids)
        offer_image = try values.decode(String.self, forKey: .offer_image)
        from_time = try values.decode(String.self, forKey: .from_time)
        to_time = try values.decode(String.self, forKey: .to_time)
    }


}

It is returning with the response error

keyNotFound(CodingKeys(stringValue: "event_id", intValue: nil), Swift.DecodingError.Context(codingPath: [CodingKeys>(stringValue: "data", intValue: nil), _JSONKey(stringValue: "Index 0", intValue: 0)], debugDescription: "No value associated with key CodingKeys(stringValue: \"event_id\", intValue: nil) (\"event_id\").", underlyingError: nil))

please suggest me the solution.

3

3 Answers

3
votes

Your codable structure not matching with response as they have status , message success and errorDev key

Try this

struct Response: Codable {
    let status: Int?
    let message: String?
    let success: Int?
    let data: [Events]?
    let errorDev: String?

    enum CodingKeys: String, CodingKey {
        case status, message, success, data
        case errorDev = "error_dev"
    }
}
struct Events: Codable {
    let eventID, eventName, eventDesc, fromDate: String?
    let toDate, barID, barNames, barIDs: String?
    let offerImage: String?
    let fromTime, toTime: String?

    enum CodingKeys: String, CodingKey {
        case eventID = "event_id"
        case eventName = "event_name"
        case eventDesc = "event_desc"
        case fromDate = "from_date"
        case toDate = "to_date"
        case barID = "bar_id"
        case barNames = "bar_names"
        case barIDs = "bar_ids"
        case offerImage = "offer_image"
        case fromTime = "from_time"
        case toTime = "to_time"
    }
}

EDIT

I can provide you more generic solution. Suppose all API has the same response but only data key is different then

struct GeneralResponse<T:Codable>: Codable {
     let status: Int?
     let message: String?
     let success: Int?
     let data: T?
     let errorDev: String?

    enum CodingKeys: String, CodingKey {
        case message = "msg"
        case code = "code"
        case data = "data" 
      // add more keys
    }

    public init(from decoder:Decoder) throws {
        let contaienr = try  decoder.container(keyedBy: CodingKeys.self)
        message = try contaienr.decode(String.self, forKey: .message)
        // Decode Other keys 

        do {
            let object = try contaienr.decodeIfPresent(T.self, forKey: .data)
            data = object

        } catch {
            data = nil
        }

    }

}

Now For every Response You Can

use GenericResponse<[Event]> or GenericResponse<Login> or GenericResponse<[UserList]>

Hope it is helpful

1
votes
struct Events : Codabel {
   let status: Int?
   let message: String?
   let success: Int?
   let data: [Event]?
   let errorDev: String?
}

struct Datum: Codable {
    let eventID, eventName, eventDesc, fromDate, offerImage, fromTime, toTime : String?
    let toDate, barID, barNames, barIDs: String?
}

no need to write down CodingKeys for snake cases,

While decoding, you just write

let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let responseModel = try decoder.decode(Events.self, from: data) // data from response 
0
votes

There are couple of errors in your code as follow...

  • event_id is of type String in your code but is is Int type.
  • bar_id is of type String in your code but is is Int type.
  • bar_ids is of type String in your code but is is Int type.

You need to correct these variable types.

Note: Wherever in response you find a number then it will be of Int type, a number with a decimal points like 12.123 then if will be of Double type by default. Directly assuming it as string is not acceptable in swift.