-3

everyone. I am making an app for iOS. I use an API that allows to fetch city's sights as JSON.

Here is an example of the data:

{
   "type":"FeatureCollection",
   "features":[
      {
         "type":"Feature",
         "id":"7281004",
         "geometry":{
            "type":"Point",
            "coordinates":[]
         },
         "properties":{
            "xid":"N5661720423",
            "name":"М. В. Захарову",
            "dist":70.53949268,
            "rate":1,
            "osm":"node/5661720423",
            "kinds":"historic,monuments_and_memorials,interesting_places,monuments"
         }
      },
... 499 objects more

My solution for parsing in SWIFT is

import Foundation

struct SightResponse: Decodable, Hashable {
    let allInfo = [String]()
    enum CodingKeys: String, CodingKey{
        case features
        case type
        
        enum FeatureKey: String, CodingKey{
            case type
            case id
        }
    }
    
    
}
extension SightResponse{
    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        let type = try values.decode(String.self, forKey: .type)
        let ContainsFeatures = values.contains(.features)
        let features = try values.decode([String: String].self, forKey: .features)
        //skips the features line
    }
}

Also, at this line I tried different variations of data type such as let features = try values.decode(String.self, forKey: .features) or let features = try values.decode([String].self, forKey: .features)

I am really confused.

I tried to change the parsing data type, but there is nothing new. I am trying to understand what I am doing wrong with my JSON data, and how I can understand what data type should be used in the future?

burnsi
  • 6,194
  • 13
  • 17
  • 27
Artemast
  • 7
  • 3

1 Answers1

0

1. Declare your Codable data types to parse the JSON objects

struct FeatureCollection: Codable {
    let type: String
    let features: [Feature]
}

struct Feature: Codable {
    let type: String
    let id: String
    let geometry: Geometry
    let properties: Properties
}

struct Geometry: Codable {
    let type: String
    let coordinates: [Double]
}

struct Properties: Codable {
    let xid: String
    let name: String
    let dist: Double
    let rate: Int
    let osm: String
    let kinds: String
}

2. Prepare some JSON mock data

// Some dummy data with 2 features in the array

let jsonString = """
{
    "type":"FeatureCollection",
    "features":[
        {
            "type":"Feature",
            "id":"1",
            "geometry":{
                "type":"Point",
                "coordinates":[]
            },
            "properties":{
                "xid":"N5661720423",
                "name":"М. В. Захарову",
                "dist":70.53949268,
                "rate":1,
                "osm":"node/5661720423",
                "kinds":"historic,monuments_and_memorials,interesting_places,monuments"
            }
        },
        {
            "type":"Feature",
            "id":"2",
            "geometry":{
                "type":"Point",
                "coordinates":[]
            },
            "properties":{
                "xid":"N5661720423",
                "name":"М. В. Захарову",
                "dist":70.53949268,
                "rate":1,
                "osm":"node/5661720423",
                "kinds":"historic,monuments_and_memorials,interesting_places,monuments"
            }
        }
    ]
}
"""

3. Decode the mock JSON data into Swift object

// Decoding

let jsonData = jsonString.data(using: .utf8)!
let decoder = JSONDecoder()
let featureCollection = try! decoder.decode(FeatureCollection.self, from: jsonData)

Extension

to use a init(from decoder: Decoder) constructor, look at this:

struct FeatureCollection: Codable {
    let type: String
    let features: [Feature]
    
    init(
        type: String,
        features: [Feature]
    ) {
        self.type: String, = type
        self.features: [Feature] = features
    }
    
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)

        type = try container.decodeIfPresent(String.self, forKey: .type)
        features = try container.decodeIfPresent([Feature].self, forKey: .features)
    }

    enum CodingKeys: String, CodingKey {
        case type
        case features
    }
}
SchmidtFx
  • 498
  • 4
  • 16
  • Hello, Sir. Thank you for the response. Also, would you kindly help me in my case, with the init() method? What the data type for features can be used there? – Artemast Feb 27 '23 at 18:44
  • Hello @Artemast, I didn't get your point. – SchmidtFx Feb 28 '23 at 09:03
  • Hi, @Schmid. I am asking about this method ```init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) let type = try values.decode(String.self, forKey: .type) let ContainsFeatures = values.contains(.features) let features = try values.decode([String: String].self, forKey: .features) //skips the features line }``` Do you know, what data type can be used for decoding in my case? I am trying to use String, [String], or [String:String], but it doesn't work. Thanks. – Artemast Feb 28 '23 at 15:07
  • @Artemast please see the extended answer – SchmidtFx Mar 01 '23 at 17:36