0

I am using NSUrlConnection and Codable library of Apple.

I am using a web API to signup new users for my application. The API returns a status and a message (in json which i map to a model).

This is my model class:

Struct SignUpResult {
  let message: String
  let status: String
} 

Struct SignUpParams {
 let name: String
 let email: String
 let mobile_no: String
 let password: String
}

If the user gave all the parameters correctly then the message is returned as a string. Like this:

{
    "status": "OK",
    "message": "User signup successfully"
}

On the other hand, if the user entered the parameters incorrectly then the message is returned as an array. Like this:

{
    "status": "INVALID_PARAMS",
    "message": [
        "The name may only contain letters."
    ]
}

If the parameters are incorrect then i get the error

"expected to decode a string but found an array instead". 

This is the code i get the error on:

let result = JSONDecoder().decode(SignUpResult.self, from: data)

What should i do?

iNotReal
  • 3
  • 1
  • I'd suggest that in your struct you use an `Array` for `message` in case of there are various messages? And you need to use a custom init cf. https://stackoverflow.com/questions/47935705/using-codable-with-key-that-is-sometimes-an-int-and-other-times-a-string?rq=1 – Larme Aug 06 '18 at 16:26
  • Try using Any type instead of String – shivi_shub Aug 06 '18 at 16:35
  • @shivi_shub `Decodable` doesn't support `Any` – vadian Aug 06 '18 at 16:36
  • Thanks Larme. Yours and vadian answers were exactly what i was looking for – iNotReal Aug 06 '18 at 17:09

1 Answers1

1

My suggestion is to decode status as enum and conditionally decode message as String or [String]. messages is declared as array.

enum SignUpStatus : String, Decodable {
    case success = "OK", failure = "INVALID_PARAMS"
}

struct SignUpResult : Decodable {
    let messages : [String]
    let status: SignUpStatus

    private enum CodingKeys : String, CodingKey { case status, message }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        status = try container.decode(SignUpStatus.self, forKey: .status)
        switch status {
        case .success: messages = [try container.decode(String.self, forKey: .message)]
        case .failure: messages = try container.decode([String].self, forKey: .message)
        }
    }
}
vadian
  • 274,689
  • 30
  • 353
  • 361
  • Thanks. This worked perfectly. I would have up voted your answer but i don't have enough reputation yet. – iNotReal Aug 06 '18 at 17:08