Search code examples
swiftmapkitgeojson

I get a response that contains GeoJSON nested. How do I parse that in Swift?


I am querying a WebService and I get the following response:

{
    "pageSize": 50,
    "pageNum": 1,
    "totalCount": 1146,
    "totalPages": 23,
    "items": [
        {
            "type": "Feature",
            "properties": {...},
            "geometry": {
                "type": "GeometryCollection",
                "geometries": [
                    {
                        "type": "Point",
                        "coordinates": [
                            0.15,
                            35.22
                        ]
                    }
                ]
            }
        }
    ]
}

The 'items' are GeoJSON objects.

In Swift, I can parse GeoJSON objects with: MKGeoJSONDecoder().decode(data), but how can I parse the whole response (that starts with pageSize, etc and then contains an array of GeoJSON items in the items member)?

If I define the root object like so

struct WebServiceResponse: Codable {
    let pageSize: Int
    let pageNum: Int
    let totalCount: Int
    let totalPages: Int
    let items: [MKGeoJSONObject]
}

I get Type 'WebServiceResponse' does not conform to protocol 'Decodable', which seems logical cos MKGeoJSONObject is defined as

public protocol MKGeoJSONObject : NSObjectProtocol {
}

So no mention of it being Decodable.

I could manually remove the first bit (and the curly bracket at the end of the response) so that I only have this bit left:

[
        {
            "type": "Feature",
            "properties": {...},
            "geometry": {
                "type": "GeometryCollection",
                "geometries": [
                    {
                        "type": "Point",
                        "coordinates": [
                            0.15,
                            35.22
                        ]
                    }
                ]
            }
        }
    ]

and parse that with the MKGeoJSONDecoder ... but that sounds like a hack to me.


Solution

  • MKGeoJSONDecoder is, as you have experienced, does not work well with Codable, in the same way that JSONSerialization doesn't work well with Codable.

    You basically need to write your own Codable types that represent the GeoJSON models. There are people who have already done this. You can use a package like CodableGeoJSON.

    struct WebServiceResponse: Codable {
        let pageSize: Int
        let pageNum: Int
        let totalCount: Int
        let totalPages: Int
        let items: [GeoJSON]
    }
    

    GeoJSON is an enum with associated values, each case representing each kind of GeoJSON data.

    If you know what specific type of GeoJSON you will get, you can use the more specialised types,

    // if you know it will be an array of features
    let items: [GeoJSON.Feature]
    
    // if you know it will be an array of features represented by points:
    let items: [GeoJSONFeature<PointGeometry, LocationProperties>]
    // 'LocationProperties' above represents the data in the 'properties' key
    
    // and so on...