Norway


If we have an enum wih associated types, the compiler can’t automatically generate conformance yet. For example, let’s create an enum that can be one of two values:

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

If we use the latest toolchain, we can conditionally make it conform to Codable (see this gist), but even in the current Swift release we can make it work by adding Codable constraints to our generic parameters:

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

To encode, we create two CodingKeys, and use those keys to tag the value before encoding it:

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

    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)
        }
    }
}

To decode, we mirror the process. First, we try to decode using the .left . If that doesn’t work, we catch the error, and try to decode using the .right key. If that doesn’t work either, the error will be propagated (just like a regular decoding error):

extension Either: Decodable {
    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)
        }
    }
}

Now we can make use of our new Codable conformance:

import Foundation

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


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

And we can see that we also can read it back:

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

The technique above doesn’t just work for the Either type, but can be made to work for any enum with associated values, as long as all associated values are Codable themselves. Just add a case to CodingKeys for each case in your enum.

For more information on Codable, see our book Advanced Swift, which has a full chapter on encoding and decoding.



Source link
Based Blockchain Network

LEAVE A REPLY

Please enter your comment!
Please enter your name here