Как быстро декодировать данные ответа http из URLSession и сопоставить со структурой ответа

Я новичок в быстром программировании. Мне удалось получить успешный ответ от URLSession, но я не могу разобрать (декодировать) объект данных в желаемую структуру APIResponse.

это мой код запроса URL:

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()
    }

и вот моя структура ответа, для которой мне нужны данные ответа, чтобы использовать их в моем коде:

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"
    }

это json с сервера, который я пытаюсь декодировать

 {
    "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
    }
 }

всякий раз, когда я запускаю этот код, я получаю сообщение о невозможности анализа данных из ошибки ответа. Был бы очень признателен, если бы кто-нибудь сказал мне, что я делаю неправильно здесь


person Rijo Samuel    schedule 13.04.2021    source источник


Ответы (1)


Проблема заключается в расшифровке id в Offer. Замените свой Offer на это:

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"
            }
        }
    }
}

Заметки

  • Во-первых, вы не должны были избавиться от ошибки. Вместо этого вы можете распечатать ошибку и попытаться выяснить, что происходит не так.
  • Если вы объявите enum CodingKeys: String, CodingKey, необработанное значение для каждого случая должно отличаться от другого, иначе Xcode будет жаловаться. В вашем случае он не жаловался, потому что id является требованием протокола Identifiable, но он также даже не использовал необработанное значение, которое вы установили для id. Если вы хотите использовать один и тот же ключ для двух разных переменных, вы должны сделать то же самое, что я сделал выше.

Лучший код

Это будет работать так же, как и ваш код, но гораздо чище:

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
    }
}

По сути, он говорит, что каждый раз, когда вам нужно id, берите его у r. Вы также можете удалить stringValue из CodingKeys, как это сделал я, и использовать конформацию CodingKeys: String.

person Mahdi BM    schedule 13.04.2021
comment
большое спасибо.. Я получил правильный ответ.. очень ценю это.. обязательно рассмотрю ваш совет.. не могли бы вы также прислать мне общую версию того же кода, который я могу использовать для нескольких API, где только раздел данных изменений ответа json все остальное остается прежним... - person Rijo Samuel; 13.04.2021