Search code examples
jsonswiftapistravastrava-api-v3

Strava API returns null as first value of powerstream - Swift


When trying to use the Strava API with wattages per second I keep running into the problem of the first value always being null instead of a number.

struct streamsElement: Decodable {
    struct Stream: Decodable {
      let data: [Double]?
      let series_type: String
      let original_size: Int
      let resolution: String
      
    }

//decoding
if let wattsData = streams.watts?.data {
    for watts in wattsData {
        wattsArr.append("watts: \(watts)" )
    }
}
"watts":{"data":[
null,
144,
98,
95, etc.

This works fine for all the other streams.

Error

Error decoding JSON data: valueNotFound(Swift.Double, Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "watts", intValue: nil), CodingKeys(stringValue: "data", intValue: nil), _JSONKey(stringValue: "Index 0", intValue: 0)], debugDescription: "Expected Double but found null value instead.", underlyingError: nil))

How can i turn this Null value into a zero so I don't get a JSON decoding error?

I tried to skip the first value in the decoding but this did not work.


Solution

  • You don´t have to turn the null JSON value into a 0 just to prevent the crash. Change the the type of the array to an optional and you are good to go. E.g.:

    struct Stream: Decodable {
        let data: [Double?] // add a ? if the array itself is optional too
        let series_type: String
        let original_size: Int
        let resolution: String
    }
    

    I don´t know why and where you do what you described as decoding. But if you want your data Double´s to be nonoptional you could do something like this in a custom decoder:

    struct streamsElement: Decodable {
        struct Stream: Decodable {
            let data: [Double]
            
            enum CodingKeys: CodingKey{
                case data
            }
            
            init(from decoder: Decoder) throws {
                let container = try decoder.container(keyedBy: CodingKeys.self)
                // decode the data property to the array of optionals
                let data = try container.decode([Double?].self, forKey: .data)
                // replace nil values with 0
                self.data = data.map{$0 ?? 0}
            }
        }
        
        var watts: Stream
    }
    

    I´ve implemented only the problematic property. Of course you would need to expand this to the others.