AaoIi
AaoIi

Reputation: 8396

Parse Codable classes and avoid repetition

I have a JSON response as the following:

{
  "http_status": 200,
  "success": true,
  "has_error": false,
  "error": [
    ""
  ],
  "response": {
    "token": "",
    "verified": false,
    "message": ""
  }
}

As far as i can say for an app API usage http_status, success, has_error, error are shared between all APIS and thus i will create a Codable class to handle it, but the response could be different model, so here is what I'm trying to do.

I have created a general response class as the below, so this class i can use for all apis in the project to avoid duplication of same class but different names:

class GeneralResponse:Codable {

    let http_status: Int?
    let success, has_error: Bool?
    let error: [String]?

    enum CodingKeys: String, CodingKey {
        case http_status = "http_status"
        case success = "success"
        case has_error = "has_error"
        case error = "error"
    }

    init(http_status: Int?, success: Bool?, has_error: Bool?,error: [String]?) {
        self.http_status = http_status
        self.success = success
        self.has_error = has_error
        self.error = error
    }

}

Now i have created the response class which will handle for now the registration response:

class RegistrationResponseDetails: Codable {
    let token: String?
    let verified: Bool?
    let message: String?

    init(token: String?, verified: Bool?, message: String?) {
        self.token = token
        self.verified = verified
        self.message = message
    }
}

And lets say i need to parse the registration the response so here is what i did, i have created a class and used both of them:

class RegistrationResponse: Codable {

    let generalResponse:GeneralResponse?
    let response: RegistrationResponseDetails?

    init(generalResponse: GeneralResponse?, response: RegistrationResponseDetails?) {
        self.generalResponse = generalResponse
        self.response = response
    }
}

So i will mainly use RegistrationResponse to parse the response which will parse "generalResponse" which includes http_status, success, has_error, error, and then response will parse the desired response object.

But at some point generalResponse object is always nil and response has the data parsed correctly, what should i do to make generalResponse get parsed without duplication in each api, because in each api i will have generalResponse object so is it possible to solve it ?

Note: I'm using Alamofire as the networking library.

Upvotes: 2

Views: 90

Answers (3)

Shehata Gamal
Shehata Gamal

Reputation: 100503

For a simple case

class Main:Decodable { 
    let name:String?   // insert all shared vars 
} 

class Sub:Main { 
   let id:String? 
} 

This will parse

{
     "name" : "rr" , 
     "id" : "oo"  
}

Upvotes: 1

vadian
vadian

Reputation: 285069

First of all if

http_status, success, has_error, error are shared between all APIS

why are the class properties optional?


If the mentioned keys are the same but the value for key response is different use generics.

In most cases structs are sufficient.

struct JSONParser<T : Decodable> {

    struct Response<U : Decodable> : Decodable {
        let httpStatus: Int
        let success, hasError: Bool
        let error: [String]
        let response : U
    }

    let responseData : Response<T>

    init(data: Data) throws {
        let decoder = JSONDecoder()
        decoder.keyDecodingStrategy = .convertFromSnakeCase
        responseData = try decoder.decode(Response.self, from: data)
    }
}

Then create the different structs e.g.

struct RegistrationResponseDetails : Decodable {
    let token: String
    let verified: Bool
    let message: String
}

and parse it

let parser = try JSONParser<RegistrationResponseDetails>(data: data)
let registrationResponseDetails = parser.responseData.response

Upvotes: 1

dan
dan

Reputation: 9825

You can make your GeneralResponse generic and tell it what type to use when parsing the response:

class GeneralResponse<T: Codable>: Codable {
    let http_status: Int?
    let success, has_error: Bool?
    let error: [String]?
    let response: T?
}

class RegistrationResponseDetails: Codable {
    let token: String?
    let verified: Bool?
    let message: String?
}

Then you can give it the inner response class when you parse the json:

let generalResponse = try JSONDecoder().decode(GeneralResponse<RegistrationResponseDetails>.self, from: jsonData)
// generalResponse.response is of type RegistrationResponseDetails?

Upvotes: 1

Related Questions