¿Cómo hago una enumeración decodificable en Swift?

Resuelto swift nub asked hace 7 años • 10 respuestas
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é casea 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)
}
swift nub avatar Jun 16 '17 11:06 swift nub
Aceptado

Es bastante fácil, simplemente use valores Stringsin Intprocesar que están asignados implícitamente.

enum PostType: Int, Codable {
    case image, blob
}

imageestá codificado hacia 0y blobhacia1

O

enum PostType: String, Codable {
    case image, blob
}

imageestá codificado hacia "image"y blobhacia"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)
}
vadian avatar Jun 16 '2017 06:06 vadian

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 PostTypeCodableFormestructura y en su lugar usa el KeyedEncodingContainertipo proporcionado por Apple como propiedad en Encodery 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)
proxpero avatar Jul 17 '2017 14:07 proxpero