Swift 之 Codable Enums

3,019 阅读1分钟

对于一个带有关联值的枚举,如何实现 Codable?

enum Either<A: Codable, B: Codable> {
    case left(A)
    case right(B)
}

extension Either: Codable {
    // error: Type 'Either<A, B>' does not conform to protocol 'Decodable'
}

需要手动实现:

extension Either: Codable {
    enum CodingKeys: CodingKey {
        case left
        case right
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        do {
            let leftValue = try container.decode(A.self, forKey: .left)
            self = .left(leftValue)
        } catch {
            let rightValue = try container.decode(B.self, forKey: .right)
            self = .right(rightValue)
        }
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        switch self {
        case .left(let value):
            try container.encode(value, forKey: .left)
        case .right(let value):
            try container.encode(value, forKey: .right)
        }
    }
}

使用:


let values: [Either<String, Int>] = [.left("Hi"), .right(42)]

let encoder = JSONEncoder()
let data = try! encoder.encode(values)
let str = String(data: data, encoding: .utf8)
print(str ?? "") 
// [{"left":"Hi"},{"right":42}]

let decoder = JSONDecoder()
let result = try! decoder.decode([Either<String,Int>].self, from: data)
print(result) 
// [__lldb_expr_5.Either<Swift.String, Swift.Int>.left("Hi"), __lldb_expr_5.Either<Swift.String, Swift.Int>.right(42)]

对于任意的关联值枚举都可以通过此法自定义实现 Codable,对于 Either 这种类型你是否发现它很适合解决服务器返回的数据类型多样性问题。

原文阅读:Swift Tip: Codable Enums

更多阅读,请关注官方微信公众号: