By Adam Bardon


2019-06-12 07:13:36 8 Comments

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 comments

@PGDev 2019-06-12 07:34:56

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

@Adam Bardon 2019-06-12 07:48:50

The data: [Body] part works! How can I return also the Meta?

@PGDev 2019-06-12 07:50:22

I've added a sample JSON response for which the above code is working. See if you're getting anything other than that.

@Adam Bardon 2019-06-12 07:56:53

Aah, my bad, got Meta as well. Thank you! The JSON is correct(sorry for not including it).

Related Questions

Sponsored Content

1 Answered Questions

[SOLVED] Create array of structs with generics

20 Answered Questions

[SOLVED] Create Generic method constraining T to an Enum

1 Answered Questions

[SOLVED] Argument type not conforming to decodable

  • 2019-05-13 01:24:32
  • WishIHadThreeGuns
  • 23 View
  • 0 Score
  • 1 Answer
  • Tags:   swift

3 Answered Questions

[SOLVED] Getting error when using Decodable for JSON - Swift

  • 2019-03-13 14:49:42
  • user11166884
  • 50 View
  • -1 Score
  • 3 Answer
  • Tags:   json swift decodable

29 Answered Questions

[SOLVED] How to create a generic array in Java?

19 Answered Questions

[SOLVED] How to Sort a List<T> by a property in the object

5 Answered Questions

1 Answered Questions

[SOLVED] Trying to write a generic function for parsing JSON into codable Structs

3 Answered Questions

[SOLVED] Recursive generic structs with different types in Swift 4

  • 2018-03-29 19:30:03
  • alexk
  • 157 View
  • 3 Score
  • 3 Answer
  • Tags:   swift generics

7 Answered Questions

[SOLVED] How do I use reflection to call a generic method?

Sponsored Content