4

I am using following decoding struct to decode the data from server but it always returns "No value associated with key CodingKeys". Please see the code below

struct UserDecode: Codable {

var user: User?
var result: String?

private enum CodingKeys: String, CodingKey {
    case user = "Records"
    case result = "Result"
}

init(from decoder: Decoder) throws {
    do {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        result = try values.decode(String.self, forKey: .result)
        user = try values.decode(User.self, forKey: .user)
    } catch {
        print(error.localizedDescription)
    }
}
}

struct User: Codable {

var mobile: Int?
var userId: Int?
var name: String?

private enum CodingKeys: String, CodingKey {
    case userId = "MEMBERID"
    case name = "Membername"
    case mobile = "Mobile"
}

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

        userId = try values.decode(Int.self, forKey: .userId)
        name = try values.decode(String.self, forKey: .name)
        mobile = try values.decode(Int.self, forKey: .mobile)
    }
    catch {
        print(error.localizedDescription)
    }
}
}

and the response from server is

{
"Result": "OK",
"Records": {
"MEMBERID": 1,
"Membername": "Balaji",
"Mobile": 12345678901,
}
}

The error i am getting is DecodingError

  ▿ keyNotFound : 2 elements
    - .0 : UserCodingKeys(stringValue: "Result", intValue: nil)
    ▿ .1 : Context
      - codingPath : 0 elements
      - debugDescription : "No value associated with key UserCodingKeys(stringValue: \"Result\", intValue: nil) (\"Result\")."
      - underlyingError : nil

This is the full data from server.

{
  "Result": "OK",
  "Records": {
    "MEMBERID": 1,
    "Membername": "Balaji",
    "Mobile": 12345678901,
    "PAYLEVEL": 0,
    "UserName": "12345678901",
    "Token": "SroRn65YfqLSGcMMHaFsl2c5RGEiv1JcHHPnpFXa7quKOPIRsqUEhpcWpGKl_23O4PJcgmLiFb9T8TAq1fgyftgpffJKCbJUozYjKF68dvChrb8Qv1egw_paxnUZlYBzwlfXUtFQ23Y1Wu6UBZHdycY4PwS9a1f_e1zG_etiV9R-E8kOLMoqwQTXTtRZ3NPEXsHDtS3KRz471c9Bzbx_v3FGw4HDcvGgejWaC1Zo6nHE8IQ7MG2oX5KuneZTqd1X",
    "Imageurl": "https://.net/images/abc.png",
    "WalletBalance": 0,
    "ResponseCode": 1,
    "ResponseMessage": "LOGIN SUCCESS",
    "ImageType": "1.jpeg",
    "Emailid": "ab.net",
    "FirstOrder": 1,
    "FirstOrderImage": "https:.net/images/abc.png"
  }
}

Decoder code:

let task = session.dataTask(with: request) {(data, response, error) in

            let (success, errorMessage) = self.isValidResponse(error: error, data: data, response: response as? HTTPURLResponse)

            if !success {
                completionHandler(nil, errorMessage)
                return
            }

            // Parse the data
            do {
                let decoder = JSONDecoder()
                if let userResult = try decoder.decode(UserDecode.self, from: data!) as? UserDecode {

                    completionHandler(userResult.user, nil)
                    return
                }
                else {
                    completionHandler(nil, ErrorMessage.unableToProcess)
                    return
                }
            } catch {
                print("Could not parse JSON data")
                completionHandler(nil, ErrorMessage.unableToProcess)
                return
            }
        }
rob mayoff
  • 375,296
  • 67
  • 796
  • 848
Balaji Kondalrayal
  • 1,743
  • 3
  • 19
  • 38
  • can you remove init from both structs and try ??? , seems that you got an error instead of a valid data ?? – Shehata Gamal Jul 23 '18 at 17:59
  • The structs and the code are supposed to work. You can even delete both `init` methods. – vadian Jul 23 '18 at 17:59
  • @vadian - I tried that.. In that case it is not throwing error, Instead it give nil for user – Balaji Kondalrayal Jul 23 '18 at 18:03
  • I tested the given JSON and code quickly in a Playground. It does work (with and without `init` methods). Are you sure this is the complete JSON? – vadian Jul 23 '18 at 18:05
  • @vadian I have updated the question please check – Balaji Kondalrayal Jul 23 '18 at 18:14
  • It works as well. – vadian Jul 23 '18 at 18:19
  • Will it be problem with xcode version? I am using xcode 9.3 – Balaji Kondalrayal Jul 23 '18 at 18:21
  • It should't make any difference. Please add also the `JSONDecoder` related code – vadian Jul 23 '18 at 18:25
  • @vadian Can you pls check the decoder code. I have updated it in question – Balaji Kondalrayal Jul 23 '18 at 18:30
  • Looks correct. But please don't (redundant) conditional downcast `if - let` with `try`. Either the decoder returns a non-optional `UserDecode` or throws an error. The code will never reach `else`. – vadian Jul 23 '18 at 18:34
  • @vadian It is returning `UserDecode` with `result` and `user` as nil. this line `let values = try decoder.container(keyedBy: CodingKeys.self)` `values.allKeys` returns 0 elements. I think there is some problem in IDE itself – Balaji Kondalrayal Jul 23 '18 at 18:38
  • I tried your code in a minimal sample (https://gist.github.com/drbarto/aeeb6e492e0b9b787d37e1db6fe931d9) and it worked fine. So the problem is somewhere in the code around the parsing. BTW: since you use Swift 4's JSON support via `Codable` there is no need to provide custom `init(from:)` implementations, that's handled by Swift :) – dr_barto Jul 23 '18 at 19:17

1 Answers1

5

Having

 let values = try decoder.container(keyedBy: CodingKeys.self)
 result = try values.decode(String.self, forKey: .result)

decode makes the decoding fails if data is nil consider using decodeIfPresent or the optional ? suffices

Shehata Gamal
  • 98,760
  • 8
  • 65
  • 87