Last active
May 12, 2019 21:26
-
-
Save proxpero/189a723fb96bb88fac5bf9e11d6cf9e2 to your computer and use it in GitHub Desktop.
Encoding and Decoding Enums with Associated Values
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// See http://proxpero.com/2017/07/11/encoding-and-decoding-custom-enums-with-associated-values-in-swift-4/ | |
enum Barcode { | |
case upc(Int, Int, Int, Int) | |
case qrCode(String) | |
} | |
extension Barcode: Codable { | |
init(from decoder: Decoder) throws { | |
self = try Barcode.Coding.init(from: decoder).barcode() | |
} | |
func encode(to encoder: Encoder) throws { | |
try Barcode.Coding.init(barcode: self).encode(to: encoder) | |
} | |
} | |
extension Barcode { | |
fileprivate struct Coding: Codable { | |
private struct UPCDigits: Codable { | |
let numberSystem: Int | |
let manufacturer: Int | |
let product: Int | |
let check: Int | |
} | |
private enum CodingError: Error { | |
case barcodeCodingError(String) | |
} | |
private var upc: UPCDigits? | |
private var qrCode: String? | |
fileprivate init(barcode: Barcode) { | |
switch barcode { | |
case .upc(let numberSystem, let manufacturer, let product, let check): | |
self.upc = UPCDigits(numberSystem: numberSystem, manufacturer: manufacturer, product: product, check: check) | |
case .qrCode(let productCode): | |
self.qrCode = productCode | |
} | |
} | |
fileprivate func barcode() throws -> Barcode { | |
switch (upc, qrCode) { | |
case (.some(let upcDigits), nil): | |
return Barcode.upc(upcDigits.numberSystem, upcDigits.manufacturer, upcDigits.product, upcDigits.check) | |
case (nil, .some(let productCode)): | |
return Barcode.qrCode(productCode) | |
default: | |
throw CodingError.barcodeCodingError("Could not convert \(self) into either a upc or a qrCode") | |
} | |
} | |
} | |
} | |
import Foundation | |
let encoder = JSONEncoder() | |
encoder.outputFormatting = .prettyPrinted | |
let decoder = JSONDecoder() | |
var productBarcode: Barcode | |
var data: Data | |
var json: String | |
var expectation: String | |
var result: Barcode | |
productBarcode = .upc(8, 85909, 51226, 3) | |
data = try encoder.encode(productBarcode) | |
json = String.init(data: data, encoding: .utf8)! | |
expectation = """ | |
{ | |
"upc" : { | |
"check" : 3, | |
"manufacturer" : 85909, | |
"product" : 51226, | |
"numberSystem" : 8 | |
} | |
} | |
""" | |
result = try decoder.decode(Barcode.self, from: data) | |
assert(json == expectation) | |
//assert(result == productBarcode) | |
productBarcode = .qrCode("ABCDEFGHIJKLMNOP") | |
data = try encoder.encode(productBarcode) | |
json = String.init(data: data, encoding: .utf8)! | |
expectation = """ | |
{ | |
"qrCode" : "ABCDEFGHIJKLMNOP" | |
} | |
""" | |
result = try decoder.decode(Barcode.self, from: data) | |
assert(json == expectation) | |
//assert(result == productBarcode) |
@stephencelis your way would result in creating different JSON - without names for enum's associated values. so sending that to some kind of API may would result in parsing error since you've passed wrong JSON.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
You can avoid the intermediate
Codable
struct by defining someCodingKey
s and encoding/decoding values from the encoder/decoder container.