1

I'm accessing the data from an API with XCode(10.2.1) and Swift(5.0) and ran into a problem I cannot seem to find the answer to. I am able to get data from all other parts of the API apart from one, which has been named as a number string "750", im not sure how to grab that data when I can't use a variable that jsonDecoder can read?

This is an example of what I know won't work but gives you an idea of what I'm trying to do.

class Images: Codable {
    let "750": String

    init("750": String){
        self."750" = "750"
    }
}

Here's a snippet from the API I'm trying to get the images from:

  "id": "069f7f26",
      "sku": "AP",
      "title": "Pizza",
      "description": "A really great pizza",
      "list_price": "9.95",
      "is_vatable": true,
      "is_for_sale": false,
      "age_restricted": false,
      "box_limit": 2,
      "always_on_menu": false,
      "volume": null,
      "zone": null,
      "created_at": "2017-03-06T10:52:43+00:00",
      "attributes": [
        {
          "id": "670f0e7c",
          "title": "Allergen",
          "unit": null,
          "value": "Products manufactured in a nut environment"
        },
        {
          "id": "c29e7",
          "title": "Weight",
          "unit": "g",
          "value": "300"
        }
      ],
      "tags": [

      ],
      "images": {
        "750": {
          "src": "https:\/\/some_website.co.uk\/cms\/product_image\some_image.jpg",
          "url": "https:\/\/some_website.co.uk\/cms\/product_image\some_image.jpg",
          "width": 750
        }
      }
    },
denis_lor
  • 6,212
  • 4
  • 31
  • 55
Kriss
  • 13
  • 2

1 Answers1

1

I setup an example that better match your situation in order to give you an overview on how to parse and access your JSON information dynamically with a Dictionary data type:

import Foundation

let jsonData = """
{
    "images": {
        "750": {
          "src": "https:\\/\\/some_website.co.uk/cms/product_image/some_image.jpg",
          "url": "https:\\/\\/some_website.co.uk/cms/product_image/some_image.jpg",
          "width": 750
        }
    }
}
"""

let json = jsonData.data(using: .utf8)!

public struct Results: Codable {
    public var images: [String:Image] = [:]

    enum CodingKeys: String, CodingKey {
        case images = "images"
    }
}

public struct Image: Codable {
    public var src: String = ""
    public var url: String = ""
    public var width: Int = 0

    enum CodingKeys: String, CodingKey {
        case src = "src"
        case url = "url"
        case width = "width"
    }
}

if let results = try? JSONDecoder().decode(Results.self, from: json) {
    let imagesDict = results.images
    for (key, value) in imagesDict {
        print("Key: \(key)")
        print("Value: \(value)")
    }
}

If you try this snippet it will give you this output printed:

Key: 750
Value: Image(src: "https://some_website.co.uk/cms/product_image/some_image.jpg", url: "https://some_website.co.uk/cms/product_image/some_image.jpg", width: 750)

You can try out the snippet above online, if you copy paste it here and run it: http://online.swiftplayground.run/

### UPDATE (in response to comment)

In response to your comment, I found it easier to setup another example to show you how you can achieve that with your exact code sample that you shared in the comment itself.

I left everything as class and just added images in order to leave you an overview on how to achieve that.

In the end, I'd suggest to rename Products and Attributes into Product and Attribute. Also if there is no strong reason on why you choosed the model to be class, just change them to struct and as well if there is no strong reasons to keep most of the attributes of each model optional give them a default value as I did in the example above if you are always expecting some values/attributes to be there.

You can try and run this snippet as well in http://online.swiftplayground.run to try it out:

import Foundation

let jsonData = """
{
    "data": [
        {
            "title": "titlex",
            "description": "descx",
            "list_price": "123,456",
            "attributes": [
                {
                    "title": "titlex",
                    "unit": "unitx",
                    "value": "valuex"
                }
            ],
            "images": {
                "750": {
                "src": "https:\\/\\/some_website.co.uk/cms/product_image/some_image.jpg",
                "url": "https:\\/\\/some_website.co.uk/cms/product_image/some_image.jpg",
                "width": 750
                }
            }
        }
    ]
}
"""

let json = jsonData.data(using: .utf8)!

class AllProducts: Codable {
    let data: [Products]

    init(data: [Products]) {
        self.data = data
    }
}

class Products: Codable {
    let title: String?
    let description: String?
    let list_price: String?
    let attributes: [Attributes]?
    let images: [String:Image]?

    init(title: String, description: String, list_price: String, attributes: [Attributes], images: [String:Image]) {
        self.title = title
        self.description = description
        self.list_price = list_price
        self.attributes = attributes
        self.images = images
    }
}

class Attributes: Codable {
    let title: String?
    let unit: String?
    let value: String?

    init(title: String, unit: String, value: String) {
        self.title = title
        self.unit = unit
        self.value = value
    }
}

class Image: Codable {
    let src: String?
    let url: String?
    let width: Int?

    init(src: String, url: String, width: Int) {
        self.src = src
        self.url = url
        self.width = width
    }
}

// parsing/decoding
if let results = try? JSONDecoder().decode(AllProducts.self, from: json) {
    if let imagesDict = results.data[0].images {

    // there is an "images" for product at position 0 (the only one in my json example)
        for (key, value) in imagesDict {
            print("Key: \(key)")
            print("Value src: \(value.src)")
            print("Value url: \(value.url)")
            print("Value width: \(value.width)")
        }
    }
}

Output

Key: 750
Value src: Optional("https://some_website.co.uk/cms/product_image/some_image.jpg")
Value url: Optional("https://some_website.co.uk/cms/product_image/some_image.jpg")
Value width: Optional(750)
denis_lor
  • 6,212
  • 4
  • 31
  • 55
  • Thanks @denis_lor. This looks great but I'm having difficulty transposing it to my current codebase. I guess the problem I really have is that there are over 400 items I am trying to sort through and put into a table view. Heres what I currently have. [https://www.codepile.net/pile/vA1pDJyN](https://www.codepile.net/pile/vA1pDJyN) – Kriss Jan 20 '20 at 02:03