1

I have a base model -

struct BaseModel<T:Decodable>: Decodable {
    let jsonData: [T]?
    let status: Bool?
    let message: String?
    
    enum CodingKeys: String, CodingKey {
        case jsonData = "data"
        case status = "success"
        case message
    }
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)

        do {
            if let string = try container.decodeIfPresent(T.self, forKey: .jsonData) {
                print(string)
                jsonData = [string]
            } else {
                jsonData = nil
            }
        } catch DecodingError.typeMismatch {
            jsonData = try container.decodeIfPresent([T].self, forKey: .jsonData)
        }
        status = try container.decodeIfPresent(Bool.self, forKey: .status)
        message = try container.decodeIfPresent(String.self, forKey: .message)
    }
}

I am getting response in two types under jsonData

  1. Object
  2. Array

Getting error while decoding if I receive response as Object. And if I choose let jsonData: T?, Then getting issue in decoding of Array response.

I am using this model in my Network Model. That looks like -

    func performOperation<T:Decodable>(urlEndPoint: String, method: HTTPMethod, param: Parameters?, isJsonAvailable: Bool, completion: @escaping(_ response: T?, [T]?, String?, Bool?) ->Void) {
        AF.request(urlEndPoint, method: method, parameters: param, headers: header).validate(statusCode: 200..<500).responseDecodable(of: BaseModel<T>.self, decoder: decoder) { (response) in
}

Json response in case of Object -

  {
    "success": true,
    "data": {
        "heading": "Same text 1",
        "title": "Sample Text 2",
        "content": "Sample text 3"
    },
    "message": "Api response received"
}

Json response in case of ArrayList -

{
    "success": true,
    "data": [
        {
            "id": 1,
            "name": "Home"
        },
        {
            "id": 2,
            "name": "Profile"
        }
    ],
    "message": "Menu List"
}
3
  • 1
    Show your Object and your Array types. Also show your JSON response. Commented Aug 28, 2020 at 12:53
  • Also please post the error that you are getting from JSONDecode. Commented Aug 28, 2020 at 13:02
  • @gcharita, Seems Like working with my own above code 😃 Thanks for you constant support. Much appreciated, Will let you know if I face any issue. Commented Aug 28, 2020 at 13:33

1 Answer 1

1

You don't need a generic structure. Just create a optional property to assign your object in case there is no user array:


struct BaseModel {
    let data: [User]
    let post: Post?
    let success: Bool
    let message: String
}

struct User: Codable {
    let id: Int
    let name: String
}

struct Post: Codable {
    let heading: String
    let title: String
    let content: String
}

extension BaseModel: Codable  {
    init(from decoder: Decoder) throws {
            let container = try decoder.container(keyedBy: CodingKeys.self)
            do {
                data = try container.decode([User].self, forKey: .data)
                post = nil
            } catch DecodingError.typeMismatch {
                data = []
                post = try container.decode(Post.self, forKey: .data)
            }
            success = try container.decode(Bool.self, forKey: .success)
            message = try container.decode(String.self, forKey: .message)
        }
}

If there is other responses not shown in your post you can do the same approach above using a generic structure as well:

struct BaseModel<T: Codable> {
    let array: [T]
    let element: T?
    let success: Bool
    let message: String
}

extension BaseModel: Codable  {
    init(from decoder: Decoder) throws {
            let container = try decoder.container(keyedBy: CodingKeys.self)
            do {
                array = try container.decode([T].self, forKey: .array)
                element = nil
            } catch DecodingError.typeMismatch {
                array = []
                element = try container.decode(T.self, forKey: .array)
            }
            success = try container.decode(Bool.self, forKey: .success)
            message = try container.decode(String.self, forKey: .message)
        }
}
Sign up to request clarification or add additional context in comments.

1 Comment

In my current code its working as expected, Now I don't need to convert object type in array. Thanks. @Leo Dabus

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.