Expected to decode Int but found a number instead

12,174

Solution 1

The error message is very misleading. This happens when the JSON contains a boolean value, and the struct has an Int property for the corresponding key.

Most likely your JSON actually looks like this:

{
    "code": 406,
    "message": "Email Address already Exist.",
    "status": false
}

and accordingly, your struct should be

struct Registration: Codable {
    let code: Int
    let status: Bool
}

if let registration = try? JSONDecoder().decode(Registration.self, from: data) {
    print(registration.code) // 406
    print(registration.status) // false
}

Solution 2

For these kinds of issues, check and make sure that the response and the type of response given in the struct are the same. Here it seems your response is not correct. The status may true or false

{
    code: 406,
    message: "Email Address already Exist.",
    status: 0
}

If the status is true, change your struct as,

struct Registration: Codable {
    var code: Int
    var status: Bool
    var message: String
}

if the status is 0, change the var status: Bool to var status: Int, in the above struct.

Solution 3

I think we can use this for resolving such issues:

protocol DecodingHelper {

    associatedtype Keys: CodingKey

    static func getStringValue(_ container: KeyedDecodingContainer<Keys>, key: Keys) -> String

    static func getIntValue(_ container: KeyedDecodingContainer<Keys>, key: Keys) -> Int

    static func getBoolValue(_ container: KeyedDecodingContainer<Keys>, key: Keys) -> Bool
}

extension DecodingHelper {

    static func getStringValue(_ container: KeyedDecodingContainer<Keys>, key: Keys) -> String {
        var str: String = ""

        if let obj = try? container.decode(String.self, forKey: key) {
            str = obj
        }
        else if let obj = try? container.decode(Int.self, forKey: key){
            str = String(obj)
        }
        return str
    }

    static func getIntValue(_ container: KeyedDecodingContainer<Keys>, key: Keys) -> Int {
        var val: Int = 0

        if let obj = try? container.decode(String.self, forKey: key),
            let intVal = Int(obj){
            val = intVal
        }
        else if let obj = try? container.decode(Int.self, forKey: key){
            val = obj
        }
        return val
    }

    static func getBoolValue(_ container: KeyedDecodingContainer<Keys>, key: Keys) -> Bool {
        var val: Bool = false

        if let obj = try? container.decode(String.self, forKey: key),
            let intVal = Int(obj){

            (intVal != 0) ? (val = true) : (val = false)
        }
        else if let obj = try? container.decode(Int.self, forKey: key){
            (obj != 0) ? (val = true) : (val = false)
        }
        else if let obj = try? container.decode(Bool.self, forKey: key){
            val = obj
        }
        return val
    }
}



struct VideoFeedback: Codable {

    // MARK:- Variables -

    var isFeedbackProvided:Bool = true

    // MARK:- Initialisation -

    private enum CodingKeys: String, DecodingHelper, CodingKey {

        typealias Keys = CodingKeys

        case isFeedbackProvided = "lastVideoFeedback"
    }

    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)

        isFeedbackProvided = CodingKeys.getBoolValue(values, key: .isFeedbackProvided)
    }
}

Let me know if you have any suggestion to improve this.

Solution 4

You don't need to implement your own decoding initializer if your struct's properties are already Decodable. Neither do you need custom CodingKeys as mentioned by @Gereon.

For the following JSON data:

let data = """
    {
        "code": 406,
        "message": "Email Address already Exist.",
        "status": 0
    }
    """.data(using: .utf8)!

This works fine:

struct Registration: Codable {
    var code: Int
    var status: Int
}

if let registration = try? JSONDecoder().decode(Registration.self, from: data) {
    print(registration.code) // 406
    print(registration.status) // 0
}

See Encoding and Decoding Custom Types from Apple for more information.

Share:
12,174
Jatin Chauhan
Author by

Jatin Chauhan

My skill is to develop native iOS app with using swift or objective c.

Updated on June 15, 2022

Comments

  • Jatin Chauhan
    Jatin Chauhan about 2 years

    I had issue with JSON parsing in Swift 4.2. Here is the following code which shown runtime error.

    My Json data is as follow which i got from server.

    {
        code: 406,
        message: "Email Address already Exist.",
        status: 0
    }
    

    I am using Codable to create my structure as follow

    struct Registration: Codable {
        var code: Int
        var status: Int
        private enum CodinggKeys: String, CodingKey {
            case code
            case status
        }
        public init(from decoder: Decoder) throws {
            let container = try decoder.container(keyedBy: CodingKeys.self)
            do {
                self.code = Int(try container.decode(String.self, forKey: .code))!
            } catch DecodingError.typeMismatch {
                let value = try container.decode(Double.self, forKey: .code)
                self.code = Int(value);
            }
    
            do {
                self.status = try container.decode(Int.self, forKey: .status)
            } catch DecodingError.typeMismatch {
                let value = try container.decode(String.self, forKey: .status)
                self.status = Int(value);
            }
        }
    } 
    

    But every time i got error on parsing status key.

    Note: I had tried to parse status in String, Int, Double, Decimal, NSInterger but neither any works. every time i got the same error. Expected to decode UInt but found a number instead.