Search code examples
swiftrealmalamofiredecodable

Type Mismatch with Decodable and Object


I've got a problem with parsing data from the server. I've got JSON which has an array of objects, something like this:

{
    "items": [
        {
            "itemType": {
                "id": 12,
                "tagId": "FCHA78558D"
            },
            "parts": [
                {
                    "partId": 52,
                    "manufacturer": "xxx"
                },
                {
                    "partId": 53,
                    "manufacturer": "xxx"
                },
                {
                    "partId": 54,
                    "manufacturer": "xxx"
                }
            ],
            "description": "yyy"
        },
        {
            "itemType": {
                "id": 13,
                "tagId": "FCHA755158D"
            },
            "parts": [
                {
                    "partId": 64,
                    "manufacturer": "xxx"
                },
                {
                    "partId": 65,
                    "manufacturer": "xxx"
                }
            ],
            "description": "zzz"
        }
    ]
}

I only want to obtain this one array of objects so I implemented this class like this:

class User : Object, Decodable {
   var items = List<Equipment>()
}

in the Alamofire I'm downloading the JSON, parsing it to data and then in do-catch block I receive an error:

let items = try JSONDecoder().decode(User.self, from: receivedValue)

error:

▿ DecodingError
  ▿ typeMismatch : 2 elements
    - .0 : Swift.Array<Any>
    ▿ .1 : Context
      ▿ codingPath : 2 elements
        - 0 : CodingKeys(stringValue: "items", intValue: nil)
        ▿ 1 : _JSONKey(stringValue: "Index 0", intValue: 0)
          - stringValue : "Index 0"
          ▿ intValue : Optional<Int>
            - some : 0
      - debugDescription : "Expected to decode Array<Any> but found a dictionary instead."
      - underlyingError : nil

That's weird because it is an array of objects for sure. I tried setting my items property to String to see the result and then I got: - debugDescription : "Expected to decode String but found an array instead." I had this error couple of times but I always managed to find the solution.


Solution

  • I suppose you were using the List conditional conformance to Decodable from my answer to your previous question. I don't fully understand why it doesn't work in this specific case, but I'll investigate.

    Until then, you can make decoding work by manually implementing the init(from decoder:Decoder) function.

    class User : Object, Decodable {
        let items = List<Equipment>()
    
        private enum CodingKeys: String, CodingKey {
            case items
        }
        required convenience init(from decoder:Decoder) throws {
            self.init()
            let container = try decoder.container(keyedBy: CodingKeys.self)
            let itemsArray = try container.decode([Equipment].self, forKey: .items)
            self.items.append(objectsIn: itemsArray)
        }
    }