Декодировать структуру с помощью универсального свойства массива - PullRequest
0 голосов
/ 12 июня 2019

У меня есть следующая структура с обобщениями для ответа API с нумерацией страниц:

struct Paginable<Body> {
    let data: [Body]
    let meta: Meta
}

extension Paginable {
    struct Meta: Codable {
        let pagination: Pagination

        struct Pagination: Codable {
            let total: Int
            let count: Int
            let perPage: Int
            let currentPage: Int
            let totalPages: Int
        }
    }
}

И я хочу иметь возможность декодировать это так:

let response  = try? response.decode(to: Paginable<Segment>.self)

Итак, вот моя попытка сделать это Decodable:

extension Paginable where Body == Data {
    func decode<BodyType: Decodable>(to type: BodyType.Type) throws -> Paginable<BodyType> {
        guard let decodedJSON = try? JSONDecoder().decode(BodyType.self, from: data) else {
            throw APIError.decodingFailure
        }

        return Paginable<BodyType>(data: decodedJSON, meta: self.meta)
    }
}

Это дает мне две ошибки:

  1. Невозможно преобразовать значение типа 'Paginable.Meta' в ожидаемый тип аргумента 'Paginable <_>. Meta'

на линии с return оператором

Если я изменю свойство meta на некоторый примитивный тип, например Int, ошибка исчезнет. Но сама мета Codable, так в чем тут проблема?

  1. Невозможно преобразовать значение типа «[Данные]» в ожидаемый тип аргумента «Данные»

в строке с guard оператором

Как решить это?

1 Ответ

1 голос
/ 12 июня 2019

Вы должны соответствовать Paginable до Codable как,

struct Paginable<Body>: Codable where Body: Codable {
    let data: [Body]
    let meta: Meta
}

Изменить decode(data:to:) метод в extension Paginable на

extension Paginable {
    static func decode<BodyType: Decodable>(data: Data, to type: BodyType.Type) throws -> BodyType? {
        do {
            let response = try JSONDecoder().decode(BodyType.self, from: data)
            return response
        } catch {
            throw error
        }
    }
}

Usage:

if let data = str.data(using: .utf8) {
    do {
        let response = try Paginable<Segment>.decode(data: data, to: Paginable<Segment>.self)
        print(response)
    } catch {
        print(error)
    }
}

Edit:

JSON формат:

{
  "data": [
      {
        "name": "Name-1"
      },
      {
        "name": "Name-2"
      }
    ],
    "meta": {
      "pagination": {
        "total": 100,
        "count": 10,
        "perPage": 5,
        "currentPage": 1,
        "totalPages": 10
      }
    }
}
...