Consider the following simple JSON:
{
"title": "Some title",
"subtitle": "Some subtitle",
"button_type": "rounded"
}
This is my current approach towards decoding the buttonType field:
// I dont have access to this enums implementation as it comes from a 3rd party lib.
enum ButtonType {
case squared
case simple
case rounded
case normal
}
struct ResponseModel: Decodable {
var title: String
var subtitle: String
var type: ButtonType
enum CodingKeys: String, CodingKey {
case title = "title"
case subtitle = "subtitle"
case type = "button_type"
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
title = try container.decode(String.self, forKey: .title)
subtitle = try container.decode(String.self, forKey: .subtitle)
let stringType = try container.decode(String.self, forKey: .type)
switch stringType {
case "squared":
type = .squared
case "simple":
type = .simple
case "rounded":
type = .rounded
default:
type = .normal
}
}
}
Is there any prettier way to accomplish decoding the string to the custom enum without that nasty switch statement iterating over a plain string?. I sadly do not have access to the enums implementation as it comes from a third party library. If I did I would just make the enum conform to String & Codable and have Codable work its magic, but I dont.
Thanks!
CodePudding user response:
You can create your own enum
enum MyButtonType: String {
case squared
case simple
case rounded
case normal
var toButtonType: ButtonType {
switch self {
case .squared: return .squared
case .simple: return .simple
case .rounded: return .rounded
case .normal: return .normal
}
}
}
Then change
var type: ButtonType
To
var type: MyButtonType
And when you need the custom enum
just use
responseModel.type.toButtonType
The String
after the :
in the enum makes it conform to RawRepresentable<String>
you won’t need the custom init
anymore.
You can also manually conform to RawPresentable<String> but it requires all the
String` entries.
extension ButtonType: RawRepresentable {
typealias RawValue = String
var rawValue: String {
switch self {
case .squared:
return "squared"
case .simple:
return "simple"
case .rounded:
return "rounded"
case .normal:
return "normal"
}
}
init?(rawValue: String) {
switch rawValue {
case "squared":
self = .squared
case "simple":
self = .simple
case "rounded":
self = .rounded
case "normal":
self = .normal
default:
return nil
}
}
}
With this you don't have to change the Type in the model.
CodePudding user response:
Just another solution.
Even if you don't control the how ButtonType
is defined, you can still conform it to Decodable
using an extension.
Step 1: Conform ButtonType
to Decodable
extension ButtonType: Decodable {
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let text = try container.decode(String.self)
switch text {
case "squared": self = .squared
case "simple": self = .simple
case "rounded": self = .rounded
case "normal": self = .normal
default: throw DecodingError.dataCorrupted(.init(codingPath: [], debugDescription: "Cannot initialize ButtonType from invalid String value \(text)"))
}
}
}
Step 2: Use it in your ResponseModel
struct ResponseModel: Decodable {
var title: String
var subtitle: String
var type: ButtonType
enum CodingKeys: String, CodingKey {
case title = "title"
case subtitle = "subtitle"
case type = "button_type"
}
}