特殊类型的可重写码

时间:2019-05-08 03:34:53

标签: swift decodable

是否有一种方法可以覆盖特定类型的Decodable实现,以使该类型成为Codable结构的一部分?

具体来说,我有这个结构:

struct Activity: Codable {
    let id: Int
    let name: String
<...>
    let distance: Measurement<UnitLength>
}

我想要一种提供像这样的解码初始化器的方法:


extension Measurement where UnitType == UnitLength {
    public init(from decoder: Decoder) throws {
        self = Measurement(value: try decoder.singleValueContainer().decode(Double.self), unit: UnitLength.meters)
    }
}

这样我就可以在解码时将Double的值转换为Measurement<UnitLength>,而而无需必须为每个init(from decoder: Decoder)提供自定义struct其中有一个Measurement

init可以很好地编译,但是似乎没有从标准Codable解码过程中调用。

我的解决方法是Activity结构的初始化:

init(from decoder: Decoder) throws {
    let values = try decoder.container(keyedBy: CodingKeys.self)

<...>
    distance = Measurement(value: try values.decode(Double.self, forKey: .distance), unit: UnitLength.meters)
}

但是我宁愿在Measurement上实现一次,而不是为每种可能使用它的类型实现。

1 个答案:

答案 0 :(得分:1)

一种可能满足您需要的解决方案是定义自己的测量结构,该结构假定以米为单位的长度测量。

struct MyMeasurement {
    let length: Measurement<UnitLength>
}

extension MyMeasurement: Codable {
    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        length = Measurement(value: try container.decode(Double.self), unit: UnitLength.meters)
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.singleValueContainer()
        try container.encode(length.value)
    }
}

然后更新您的Activity结构以使用MyMeasurement

struct Activity: Codable {
    let id: Int
    let name: String
<...>
    let distance: MyMeasurement
}

然后您可以正常解码:

let decoder = JSONDecoder()
do {
    let activity = try decoder.decode(Activity.self, from: someJSONData)
    print(activity.distance.length)
} catch {
    print(error)
}

您可能想要一个MyMeasurement更好的名称,暗示它是一个以米为单位的长度。

您可以在任何具有米长度属性的结构中重用MyMeasurement,而无需为该结构编写自定义init(from:)