user1898829
user1898829

Reputation: 3527

Decodable not working with non empty array

I'm using this library which has created non-empty collections like arrays, dictionaries and strings. https://github.com/pointfreeco/swift-nonempty

When I decode to the non-empty array I get the following error

Swift.DecodingError.typeMismatch(Swift.Dictionary, Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "poiList", intValue: nil)], debugDescription: "Expected to decode Dictionary but found an array instead.", underlyingError: nil))

This is my structure

struct LocationCarModel: Codable {

    // MARK: - Properties
    var poiList: NonEmptyArray<PointOfInterest>

    // MARK: - PointOfInterest
    struct PointOfInterest: Codable {
        var id: Int
        var coordinate: Position
        var fleetType: String
        let numberPlate = "HCD837EC"
        let model: String = "Tesla S"
        let fuel: Double = 0.9
    }
}

This is the response I'm getting https://fake-poi-api.mytaxi.com/?p2Lat=53.394655&p1Lon=9.757589&p1Lat=53.694865&p2Lon=10.099891

and this how I'm decoding it.

 public extension Decodable {

   static func parse(from item: Any?, strategy: JSONDecoder.KeyDecodingStrategy = .useDefaultKeys) -> Self? {

       guard let data = self.data(from: item) else {
           return nil
       }

       let decoder = JSONDecoder()
       decoder.keyDecodingStrategy = strategy

       do {
           let result = try decoder.decode(Self.self, from: data)
           return result
       } catch {
           debugPrint(error)
           return nil
       }
   }

   private static func data(from item: Any?) -> Data? {
       switch item {
       case let data as Data:
           return data
       case let string as String:
           return string.data(using: .utf8)
       case .some(let item):
           return try? JSONSerialization.data(withJSONObject: item, options: [])
       case nil:
           return nil
       }
   }
}

and the line to decode using the above function is

let model = LocationCarModel.parse(from: data)

Changing poiList to the standard swift array then no error occurs.

Any ideas how I can solve this issue? Any help would be appreciated. Thank you in advance.

Upvotes: 1

Views: 286

Answers (2)

Joakim Danielson
Joakim Danielson

Reputation: 51973

You need to have your own init(from:) for the top struct since JSONDecoder doesn't understand NonEmpty and how to initialise it. Apart from the init I also added coding keys and a new error

enum DecodeError: Error {
    case arrayIsEmptyError
}

struct LocationCarModel: Codable {
    var poiList: NonEmpty<[PointOfInterest]>

    enum CodingKeys: String, CodingKey {
        case poiList
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        let array = try container.decode([PointOfInterest].self, forKey: .poiList)
        guard let first = array.first else {
            throw DecodeError.arrayIsEmptyError
        }
        poiList = NonEmptyArray(first, array)
    }
    //...
}

Upvotes: 1

Shehata Gamal
Shehata Gamal

Reputation: 100523

You can try

struct Root: Codable {
    let poiList: [PoiList]
}

// MARK: - PoiList
struct PoiList: Codable {
    let id: Int
    let coordinate: Coordinate
    let fleetType: String
    let heading: Double
}

// MARK: - Coordinate
struct Coordinate: Codable {
    let latitude, longitude: Double
}

let res = try? JSONDecoder().decode(Root.self,from:data)
print(res)

Upvotes: 0

Related Questions