0

I am trying to decode an array of my model objects(Catalog) from a JSON that looks like this after serialization of corresponding 'Data' object.

 { "id" : 5,
   "catalogs" : [ {catalogKeyValue1},{catalogKeyValue2}]
 }

My model object looks like this

struct Catalog : Codable{
 var id : Int
 var name : String
 var categoryId : Int
 var minProductPrice : Int
 var maxProductDiscount : Int?
 var shareText : String
 var collageImage : String
 var collageImageAspectRatio : Double?
 var shipping : [String : Int]?
 var description : String
}

I need to get an array of Catalogs (which is nested against 'catalogs' key in JSON) after decoding.I fully understand using nested containers and writing custom initialaizer for Catalog struct .How can I achieve this without having to write another Codable struct for outer JSOn that looks like this

struct CatalogArray: Codable {
 var catalogs : [Catalog]
}

and then do something like this to get a decoded array of Catalogs

let catalogArray = try decoder.decode(CatalogArray.self, from: validData)

My problem is I dont have any need for this catalogArray struct. Is there a way of getting Catalog model objects decoded without having to create unnecessary nested structs.

  • 1
    Possible duplicate of [How to decode a nested JSON struct with Swift Decodable protocol?](https://stackoverflow.com/questions/44549310/how-to-decode-a-nested-json-struct-with-swift-decodable-protocol) – Prashant Tukadiya Feb 08 '19 at 06:19
  • 1
    You need to write additional code, theoretically it should be possible to achieve this without intermediary structures, however this would mean more decoding code to write. So you'd need to write additional code anyway, with or without an intermediary struct. – Cristik Feb 08 '19 at 06:26
  • 1
    What's wrong with more structs? If you give them good names they won't clutter your code. – Sweeper Feb 08 '19 at 06:37
  • The problem with more Codable structs is that I will need to create another struct if the same array of catalogs comes with a different key in another API response. – Tarun Bhargava Feb 08 '19 at 06:43
  • @TarunBhargava You can create generic struct like my answer – Prashant Tukadiya Feb 08 '19 at 06:52
  • @Cristik I am talking about two API responses . One gives me [Catalog] with key 'catalogs' and another with ,lets say, key 'new_catalogs' . How would I not create a new struct for second case and still get my array [Catalog] for both cases? – Tarun Bhargava Feb 08 '19 at 07:50
  • @Cristik I dont understand. Can you please tell me how can I get a Catalog array like this from raw Data response of both APIs without having to change the key(`catalogs`, here in struct CatalogArray and lets say `newCatalogs` in second response) against which I will get `[Catalog]` in the JSON ? `let catalogArray = try decoder.decode(CatalogArray.self, from: validData)` – Tarun Bhargava Feb 08 '19 at 08:18

2 Answers2

0

You can do this instead of making a new Struct everytime: try container.decode([Catalog].self, forKey: "Catalogs")

Arrays of a type that is Codable are automatically Codable.

Atharva Vaidya
  • 754
  • 1
  • 5
  • 12
  • That wont work because only way to getting container will be in the init(from decoder: Decoder) method of a single Catalog Model.And after doing try container.decode([Catalog].self, forKey: "Catalogs") I will get an array of Catalogs within the initializer of a single Catalog Model object. – Tarun Bhargava Feb 08 '19 at 06:54
0

As per your comment

The problem with more Codable structs is that I will need to create another struct if the same array of catalogs comes with a different key in another API response.

You can create generic struct that can do the same for you. here is a example

struct GeneralResponse<T:Codable>: Codable {
    let code: Int
    let catalogs: T?

    enum CodingKeys: String, CodingKey {
        case code = "id"
        case catalogs = "catalogs"
    }

    public init(from decoder:Decoder) throws {
        let contaienr = try  decoder.container(keyedBy: CodingKeys.self)
        code = try contaienr.decode(Int.self, forKey: .code)


        do {
            let object = try contaienr.decodeIfPresent(T.self, forKey: .data)
            catalogs = object

        } catch {
            catalogs = nil
        }

    }

}

Now

You can use this with different catalogs type of struct

like

GeneralResponse<[Catalogs]> or GeneralResponse<[CatalogsAnother]>

Hope it is helpful

Prashant Tukadiya
  • 15,838
  • 4
  • 62
  • 98
  • That wont solve my problem as this wont work if another GeneralResponse(API Response) has `[Catalog] array` against a key different from key `"catalogs"`. This will lead to the same problem of creating another struct that has that other key. ` – Tarun Bhargava Feb 08 '19 at 07:58