background

Enum types are commonly used in development because they represent different cases of objects. Such as:

enum OrderState: Int {
    case new = 1
    case payed = 2
    case done = 3

}
Copy the code

For enum types whose primitive value is Int, the compiler can automatically synthesize Codable implementations for them.

extension OrderState: Codable {}struct Order: Codable {
    let id: Int
    let state: OrderState
}
Copy the code

It’s not hard to guess the implementation process for an enum type codable simulation:

  • Will be to decode value to Int
  • Attempt to construct Int to enum type
extension OrderState: Codable {
    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        let int = try container.decode(Int.self)
        self = OrderState(rawValue: int) ??...}}Copy the code

However, there is a potential risk in using enum types in Codable. The reason is that OrderState is fixed in the example from the client’s current point of view, whereas the back-end API may iterate in the future and if the value delivered is outside the current declared range, such as case Cancel = 4, the decoder will fail. This corresponds to the possible failure of the construction from Int to OrderState in the above simulated implementation.

Solution: Use struct instead

Enum raw values support codable because they follow the RawReresentable protocol, which provides a default implementation in Codable:

extension RawRepresentable where Self : Decodable.Self.RawValue= =Int {

    /// Creates a new instance by decoding from the given decoder, when the
    /// type's `RawValue` is `Int`.
    ///
    /// This initializer throws an error if reading from the decoder fails, or
    /// if the data read is corrupted or otherwise invalid.
    ///
    /// - Parameter decoder: The decoder to read data from.
    public init(from decoder: Decoder) throws
}
Copy the code

With such an implementation, you can use struct to follow RawReprentable to get:

  • To avoid case failure, any INT can be delivered
  • Replace concrete case declarations with static attributes

Such as:

struct OrderStateStruct: RawRepresentable, Codable {
    let rawValue: Int
    
    static let new = OrderStateStruct(rawValue: 1)
    static let payed = OrderStateStruct(rawValue: 2)
    static let done = OrderStateStruct(rawValue: 3)
}
Copy the code

summary

Swift’s strong typing features stand out in Codable, so take care when designing parameter types. A few caveats:

  • A similar scheme can be used for enumeration scenarios where the raw value is string
  • In struct mode, Swift can continue to retain the support of Swift case, the difference is that there will be default case, because it is not exhaustive, so in the future when new cases are added, we need to search and supplement in the place where this enumeration is used. There is no compiler support for enum types, so the tradeoff is not too big.
  • Considering the use of enum, then we need to consider the decode failure of the bottom of the pocket processing, can be customized to implement Decodable perspective to think and expand.

The resources

Sundel: Codable synthesis for Swift enums

Onevcat: Use Property Wrapper to set default values for Codable decoding