Reputation: 2956
I'm accessing a public transport API and mapping the response to a model using Codable and Alamofire 5.
It mostly seem to work, but the API isn't normalized correctly, meaning I'm getting different types of data for the same properties (Bus lines can be either Int or String etc...)
I'm trying to map a vehicule type property to an enum that looks like this:
enum VehiculeType {
case bus
case trolleybus
case tram
init?(rawValue: String) {
switch rawValue {
case "AB", "ABA", "ABAA":
self = .bus
break
case "TBA", "TBAA":
self = .trolleybus
case "TW6", "TW7", "TW2":
self = .tram
default: return nil
}
}
}
And here is my model that decodes so far correctly.
struct Departure: Codable {
// let lineCode: String
let destination: String
let waitingTime: Int
let waitingMilliseconds: Int
let reroute: String
// let vehiculeType: VehiculeType?
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
destination = try container.decode(String.self, forKey: .destination)
waitingTime = try container.decode(Int.self, forKey: .waitingTime, transformFrom: String.self) ?? 0
waitingMilliseconds = try container.decode(Int.self, forKey: .waitingMilliseconds)
reroute = try container.decode(String.self, forKey: .reroute)
// vehiculeType = try container.decodeIfPresent(String.self, forKey: .vehiculeType, transformFrom: String.self) // This does not work. Correct implementation needed here
}
}
extension Departure {
enum CodingKeys: String, CodingKey {
case destination = "destination"
case waitingTime = "attente"
case waitingMilliseconds = "attenteMilli"
case reroute = "deviation"
// case vehiculeType
}
}
Also I have KeyedDecodingContainer
extensions implemented for converting some types to another. Float for instance..
How can I automatically map the enum to my model when it is decoded so I get enum values attached to it (see enum), instead of Strings? Can I conform enum directly to Codable?
Upvotes: 0
Views: 296
Reputation: 4336
My recommendation for this would be to go ahead and make VehicleType decodable. Note that you can use Decodable
instead of Codable
to make things easier and not have to implement the encoding logic if you are never going to be converting that object back anyway.
your final code would look something like this:
enum DecodingError: Error {
case unknownVehiculeType
}
enum VehiculeType: Decodable {
case bus
case trolleybus
case tram
init(from decoder: Decoder) throws {
let rawValue = try decoder.singleValueContainer().decode(String.self)
switch rawValue {
case "AB", "ABA", "ABAA":
self = .bus
break
case "TBA", "TBAA":
self = .trolleybus
case "TW6", "TW7", "TW2":
self = .tram
default:
throw DecodingError.unknownVehiculeType
}
}
}
struct Departure: Decodable {
// let lineCode: String
let destination: String
let waitingTime: Int
let waitingMilliseconds: Int
let reroute: String
let vehiculeType: VehiculeType?
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
destination = try container.decode(String.self, forKey: .destination)
waitingTime = try container.decode(Int.self, forKey: .waitingTime, transformFrom: String.self) ?? 0
waitingMilliseconds = try container.decode(Int.self, forKey: .waitingMilliseconds)
reroute = try container.decode(String.self, forKey: .reroute)
vehiculeType = try container.decodeIfPresent(VehiculeType.self, forKey: .vehiculeType)
}
}
extension Departure {
enum CodingKeys: String, CodingKey {
case destination = "destination"
case waitingTime = "attente"
case waitingMilliseconds = "attenteMilli"
case reroute = "deviation"
case vehiculeType = "vehiculeType"
}
}
Upvotes: 1