Search code examples
iosjsonswiftdecodable

Swift Codable Json Response


I Have the following JSON response from API,

{
    "status_code": 1000,
    "data": [
        {
            "id": 3,
            "invoice_id": 100000,
            "user_id": 1000,
            "contact_number": "NA",
            "province": 0,
            "location": "100000",
            "total": 0,
            "confirm_code": 1234,
            "status": 0,
            "created_at": "2020-03-18 22:07:25",
            "updated_at": "2020-03-18 22:07:25"
        },
        {
            "id": 4,
            "invoice_id": 100000,
            "user_id": 1000,
            "contact_number": "NA",
            "province": 0,
            "location": "100000",
            "total": 0,
            "confirm_code": 1234,
            "status": 0,
            "created_at": "2020-03-18 22:10:40",
            "updated_at": "2020-03-18 22:10:40"
        },
        {
            "id": 5,
            "invoice_id": 100000,
            "user_id": 1000,
            "contact_number": "NA",
            "province": 0,
            "location": "100000",
            "total": 0,
            "confirm_code": 1234,
            "status": 0,
            "created_at": "2020-03-18 22:12:29",
            "updated_at": "2020-03-18 22:12:29"
        }
    ],
    "message": null
}

and my struct is,

struct Invoice: Codable {
    let statusCode: Int
    let data: [Datum]
    let message: String?

    enum CodingKeys: String, CodingKey {
        case statusCode
        case data, message
    }
}

struct Datum: Codable {
    let id, invoiceID, userID: Int
    let contactNumber: String
    let province: Int
    let location: String
    let total, confirmCode, status: Int
    let createdAt, updatedAt: String

    enum CodingKeys: String, CodingKey {
        case id
        case invoiceID
        case userID
        case contactNumber
        case province, location, total
        case confirmCode
        case status
        case createdAt
        case updatedAt
    }
}

and In View Controller,

override func viewDidLoad() {
        super.viewDidLoad()

        let url = URL(string: "http://xxxxx/api/invoices/\(1000)")!
        var request = URLRequest(url: url)
        request.httpMethod = "get"
        let task = session.dataTask(with: request) { (data, response, error) in
            guard let data = data else { return }
            do {
                let jsonData = try JSONDecoder().decode([Invoice].self, from: data)
                print(jsonData)
            }
            catch let jsonerr {
                print("error serrializing error",jsonerr)
            }

        }
        task.resume()
    }

But Im keeping below error message,

error serrializing error typeMismatch(Swift.Array, Swift.DecodingError.Context(codingPath: [], debugDescription: "Expected to decode Array but found a dictionary instead.", underlyingError: nil))

Please what Im missing here ! Any help will be much appreciated


Solution

  • The Codable struct will be like

    struct Invoice: Codable {
        let statusCode: Int
        let data: [Datum]
        let message: String?
    
        enum CodingKeys: String, CodingKey {
            case statusCode = "status_code"
            case data, message
        }
    }
    
    struct Datum: Codable {
        let id, invoiceID, userID: Int
        let contactNumber: String
        let province: Int
        let location: String
        let total, confirmCode, status: Int
        let createdAt, updatedAt: String
    
        enum CodingKeys: String, CodingKey {
            case id = "id"
            case invoiceID = "invoice_id"
            case userID = "user_id"
            case contactNumber = "contact_number"
            case province, location, total
            case confirmCode = "confirm_code"
            case status
            case createdAt = "created_at"
            case updatedAt = "updated_at"
        }
    }
    

    And also use

    let jsonData = try JSONDecoder().decode(Invoice.self, from: data)
    

    instead of

     let jsonData = try JSONDecoder().decode([Invoice].self, from: data)