¿Cómo hago una enumeración decodificable en Swift?
enum PostType: Decodable {
init(from decoder: Decoder) throws {
// What do i put here?
}
case Image
enum CodingKeys: String, CodingKey {
case image
}
}
¿Qué pongo para completar esto? Además, digamos que cambié case
a esto:
case image(value: Int)
¿Cómo hago para que esto se ajuste a Decodable?
Aquí está mi código completo (que no funciona)
let jsonData = """
{
"count": 4
}
""".data(using: .utf8)!
do {
let decoder = JSONDecoder()
let response = try decoder.decode(PostType.self, from: jsonData)
print(response)
} catch {
print(error)
}
}
}
enum PostType: Int, Codable {
case count = 4
}
Además, ¿cómo manejará una enumeración como esta?
enum PostType: Decodable {
case count(number: Int)
}
Es bastante fácil, simplemente use valores String
sin Int
procesar que están asignados implícitamente.
enum PostType: Int, Codable {
case image, blob
}
image
está codificado hacia 0
y blob
hacia1
O
enum PostType: String, Codable {
case image, blob
}
image
está codificado hacia "image"
y blob
hacia"blob"
Este es un ejemplo sencillo de cómo usarlo:
enum PostType : Int, Codable {
case count = 4
}
struct Post : Codable {
var type : PostType
}
let jsonString = "{\"type\": 4}"
let jsonData = Data(jsonString.utf8)
do {
let decoded = try JSONDecoder().decode(Post.self, from: jsonData)
print("decoded:", decoded.type)
} catch {
print(error)
}
Actualizar
En iOS 13.3+ y macOS 15.1+ se permite codificar/decodificar fragmentos : valores JSON únicos que no están incluidos en un tipo de colección.
let jsonString = "4"
let jsonData = Data(jsonString.utf8)
do {
let decoded = try JSONDecoder().decode(PostType.self, from: jsonData)
print("decoded:", decoded) // -> decoded: count
} catch {
print(error)
}
En Swift 5.5+, incluso es posible codificar/decodificar enumeraciones con valores asociados sin ningún código adicional. Los valores se asignan a un diccionario y se debe especificar una etiqueta de parámetro para cada valor asociado.
enum Rotation: Codable {
case zAxis(angle: Double, speed: Int)
}
let jsonString = #"{"zAxis":{"angle":90,"speed":5}}"#
let jsonData = Data(jsonString.utf8)
do {
let decoded = try JSONDecoder().decode(Rotation.self, from: jsonData)
print("decoded:", decoded)
} catch {
print(error)
}
Cómo hacer que las enumeraciones con tipos asociados se ajusten aCodable
Esta respuesta es similar a la de @Howard Lovatt, pero evita crear una PostTypeCodableForm
estructura y en su lugar usa el KeyedEncodingContainer
tipo proporcionado por Apple como propiedad en Encoder
y Decoder
, lo que reduce el texto estándar.
enum PostType: Codable {
case count(number: Int)
case title(String)
}
extension PostType {
private enum CodingKeys: String, CodingKey {
case count
case title
}
enum PostTypeCodingError: Error {
case decoding(String)
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
if let value = try? values.decode(Int.self, forKey: .count) {
self = .count(number: value)
return
}
if let value = try? values.decode(String.self, forKey: .title) {
self = .title(value)
return
}
throw PostTypeCodingError.decoding("Whoops! \(dump(values))")
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
switch self {
case .count(let number):
try container.encode(number, forKey: .count)
case .title(let value):
try container.encode(value, forKey: .title)
}
}
}
Este código me funciona en Xcode 9b3.
import Foundation // Needed for JSONEncoder/JSONDecoder
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
let decoder = JSONDecoder()
let count = PostType.count(number: 42)
let countData = try encoder.encode(count)
let countJSON = String.init(data: countData, encoding: .utf8)!
print(countJSON)
// {
// "count" : 42
// }
let decodedCount = try decoder.decode(PostType.self, from: countData)
let title = PostType.title("Hello, World!")
let titleData = try encoder.encode(title)
let titleJSON = String.init(data: titleData, encoding: .utf8)!
print(titleJSON)
// {
// "title": "Hello, World!"
// }
let decodedTitle = try decoder.decode(PostType.self, from: titleData)