I am passing API response with Moya and getting this value. I am able to get the object but I extented a base response to handle extra parameters but the extended value does not seem to work. The data expected could be an array of objects and it could just be a regular object. After passing this values, It stopped working and data is not got but every other parameter like status
, message
are passed except data
. Here is my Base response and how I used it
class MaxResponseBase: Codable {
var status: String?
var message: String?
var pagination: Pagination?
var isSucessful: Bool {
return status == "success"
}
struct ErrorMessage {
static let passwordInvalid = " Current password is invalid."
static let loginErrorIncorrectInfo = " Incorrect username/password."
static let loginErrorAccountNotExist = " Invalid request"
}
}
class MaxResponse<T: Codable>: MaxResponseBase {
var data: T?
}
class MaxArrayResponse<T: Codable>: MaxResponseBase {
var data = [T]()
}
Here is my API call for signin for example
func signin(email: String, password: String) -> Observable<MaxResponse<AuthResponse>> {
return provider.rx.request(.signin(username: email, password: password))
.filterSuccess()
.mapObject(MaxResponse<AuthResponse>.self)
.asObservable()
}
how can I tweak this to get data object also
JSON
{
"status" : "success",
"data" : {
"is_locked" : false,
"__v" : 0,
"created_at" : "2019-04-15T11:57:12.551Z"
}
}
It could also be an array of data
(Note: all the code below can be put in a Playground to show that it works.)
In order to solve this, you have to manually write all your initializers. I posted the code that does most of it below but I strongly recommend you use structs instead of classes. It is better in every way if you use structs and containment instead of classes and inheritance.
struct Pagination: Codable { }
struct AuthResponse: Codable {
let isLocked: Bool
let __v: Int
let createdAt: Date
}
class MaxResponseBase: Codable {
let status: String?
let message: String?
let pagination: Pagination?
var isSucessful: Bool {
return status == "success"
}
struct ErrorMessage {
static let passwordInvalid = " Current password is invalid."
static let loginErrorIncorrectInfo = " Incorrect username/password."
static let loginErrorAccountNotExist = " Invalid request"
}
enum CodingKeys: String, CodingKey {
case status, message, pagination
}
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
status = try container.decode(String?.self, forKey: .status)
message = try? container.decode(String?.self, forKey: .message) ?? nil
pagination = try? container.decode(Pagination?.self, forKey: .pagination) ?? nil
}
}
class MaxResponse<T: Codable>: MaxResponseBase {
let data: T?
enum DataCodingKeys: String, CodingKey {
case data
}
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: DataCodingKeys.self)
data = try container.decode(T?.self, forKey: .data)
try super.init(from: decoder)
}
}
let json = """
{
"status" : "success",
"data" : {
"is_locked" : false,
"__v" : 0,
"created_at" : "2019-04-15T11:57:12.551Z"
}
}
"""
let data = json.data(using: .utf8)!
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "YYYY-MM-dd'T'HH:mm:ss.SSZ"
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
decoder.dateDecodingStrategy = .formatted(dateFormatter)
let response = try decoder.decode(MaxResponse<AuthResponse>.self, from: data)
print(response)
It is far simpler and less code to just use a struct:
struct AuthResponse: Codable {
struct ResponseData: Codable {
let isLocked: Bool
let __v: Int
let createdAt: Date
}
let status: String?
let data: ResponseData
}
let json = """
{
"status" : "success",
"data" : {
"is_locked" : false,
"__v" : 0,
"created_at" : "2019-04-15T11:57:12.551Z"
}
}
"""
let data = json.data(using: .utf8)!
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "YYYY-MM-dd'T'HH:mm:ss.SSZ"
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
decoder.dateDecodingStrategy = .formatted(dateFormatter)
let response = try decoder.decode(AuthResponse.self, from: data)
print(response)
And if you really need the MaxResponse
type, then make it a protocol and have your other types conform to it. I'm almost willing to bet that you don't need it though.
In response to your comments, here is a generic solution using structs:
struct LoginResponseData: Codable {
let isLocked: Bool
let __v: Int
let createdAt: Date
}
struct BlogResponseData: Codable {
let xxx: Bool
let yyy: Int
let createdAt: Date
}
struct BaseRresponse<ResponseData: Codable>: Codable {
let status: String?
let data: ResponseData
}