Rijo Samuel
Rijo Samuel

Reputation: 291

How to decode an http response data from URLSession and map to a response structure swift

I am new to swift programming..was able to obtain a successful response from URLSession but I am unable to parse (decode) the data object to my desired APIResponse Structure

this is my url request code:

func load(urlRequest: URLRequest, withCompletion completion: @escaping (_ response: APIResponse) -> Void) {
        
        let task = URLSession.shared.dataTask(with: urlRequest) { (data, response, error) in
            
            guard error == nil else {
                print("Error fetching data from server\nERROR: \(String(describing: error))")
                return
            }
            
            guard let jsonData = data else {
                print("Response Data is empty")
                return
            }
            
            printResponseBody(response: data)
            
            let decoder = JSONDecoder()
            let response = try? decoder.decode(APIResponse.self, from: jsonData)
            
            guard let decodedResponse = response else {
                print("Unable to parse data from response")
                return
            }
            
            print("Decoded Response: ", decodedResponse)
            
            DispatchQueue.main.async { completion(decodedResponse) }
        }
        
        task.resume()
    }

and here is my response structure which i need the response data to map to to use in my code:

struct APIResponse: Codable {
    
    let responseCode: Int
    let data: ResultSet
    let meta: Meta

    enum CodingKeys: String, CodingKey {
        case responseCode = "response_code"
        case data, meta
    }
}

// MARK: - DataClass
struct ResultSet: Codable {
    
    let appVersionUpdate: String
    let offers: [Offer]
    let rate: Int

    enum CodingKeys: String, CodingKey {
        case appVersionUpdate = "app_version_update"
        case offers, rate
    }
}

// MARK: - Offer
struct Offer: Codable, Identifiable {
    
    let id: Int
    let title: String
    let image: String?
    let r, resultCount: Double

    enum CodingKeys: String, CodingKey {
        case id, r = "r"
        case title, image
        case resultCount = "result_count"
    }
}

// MARK: - Meta
struct Meta: Codable {
    
    let apiVersion: Int

    enum CodingKeys: String, CodingKey {
        case apiVersion = "api_version"
    }

this is the json from server which I am trying to decode

 {
    "response_code": 0,
    "data": {
        "app_version_update": "",
        "offers": [
            {
                "title": "Special Scheme1",
                "image": "http://59.145.109.138:11101/Offers/BGL_banner_1080_x_540_1.jpg",
                "r": 1.0,
                "result_count": 5.0
            },
            {
                "title": "test 1",
                "image": "http://59.145.109.138:11101/Offers/Yoho-National-Park2018-10-27_10-10-52-11.jpg",
                "r": 2.0,
                "result_count": 5.0
            },
            {
                "title": "Offer Test 1234444",
                "image": "http://59.145.109.138:11101/Offers/Stanley-Park2018-10-27_10-11-27-44.jpg",
                "r": 3.0,
                "result_count": 5.0
            }
        ],
        "rate": 2000
    },
    "meta": {
        "api_version": 2.0
    }
 }

whenever i run this code i am getting the "unable to parse data from response" error. Would really appreciate if someone tells me what I am doing wrong here

Upvotes: 2

Views: 2566

Answers (1)

Mahdi BM
Mahdi BM

Reputation: 2104

The problem is with decoding the id in Offer. Replace your Offer with this:

struct Offer: Codable, Identifiable {
    
    let id: Int
    let title: String
    let image: String?
    let r, resultCount: Int

    enum CodingKeys: CodingKey {
        case id, r, title, image, resultCount
        
        var stringValue: String {
            switch self {
            case .id, .r: return "r"
            case .title: return "title"
            case .image: return "image"
            case .resultCount: return "result_count"
            }
        }
    }
}

Notes

  • First of all, you shouldn't have gotten rid of the error. Instead you could print the error and try to find out whats going wrong.
  • If you declare enum CodingKeys: String, CodingKey, the raw value for each case Must be different from the other or the Xcode will complain. In your case it didn't complain because id is the requirement of the Identifiable protocol, but it also didn't even use the raw value that you set for id. If you want to use the same key for 2 different variables, you should do the same thing i did above.

Better code

This will work the same as the your code, but is quite cleaner:

struct Offer: Codable, Identifiable {
    
    var id: Int { r }
    let title: String
    let image: String?
    let r, resultCount: Int

    enum CodingKeys: String, CodingKey {
        case r, title, image, resultCount
    }
}

It basically says everytime you need id, get it from r. You can also remove the stringValue in CodingKeys as i did, and use the CodingKeys: String conformation.

Upvotes: 1

Related Questions