jmsiox
jmsiox

Reputation: 123

Swift Codable / Decodable with Nested json Array

I am new to swift and am trying to figureout passing Nested Json. So far I have tried JSONSerialization without success but after being advised to switch to Codable I have give it a try but I keep getting nill from parsed JSON. Code I have so far:

struct AppData: Codable {
    let paymentMethods: [PaymentMethods]?
}
struct PaymentMethods: Codable {
    let payment_method: String?
}

AF.request(startAppUrl, method: .post, parameters: requestParams , encoding: JSONEncoding.default).responseString{
    response in
    switch response.result {
        case .success(let data):
            let dataStr = data.data(using: .utf8)!
            let parsedResult = try? JSONDecoder().decode( AppData.self, from: dataStr)
            print(parsedResult)

        case .failure(let error):
            print((error.localizedDescription))
    }
}

My JSON data can be found here: https://startv.co.tz/startvott/engine/jsonsample/ . I am using xcode 11

I will appreciate assistance as its already one week stuck on this.

Upvotes: 0

Views: 308

Answers (1)

vadian
vadian

Reputation: 285260

First of all replace responseString with responseData in the request line, this avoids the extra step to convert string (back) to data.

Second of all add always a do - catch block around a JSONDecoder line. Never ignore decoding errors with try?. The block will catch this comprehensive DecodingError:

typeMismatch(Swift.Array, Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "paymentMethods", intValue: nil)], debugDescription: "Expected to decode Array but found a string/data instead.", underlyingError: nil))

The error states that the value for key paymentMethods is not an array. It's a string. Looking at the JSON it's in fact a nested JSON string which must be decoded on a second level.

struct AppData: Codable {
    let paymentMethods: String
}
struct PaymentMethods: Codable {
    let paymentMethod: String
}

AF.request(startAppUrl, method: .post, parameters: requestParams , encoding: JSONEncoding.default).responseData{
    response in
    switch response.result {
        case .success(let data):
            do {
                let parsedResult = try JSONDecoder().decode( AppData.self, from: data)
                let paymentData = Data(parsedResult.paymentMethods.utf8)
                let secondDecoder = JSONDecoder()
                secondDecoder.keyDecodingStrategy = .convertFromSnakeCase
                let paymentMethods = try secondDecoder.decode([PaymentMethods].self, from: paymentData)
                print(paymentMethods)
            } catch {
                print(error)
            }

        case .failure(let error):
            print((error.localizedDescription))
    }
}

Side note:

The URL doesn't require a POST request and parameters. You can omit all parameters except the first.

Upvotes: 2

Related Questions