1

I make a request to the OpenSky Network API and get a result like this:

{
  "time":1629739170,
  "states":[
    ["4b1800","SWR1076 ","Switzerland",1629739169,1629739169,8.5493,47.4581,373.38,false,79.14,275.97,7.15,null,487.68,null,false,0],
    ["3c65c4","DLH35A  ","Germany",1629739170,1629739170,6.5185,46.2346,11590.02,false,255.57,48.84,0,null,11986.26,"1000",false,0]
  ]
}

Now I want to decode that JSON string to a list of FlightState objects, but there are no keys provided. From the API documentation (see link above) I know what value corresponds to what property, but how do I create corresponding Swift objects?

The following doesn't work, obviously, because there are no keys.

let decoder = JSONDecoder()             
let flightState: FlightState = try! decoder.decode(FlightState.self, from: dataString.data(using: .utf8)!)


struct FlightState: Codable {
    
    let time: Int
    let states: [StateVector]

    struct StateVector: Codable {
        let icao24: UUID
        let callsign: String
        let origin_country: String
        let time_position: Int
        let last_contact: Int
        let longitude: Float
        let latitude: Float
        let baro_altitude: Float
        let on_ground: Bool
        let velocity: Float
        let true_track: Float
        let vertical_rate: Float
        let sensors: [Int]
        let geo_altitude: Float
        let squawk: String
        let spi: Bool
        let position_source: Int
    }

}

The error message says Expected to decode Dictionary<String, Any> but found an array instead. So how do I tell Swift to interpret the values in the states lists as properties of objects with their corresponding type?

I have the feeling that someone must have been stumbled upon this before, but I couldn't find a solution. Maybe I am too new to Swift to recognize a solution as such.

2
  • "let icao24: UUID" UUID? Where does it come from? Commented Aug 24, 2021 at 11:08
  • @ElTomato I know that it is described as string in the API documentation, but I thought a uuid is required to an object in Swift!? For this property corresponds to the "Unique ICAO 24-bit address of the transponder" I thought I could simply use UUID here. Commented Aug 24, 2021 at 11:21

1 Answer 1

3

You can decode a JSON where specific properties are held in specific array indices rather than under specific Dictionary keys by implementing init(from decoder:) on your own and using an unkeyedContainer() to decode the array, then calling decode to decode each property in order.

Bear in mind lots of properties in the response can be null, so you need to declare those as Optional and use decodeIfPresent rather than decode.

struct FlightState: Decodable {

    let time: Int
    let states: [StateVector]

    struct StateVector: Decodable {
        let icao24: String
        let callSign: String
        let originCountry: String
        let timePosition: Int?
        let lastContact: Int
        let longitude: Float?
        let latitude: Float?
        let baroAltitude: Float?
        let onGround: Bool
        let velocity: Float?
        let trueTrack: Float?
        let verticalRate: Float?
        let sensors: [Int]?
        let geoAltitude: Float?
        let squawk: String?
        let spi: Bool
        let positionSource: Int

        init(from decoder: Decoder) throws {
            var values = try decoder.unkeyedContainer()
            self.icao24 = try values.decode(String.self)
            self.callSign = try values.decode(String.self)
            self.originCountry = try values.decode(String.self)
            self.timePosition = try values.decodeIfPresent(Int.self)
            self.lastContact = try values.decode(Int.self)
            self.longitude = try values.decodeIfPresent(Float.self)
            self.latitude = try values.decodeIfPresent(Float.self)
            self.baroAltitude = try values.decodeIfPresent(Float.self)
            self.onGround = try values.decode(Bool.self)
            self.velocity = try values.decodeIfPresent(Float.self)
            self.trueTrack = try values.decodeIfPresent(Float.self)
            self.verticalRate = try values.decodeIfPresent(Float.self)
            self.sensors = try values.decodeIfPresent([Int].self)
            self.geoAltitude = try values.decodeIfPresent(Float.self)
            self.squawk = try values.decodeIfPresent(String.self)
            self.spi = try values.decode(Bool.self)
            self.positionSource = try values.decode(Int.self)
        }
    }
}
Sign up to request clarification or add additional context in comments.

1 Comment

That's what I was looking for, thank you!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.