Here I have a struct that I use for api results
struct Response<Result> {
let status: Int
let message: String
let result: Result
}
Now normally getting this to conform to Codable
would mean that the Result
object needs to be Codable
. Doing that would look like one of the following
struct Response<Result: Codable>: Codable {...}
// or
extension Response: Codable where Result: Codable {}
The problem I'm getting is that some responses don't have the result
key and I want to be able to use the Response
object like with the Void
type instead Response<Void>
much like this so question.
Currently I have a possible way around this, to just declare another Response
type with no result
variable inside it like this:
struct BaseResponse {
let status: Int
let message: String
}
Is there a way around this so that I don't have to declare another Response
type?
I've tried doing the following but nothing works
Void
to Codable
Result: Void
extension Response: Codable where Result: Codable {}
extension Response: Codable where Result: Void {}
Never
also won't work because it doesn't have it's own initializer therefore I can't conform it to Codable
Nothing
type that conforms to Codable
like thisstruct Nothing: Codable, Hashable {
init() {}
static var nothing: Nothing { return .init() }
}
So I can use the response like this
let response: Response<Nothing> = Response(
status: 200,
message: "Success",
result: .nothing
)
or
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.status = try container.decode(forKey: .status)
self.message = try container.decode(forKey: .message)
// These lines don't work.
if let result = try container.decodeIfPresent(Result.self, forKey: .result) {
self.result = result
} else {
self = .nothing
}
}
But the thing is I can't have a decodeIfPresent
method specific for the Nothing
type. So much for that.
You could define your init(from:)
method like this:
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
status = try container.decode(Int.self, forKey: .status)
message = try container.decode(String.self, forKey: .message)
if Result.self is Nothing.Type {
result = Nothing() as! Result
} else {
result = try container.decode(Result.self, forKey: .result)
}
}
You detect that you're in the Response<Nothing>
case and skip the decoding of the result altogether. This way you keep your normal result
decoding in the case where a result is required and can leave it non-optional.
I thought this would result to a crash, but it worked. I tried doing this using extensions
extension Response where Result: Nothing
andextension Response
but it didn't work.