0

I have a following struct with generics for API response with pagination:

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

And I want to be able to decode it like so:

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

So here's my attempt to make it 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)
    }
}

This gives me two errors:

  1. Cannot convert value of type 'Paginable.Meta' to expected argument type 'Paginable<_>.Meta'

on the line with return statement

If I change the meta property to some primitive type like Int, the error disappears. But Meta itself is Codable, so what's to problem here?

  1. Cannot convert value of type '[Data]' to expected argument type 'Data'

on the line with guard statement

How to solve this one?

1
  • Add the definition of Segment. Also the JSON that you're trying to decode. Commented Jun 12, 2019 at 7:19

1 Answer 1

1

You need to conform Paginable to Codable like,

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

Change decode(data:to:) method in extension Paginable to,

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 format:

{
  "data": [
      {
        "name": "Name-1"
      },
      {
        "name": "Name-2"
      }
    ],
    "meta": {
      "pagination": {
        "total": 100,
        "count": 10,
        "perPage": 5,
        "currentPage": 1,
        "totalPages": 10
      }
    }
}
Sign up to request clarification or add additional context in comments.

3 Comments

The data: [Body] part works! How can I return also the Meta?
I've added a sample JSON response for which the above code is working. See if you're getting anything other than that.
Aah, my bad, got Meta as well. Thank you! The JSON is correct(sorry for not including it).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.