Custom decoding with property wrapper

Updated
Jun 11, 2021 1:02 PM
Created
Jun 11, 2021 12:51 PM
Tags
Swift

Swift's Codable (Decodable) is very handy for decoding from JSON. We can get to do that without writing the code how decode value.

However, we have to write everything on how decodes if we get one or more exceptions from automatic decoding. It's so disappointing.

We solve this problem with using PropertyWrapper.

let json = """
{
  "name": "muukii",
  "number": "0.07"
}
""".data(using: .utf8)!
struct Container: Decodable {

  var name: String
  @CustomDecoding<Decimal, StringToDecimalDecoder> var number: Decimal

}
enum StringToDecimalDecoder: CustomDecoder {

  enum Error: Swift.Error {
    case unableToCreateDecimalFromStringValue
  }

  typealias Value = Decimal

  static func decode(from decoder: Decoder) throws -> Decimal {
    let value = try decoder.singleValueContainer().decode(String.self)
    guard let decimal = Decimal.init(string: value) else {
      throw Error.unableToCreateDecimalFromStringValue
    }
    return decimal
  }
}

public protocol CustomDecoder {

  associatedtype Value

  static func decode(from decoder: Decoder) throws -> Value
}

@propertyWrapper
public struct CustomDecoding<Value, Decoding: CustomDecoder>: Decodable
where Decoding.Value == Value {

  public var wrappedValue: Value

  public init(
    wrappedValue: Value
  ) {
    self.wrappedValue = wrappedValue
  }

  public init(
    from decoder: Decoder
  ) throws {

    let value = try Decoding.decode(from: decoder)
    self.wrappedValue = value
  }

}
CustomDecoder.playground.zip14.2KB