1

假设我有可解码的属性包装器:

@propertyWrapper
struct OptionalDecodable<Value: Decodable>: Decodable {
  var wrappedValue: Value?
}

编译器确实为以下内容综合了 init

struct Model: Decodable {
  @OptionalDecodable private(set) var string: String?
}

为了测试这是否有效,我只是尝试解码空 JSON(即“{}”)

但是,string属性不被视为可选,即当没有string密钥时,我得到一个错误,即找不到密钥。

有解决办法吗?

4

1 回答 1

0

我不确定这是否是最好的方法,但问题是wrappedValue属性包装器的类型必须匹配属性的类型,并且String不同于String?.

克服这个问题的一种方法是使属性包装器通用,但以允许您从 aString或 an初始化类型的方式进行约束Int

protocol ExpressibleByString {
    init(fromString: String)
}
extension String: ExpressibleByString {
    init(fromString: String) { self = fromString }
}
extension Optional: ExpressibleByString where Wrapped == String {
    init(fromString: String) { self = fromString }
}
@propertyWrapper
struct IntOrString<V: ExpressibleByString & Decodable>: Decodable {
    var wrappedValue: V
}

extension IntOrString {
    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        
        do {
            let int = try container.decode(Int.self)
            wrappedValue = .init(fromString: int.description)
        } catch DecodingError.typeMismatch {
            wrappedValue = try .init(fromString: container.decode(String.self))
        }
   }
}

extension KeyedDecodingContainer {
    func decode<V: ExpressibleByNilLiteral>(_ t: IntOrString<V>.Type, forKey key: K) throws -> IntOrString<V> {
        if let v = try decodeIfPresent(t, forKey: key) {
            return v
        }
        return IntOrString(wrappedValue: nil)
    }
}

然后你可以在 optional 和 non-optional 上使用它String

struct Foo: Decodable {
    @IntOrString
    var p1: String?

    @IntOrString
    var p2: String
}
于 2021-02-21T16:39:06.747 回答