Swift 4 中默认大小写的可编码枚举
Codable enum with default case in Swift 4
我定义了一个enum
如下:
enum Type: String, Codable {
case text = "text"
case image = "image"
case document = "document"
case profile = "profile"
case sign = "sign"
case inputDate = "input_date"
case inputText = "input_text"
case inputNumber = "input_number"
case inputOption = "input_option"
case unknown
}
映射 JSON 字符串 属性。
自动序列化和反序列化工作正常,但我发现如果遇到不同的字符串,反序列化失败。
是否可以定义映射任何其他可用案例的 unknown
案例?
这可能非常有用,因为此数据来自 RESTFul API,将来可能会发生变化。
您必须实施 init(from decoder: Decoder) throws
初始化程序并检查有效值:
struct SomeStruct: Codable {
enum SomeType: String, Codable {
case text
case image
case document
case profile
case sign
case inputDate = "input_date"
case inputText = "input_text"
case inputNumber = "input_number"
case inputOption = "input_option"
case unknown
}
var someType: SomeType
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
someType = (try? values.decode(SomeType.self, forKey: .someType)) ?? .unknown
}
}
您可以扩展您的 Codable
类型并在失败时指定默认值:
enum Type: String {
case text,
image,
document,
profile,
sign,
inputDate = "input_date",
inputText = "input_text" ,
inputNumber = "input_number",
inputOption = "input_option",
unknown
}
extension Type: Codable {
public init(from decoder: Decoder) throws {
self = try Type(rawValue: decoder.singleValueContainer().decode(RawValue.self)) ?? .unknown
}
}
edit/update:
Xcode 11.2 • Swift 5.1 或更高版本
创建默认为 CaseIterable & Decodable
枚举的最后一个案例的协议:
protocol CaseIterableDefaultsLast: Decodable & CaseIterable & RawRepresentable
where RawValue: Decodable, AllCases: BidirectionalCollection { }
extension CaseIterableDefaultsLast {
init(from decoder: Decoder) throws {
self = try Self(rawValue: decoder.singleValueContainer().decode(RawValue.self)) ?? Self.allCases.last!
}
}
游乐场测试:
enum Type: String, CaseIterableDefaultsLast {
case text, image, document, profile, sign, inputDate = "input_date", inputText = "input_text" , inputNumber = "input_number", inputOption = "input_option", unknown
}
let types = try! JSONDecoder().decode([Type].self , from: Data(#"["text","image","sound"]"#.utf8)) // [text, image, unknown]
您可以删除 Type
的原始类型,并使 unknown case 处理关联值。但这是有代价的。您以某种方式需要案例的原始值。受到 this and SO answers 的启发,我想出了这个优雅的解决方案来解决你的问题。
为了能够存储原始值,我们将维护另一个枚举,但作为私有:
enum Type {
case text
case image
case document
case profile
case sign
case inputDate
case inputText
case inputNumber
case inputOption
case unknown(String)
// Make this private
private enum RawValues: String, Codable {
case text = "text"
case image = "image"
case document = "document"
case profile = "profile"
case sign = "sign"
case inputDate = "input_date"
case inputText = "input_text"
case inputNumber = "input_number"
case inputOption = "input_option"
// No such case here for the unknowns
}
}
将 encoding
& decoding
部分移动到扩展:
可解码部分:
extension Type: Decodable {
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
// As you already know your RawValues is String actually, you decode String here
let stringForRawValues = try container.decode(String.self)
// This is the trick here...
switch stringForRawValues {
// Now You can switch over this String with cases from RawValues since it is String
case RawValues.text.rawValue:
self = .text
case RawValues.image.rawValue:
self = .image
case RawValues.document.rawValue:
self = .document
case RawValues.profile.rawValue:
self = .profile
case RawValues.sign.rawValue:
self = .sign
case RawValues.inputDate.rawValue:
self = .inputDate
case RawValues.inputText.rawValue:
self = .inputText
case RawValues.inputNumber.rawValue:
self = .inputNumber
case RawValues.inputOption.rawValue:
self = .inputOption
// Now handle all unknown types. You just pass the String to Type's unknown case.
// And this is true for every other unknowns that aren't defined in your RawValues
default:
self = .unknown(stringForRawValues)
}
}
}
可编码部分:
extension Type: Encodable {
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
switch self {
case .text:
try container.encode(RawValues.text)
case .image:
try container.encode(RawValues.image)
case .document:
try container.encode(RawValues.document)
case .profile:
try container.encode(RawValues.profile)
case .sign:
try container.encode(RawValues.sign)
case .inputDate:
try container.encode(RawValues.inputDate)
case .inputText:
try container.encode(RawValues.inputText)
case .inputNumber:
try container.encode(RawValues.inputNumber)
case .inputOption:
try container.encode(RawValues.inputOption)
case .unknown(let string):
// You get the actual String here from the associated value and just encode it
try container.encode(string)
}
}
}
示例:
我只是将它包装在一个容器结构中(因为我们将使用 JSONEncoder/JSONDecoder)作为:
struct Root: Codable {
let type: Type
}
对于大小写以外的值:
let rootObject = Root(type: Type.document)
do {
let encodedRoot = try JSONEncoder().encode(rootObject)
do {
let decodedRoot = try JSONDecoder().decode(Root.self, from: encodedRoot)
print(decodedRoot.type) // document
} catch {
print(error)
}
} catch {
print(error)
}
大小写未知的值:
let rootObject = Root(type: Type.unknown("new type"))
do {
let encodedRoot = try JSONEncoder().encode(rootObject)
do {
let decodedRoot = try JSONDecoder().decode(Root.self, from: encodedRoot)
print(decodedRoot.type) // unknown("new type")
} catch {
print(error)
}
} catch {
print(error)
}
I put the example with local objects. You can try with your REST API response.
这是一个基于 nayem 答案的替代方案,它通过使用内部 RawValues
初始化的可选绑定提供了稍微更精简的语法:
enum MyEnum: Codable {
case a, b, c
case other(name: String)
private enum RawValue: String, Codable {
case a = "a"
case b = "b"
case c = "c"
}
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let decodedString = try container.decode(String.self)
if let value = RawValue(rawValue: decodedString) {
switch value {
case .a:
self = .a
case .b:
self = .b
case .c:
self = .c
}
} else {
self = .other(name: decodedString)
}
}
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
switch self {
case .a:
try container.encode(RawValue.a)
case .b:
try container.encode(RawValue.b)
case .c:
try container.encode(RawValue.c)
case .other(let name):
try container.encode(name)
}
}
}
如果您确定所有现有的枚举案例名称都匹配它们所代表的基础字符串值,您可以将 RawValue
简化为:
private enum RawValue: String, Codable {
case a, b, c
}
...和 encode(to:)
至:
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
if let rawValue = RawValue(rawValue: String(describing: self)) {
try container.encode(rawValue)
} else if case .other(let name) = self {
try container.encode(name)
}
}
这里有一个使用它的实际例子,例如,你想建模 SomeValue
有一个你想建模为枚举的 属性:
struct SomeValue: Codable {
enum MyEnum: Codable {
case a, b, c
case other(name: String)
private enum RawValue: String, Codable {
case a = "a"
case b = "b"
case c = "letter_c"
}
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let decodedString = try container.decode(String.self)
if let value = RawValue(rawValue: decodedString) {
switch value {
case .a:
self = .a
case .b:
self = .b
case .c:
self = .c
}
} else {
self = .other(name: decodedString)
}
}
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
switch self {
case .a:
try container.encode(RawValue.a)
case .b:
try container.encode(RawValue.b)
case .c:
try container.encode(RawValue.c)
case .other(let name):
try container.encode(name)
}
}
}
}
let jsonData = """
[
{ "value": "a" },
{ "value": "letter_c" },
{ "value": "c" },
{ "value": "Other value" }
]
""".data(using: .utf8)!
let decoder = JSONDecoder()
if let values = try? decoder.decode([SomeValue].self, from: jsonData) {
values.forEach { print([=13=].value) }
let encoder = JSONEncoder()
if let encodedJson = try? encoder.encode(values) {
print(String(data: encodedJson, encoding: .utf8)!)
}
}
/* Prints:
a
c
other(name: "c")
other(name: "Other value")
[{"value":"a"},{"value":"letter_c"},{"value":"c"},{"value":"Other value"}]
*/
添加此扩展并设置 YourEnumName
。
extension <#YourEnumName#>: Codable {
public init(from decoder: Decoder) throws {
self = try <#YourEnumName#>(rawValue: decoder.singleValueContainer().decode(RawValue.self)) ?? .unknown
}
}
@LeoDabus 感谢您的回答。我对它们进行了一些修改,为 String 枚举制定了一个似乎对我有用的协议:
protocol CodableWithUnknown: Codable {}
extension CodableWithUnknown where Self: RawRepresentable, Self.RawValue == String {
init(from decoder: Decoder) throws {
do {
try self = Self(rawValue: decoder.singleValueContainer().decode(RawValue.self))!
} catch {
if let unknown = Self(rawValue: "unknown") {
self = unknown
} else {
throw error
}
}
}
}
enum Type: String, Codable, Equatable {
case image
case document
case unknown
public init(from decoder: Decoder) throws {
guard let rawValue = try? decoder.singleValueContainer().decode(String.self) else {
self = .unknown
return
}
self = Type(rawValue: rawValue) ?? .unknown
}
}
让我们从一个测试用例开始。我们希望这能通过:
func testCodableEnumWithUnknown() throws {
enum Fruit: String, Decodable, CodableEnumWithUnknown {
case banana
case apple
case unknown
}
struct Container: Decodable {
let fruit: Fruit
}
let data = #"{"fruit": "orange"}"#.data(using: .utf8)!
let val = try JSONDecoder().decode(Container.self, from: data)
XCTAssert(val.fruit == .unknown)
}
我们的协议 CodableEnumWithUnknown
表示支持解码器在数据中出现未知值时应使用的 unknown
情况。
然后是解决方案:
public protocol CodableEnumWithUnknown: Codable, RawRepresentable {
static var unknown: Self { get }
}
public extension CodableEnumWithUnknown where Self: RawRepresentable, Self.RawValue == String {
init(from decoder: Decoder) throws {
self = (try? Self(rawValue: decoder.singleValueContainer().decode(RawValue.self))) ?? Self.unknown
}
}
诀窍是让您的枚举实现 CodableEnumWithUnknown
协议并添加 unknown
大小写。
我赞成使用其他帖子中提到的 .allCases.last!
实现的上述解决方案,因为我发现它们有点脆弱,因为编译器没有对它们进行类型检查。
您可以使用此扩展程序进行编码/解码
(此代码段支持 Int 和 String RawValue 类型枚举,但可以轻松扩展以适应其他类型)
extension NSCoder {
func encodeEnum<T: RawRepresentable>(_ value: T?, forKey key: String) {
guard let rawValue = value?.rawValue else {
return
}
if let s = rawValue as? String {
encode(s, forKey: key)
} else if let i = rawValue as? Int {
encode(i, forKey: key)
} else {
assert(false, "Unsupported type")
}
}
func decodeEnum<T: RawRepresentable>(forKey key: String, defaultValue: T) -> T {
if let s = decodeObject(forKey: key) as? String, s is T.RawValue {
return T(rawValue: s as! T.RawValue) ?? defaultValue
} else {
let i = decodeInteger(forKey: key)
if i is T.RawValue {
return T(rawValue: i as! T.RawValue) ?? defaultValue
}
}
return defaultValue
}
}
比使用它
// encode
coder.encodeEnum(source, forKey: "source")
// decode
source = coder.decodeEnum(forKey: "source", defaultValue: Source.home)
以下方法将解码所有类型的枚举,其 RawValue 类型为 Decodable (Int, String, ..),如果失败则 returns nil。这将防止由 JSON 响应中的 non-existent 原始值引起的崩溃。
定义:
extension Decodable {
static func decode<T: RawRepresentable, R, K: CodingKey>(rawValue _: R.Type, forKey key: K, decoder: Decoder) throws -> T? where T.RawValue == R, R: Decodable {
let container = try decoder.container(keyedBy: K.self)
guard let rawValue = try container.decodeIfPresent(R.self, forKey: key) else { return nil }
return T(rawValue: rawValue)
}
}
用法:
enum Status: Int, Decodable {
case active = 1
case disabled = 2
}
struct Model: Decodable {
let id: String
let status: Status?
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
id = try container.decodeIfPresent(String.self, forKey: .id)
status = try .decode(rawValue: Int.self, forKey: .status, decoder: decoder)
}
}
// status: -1 reutrns nil
// status: 2 returns .disabled
我定义了一个enum
如下:
enum Type: String, Codable {
case text = "text"
case image = "image"
case document = "document"
case profile = "profile"
case sign = "sign"
case inputDate = "input_date"
case inputText = "input_text"
case inputNumber = "input_number"
case inputOption = "input_option"
case unknown
}
映射 JSON 字符串 属性。 自动序列化和反序列化工作正常,但我发现如果遇到不同的字符串,反序列化失败。
是否可以定义映射任何其他可用案例的 unknown
案例?
这可能非常有用,因为此数据来自 RESTFul API,将来可能会发生变化。
您必须实施 init(from decoder: Decoder) throws
初始化程序并检查有效值:
struct SomeStruct: Codable {
enum SomeType: String, Codable {
case text
case image
case document
case profile
case sign
case inputDate = "input_date"
case inputText = "input_text"
case inputNumber = "input_number"
case inputOption = "input_option"
case unknown
}
var someType: SomeType
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
someType = (try? values.decode(SomeType.self, forKey: .someType)) ?? .unknown
}
}
您可以扩展您的 Codable
类型并在失败时指定默认值:
enum Type: String {
case text,
image,
document,
profile,
sign,
inputDate = "input_date",
inputText = "input_text" ,
inputNumber = "input_number",
inputOption = "input_option",
unknown
}
extension Type: Codable {
public init(from decoder: Decoder) throws {
self = try Type(rawValue: decoder.singleValueContainer().decode(RawValue.self)) ?? .unknown
}
}
edit/update:
Xcode 11.2 • Swift 5.1 或更高版本
创建默认为 CaseIterable & Decodable
枚举的最后一个案例的协议:
protocol CaseIterableDefaultsLast: Decodable & CaseIterable & RawRepresentable
where RawValue: Decodable, AllCases: BidirectionalCollection { }
extension CaseIterableDefaultsLast {
init(from decoder: Decoder) throws {
self = try Self(rawValue: decoder.singleValueContainer().decode(RawValue.self)) ?? Self.allCases.last!
}
}
游乐场测试:
enum Type: String, CaseIterableDefaultsLast {
case text, image, document, profile, sign, inputDate = "input_date", inputText = "input_text" , inputNumber = "input_number", inputOption = "input_option", unknown
}
let types = try! JSONDecoder().decode([Type].self , from: Data(#"["text","image","sound"]"#.utf8)) // [text, image, unknown]
您可以删除 Type
的原始类型,并使 unknown case 处理关联值。但这是有代价的。您以某种方式需要案例的原始值。受到 this and
为了能够存储原始值,我们将维护另一个枚举,但作为私有:
enum Type {
case text
case image
case document
case profile
case sign
case inputDate
case inputText
case inputNumber
case inputOption
case unknown(String)
// Make this private
private enum RawValues: String, Codable {
case text = "text"
case image = "image"
case document = "document"
case profile = "profile"
case sign = "sign"
case inputDate = "input_date"
case inputText = "input_text"
case inputNumber = "input_number"
case inputOption = "input_option"
// No such case here for the unknowns
}
}
将 encoding
& decoding
部分移动到扩展:
可解码部分:
extension Type: Decodable {
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
// As you already know your RawValues is String actually, you decode String here
let stringForRawValues = try container.decode(String.self)
// This is the trick here...
switch stringForRawValues {
// Now You can switch over this String with cases from RawValues since it is String
case RawValues.text.rawValue:
self = .text
case RawValues.image.rawValue:
self = .image
case RawValues.document.rawValue:
self = .document
case RawValues.profile.rawValue:
self = .profile
case RawValues.sign.rawValue:
self = .sign
case RawValues.inputDate.rawValue:
self = .inputDate
case RawValues.inputText.rawValue:
self = .inputText
case RawValues.inputNumber.rawValue:
self = .inputNumber
case RawValues.inputOption.rawValue:
self = .inputOption
// Now handle all unknown types. You just pass the String to Type's unknown case.
// And this is true for every other unknowns that aren't defined in your RawValues
default:
self = .unknown(stringForRawValues)
}
}
}
可编码部分:
extension Type: Encodable {
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
switch self {
case .text:
try container.encode(RawValues.text)
case .image:
try container.encode(RawValues.image)
case .document:
try container.encode(RawValues.document)
case .profile:
try container.encode(RawValues.profile)
case .sign:
try container.encode(RawValues.sign)
case .inputDate:
try container.encode(RawValues.inputDate)
case .inputText:
try container.encode(RawValues.inputText)
case .inputNumber:
try container.encode(RawValues.inputNumber)
case .inputOption:
try container.encode(RawValues.inputOption)
case .unknown(let string):
// You get the actual String here from the associated value and just encode it
try container.encode(string)
}
}
}
示例:
我只是将它包装在一个容器结构中(因为我们将使用 JSONEncoder/JSONDecoder)作为:
struct Root: Codable {
let type: Type
}
对于大小写以外的值:
let rootObject = Root(type: Type.document)
do {
let encodedRoot = try JSONEncoder().encode(rootObject)
do {
let decodedRoot = try JSONDecoder().decode(Root.self, from: encodedRoot)
print(decodedRoot.type) // document
} catch {
print(error)
}
} catch {
print(error)
}
大小写未知的值:
let rootObject = Root(type: Type.unknown("new type"))
do {
let encodedRoot = try JSONEncoder().encode(rootObject)
do {
let decodedRoot = try JSONDecoder().decode(Root.self, from: encodedRoot)
print(decodedRoot.type) // unknown("new type")
} catch {
print(error)
}
} catch {
print(error)
}
I put the example with local objects. You can try with your REST API response.
这是一个基于 nayem 答案的替代方案,它通过使用内部 RawValues
初始化的可选绑定提供了稍微更精简的语法:
enum MyEnum: Codable {
case a, b, c
case other(name: String)
private enum RawValue: String, Codable {
case a = "a"
case b = "b"
case c = "c"
}
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let decodedString = try container.decode(String.self)
if let value = RawValue(rawValue: decodedString) {
switch value {
case .a:
self = .a
case .b:
self = .b
case .c:
self = .c
}
} else {
self = .other(name: decodedString)
}
}
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
switch self {
case .a:
try container.encode(RawValue.a)
case .b:
try container.encode(RawValue.b)
case .c:
try container.encode(RawValue.c)
case .other(let name):
try container.encode(name)
}
}
}
如果您确定所有现有的枚举案例名称都匹配它们所代表的基础字符串值,您可以将 RawValue
简化为:
private enum RawValue: String, Codable {
case a, b, c
}
...和 encode(to:)
至:
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
if let rawValue = RawValue(rawValue: String(describing: self)) {
try container.encode(rawValue)
} else if case .other(let name) = self {
try container.encode(name)
}
}
这里有一个使用它的实际例子,例如,你想建模 SomeValue
有一个你想建模为枚举的 属性:
struct SomeValue: Codable {
enum MyEnum: Codable {
case a, b, c
case other(name: String)
private enum RawValue: String, Codable {
case a = "a"
case b = "b"
case c = "letter_c"
}
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let decodedString = try container.decode(String.self)
if let value = RawValue(rawValue: decodedString) {
switch value {
case .a:
self = .a
case .b:
self = .b
case .c:
self = .c
}
} else {
self = .other(name: decodedString)
}
}
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
switch self {
case .a:
try container.encode(RawValue.a)
case .b:
try container.encode(RawValue.b)
case .c:
try container.encode(RawValue.c)
case .other(let name):
try container.encode(name)
}
}
}
}
let jsonData = """
[
{ "value": "a" },
{ "value": "letter_c" },
{ "value": "c" },
{ "value": "Other value" }
]
""".data(using: .utf8)!
let decoder = JSONDecoder()
if let values = try? decoder.decode([SomeValue].self, from: jsonData) {
values.forEach { print([=13=].value) }
let encoder = JSONEncoder()
if let encodedJson = try? encoder.encode(values) {
print(String(data: encodedJson, encoding: .utf8)!)
}
}
/* Prints:
a
c
other(name: "c")
other(name: "Other value")
[{"value":"a"},{"value":"letter_c"},{"value":"c"},{"value":"Other value"}]
*/
添加此扩展并设置 YourEnumName
。
extension <#YourEnumName#>: Codable {
public init(from decoder: Decoder) throws {
self = try <#YourEnumName#>(rawValue: decoder.singleValueContainer().decode(RawValue.self)) ?? .unknown
}
}
@LeoDabus 感谢您的回答。我对它们进行了一些修改,为 String 枚举制定了一个似乎对我有用的协议:
protocol CodableWithUnknown: Codable {}
extension CodableWithUnknown where Self: RawRepresentable, Self.RawValue == String {
init(from decoder: Decoder) throws {
do {
try self = Self(rawValue: decoder.singleValueContainer().decode(RawValue.self))!
} catch {
if let unknown = Self(rawValue: "unknown") {
self = unknown
} else {
throw error
}
}
}
}
enum Type: String, Codable, Equatable {
case image
case document
case unknown
public init(from decoder: Decoder) throws {
guard let rawValue = try? decoder.singleValueContainer().decode(String.self) else {
self = .unknown
return
}
self = Type(rawValue: rawValue) ?? .unknown
}
}
让我们从一个测试用例开始。我们希望这能通过:
func testCodableEnumWithUnknown() throws {
enum Fruit: String, Decodable, CodableEnumWithUnknown {
case banana
case apple
case unknown
}
struct Container: Decodable {
let fruit: Fruit
}
let data = #"{"fruit": "orange"}"#.data(using: .utf8)!
let val = try JSONDecoder().decode(Container.self, from: data)
XCTAssert(val.fruit == .unknown)
}
我们的协议 CodableEnumWithUnknown
表示支持解码器在数据中出现未知值时应使用的 unknown
情况。
然后是解决方案:
public protocol CodableEnumWithUnknown: Codable, RawRepresentable {
static var unknown: Self { get }
}
public extension CodableEnumWithUnknown where Self: RawRepresentable, Self.RawValue == String {
init(from decoder: Decoder) throws {
self = (try? Self(rawValue: decoder.singleValueContainer().decode(RawValue.self))) ?? Self.unknown
}
}
诀窍是让您的枚举实现 CodableEnumWithUnknown
协议并添加 unknown
大小写。
我赞成使用其他帖子中提到的 .allCases.last!
实现的上述解决方案,因为我发现它们有点脆弱,因为编译器没有对它们进行类型检查。
您可以使用此扩展程序进行编码/解码 (此代码段支持 Int 和 String RawValue 类型枚举,但可以轻松扩展以适应其他类型)
extension NSCoder {
func encodeEnum<T: RawRepresentable>(_ value: T?, forKey key: String) {
guard let rawValue = value?.rawValue else {
return
}
if let s = rawValue as? String {
encode(s, forKey: key)
} else if let i = rawValue as? Int {
encode(i, forKey: key)
} else {
assert(false, "Unsupported type")
}
}
func decodeEnum<T: RawRepresentable>(forKey key: String, defaultValue: T) -> T {
if let s = decodeObject(forKey: key) as? String, s is T.RawValue {
return T(rawValue: s as! T.RawValue) ?? defaultValue
} else {
let i = decodeInteger(forKey: key)
if i is T.RawValue {
return T(rawValue: i as! T.RawValue) ?? defaultValue
}
}
return defaultValue
}
}
比使用它
// encode
coder.encodeEnum(source, forKey: "source")
// decode
source = coder.decodeEnum(forKey: "source", defaultValue: Source.home)
以下方法将解码所有类型的枚举,其 RawValue 类型为 Decodable (Int, String, ..),如果失败则 returns nil。这将防止由 JSON 响应中的 non-existent 原始值引起的崩溃。
定义:
extension Decodable {
static func decode<T: RawRepresentable, R, K: CodingKey>(rawValue _: R.Type, forKey key: K, decoder: Decoder) throws -> T? where T.RawValue == R, R: Decodable {
let container = try decoder.container(keyedBy: K.self)
guard let rawValue = try container.decodeIfPresent(R.self, forKey: key) else { return nil }
return T(rawValue: rawValue)
}
}
用法:
enum Status: Int, Decodable {
case active = 1
case disabled = 2
}
struct Model: Decodable {
let id: String
let status: Status?
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
id = try container.decodeIfPresent(String.self, forKey: .id)
status = try .decode(rawValue: Int.self, forKey: .status, decoder: decoder)
}
}
// status: -1 reutrns nil
// status: 2 returns .disabled