enum PostType: Decodable {
init(from decoder: Decoder) throws {
// What do i put here?
}
case Image
enum CodingKeys: String, CodingKey {
Swift would throw a .dataCorrupted
error if it encounters unknown enum value. If your data is coming from a server, it can send you an unknown enum value at any time (bug server side, new type added in an API version and you want the previous versions of your app to handle the case gracefully, etc), you'd better be prepared, and code "defensive style" to safely decode your enums.
Here is an example on how to do it, with or without associated value
enum MediaType: Decodable {
case audio
case multipleChoice
case other
// case other(String) -> we could also parametrise the enum like that
init(from decoder: Decoder) throws {
let label = try decoder.singleValueContainer().decode(String.self)
switch label {
case "AUDIO": self = .audio
case "MULTIPLE_CHOICES": self = .multipleChoice
default: self = .other
// default: self = .other(label)
}
}
}
And how to use it in a enclosing struct:
struct Question {
[...]
let type: MediaType
enum CodingKeys: String, CodingKey {
[...]
case type = "type"
}
extension Question: Decodable {
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
[...]
type = try container.decode(MediaType.self, forKey: .type)
}
}
import Foundation
enum DecodableEnum<Enum: RawRepresentable> where Enum.RawValue == String {
case value(Enum)
case error(DecodingError)
var value: Enum? {
switch self {
case .value(let value): return value
case .error: return nil
}
}
var error: DecodingError? {
switch self {
case .value: return nil
case .error(let error): return error
}
}
enum DecodingError: Error {
case notDefined(rawValue: String)
case decoding(error: Error)
}
}
extension DecodableEnum: Decodable {
init(from decoder: Decoder) throws {
do {
let rawValue = try decoder.singleValueContainer().decode(String.self)
guard let layout = Enum(rawValue: rawValue) else {
self = .error(.notDefined(rawValue: rawValue))
return
}
self = .value(layout)
} catch let err {
self = .error(.decoding(error: err))
}
}
}
enum SimpleEnum: String, Codable {
case a, b, c, d
}
struct Model: Decodable {
let num: Int
let str: String
let enum1: DecodableEnum<SimpleEnum>
let enum2: DecodableEnum<SimpleEnum>
let enum3: DecodableEnum<SimpleEnum>
let enum4: DecodableEnum<SimpleEnum>?
}
let dictionary: [String : Any] = ["num": 1, "str": "blablabla", "enum1": "b", "enum2": "_", "enum3": 1]
let data = try! JSONSerialization.data(withJSONObject: dictionary)
let object = try JSONDecoder().decode(Model.self, from: data)
print("1. \(object.enum1.value)")
print("2. \(object.enum2.error)")
print("3. \(object.enum3.error)")
print("4. \(object.enum4)")