在Swift 4中更新具有多种类型的标签

时间:2019-04-04 15:51:46

标签: ios swift

我有一个API,有时会以Int的形式返回JSON中的特定键,而有时它会返回与String相同的键,我通过创建一个枚举IntOrString来解决此问题。现在的问题是,当我调用API更新这些特定键的标签时,类型是错误的。

  

然后我遇到错误,无法将Double转换为Type   DoubleOrString

enum DoubleOrString: Codable {

    case double(Double)
    case string(String)

    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        do {
            self = try .double(container.decode(Double.self))
        } catch DecodingError.typeMismatch {
            do {
                self = try .string(container.decode(String.self))
            } catch DecodingError.typeMismatch {
                throw DecodingError.typeMismatch(
                    DoubleOrString.self,
                    DecodingError.Context(
                        codingPath: decoder.codingPath,
                        debugDescription: "Encoded payload conflicts with expected type, (Double or String)"
                    )
                )
            }
        }
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.singleValueContainer()
        switch self {
        case .double(let double):
            try container.encode(double)
        case .string(let string):
            try container.encode(string)
        }
    }
}

下面是我要更新标签的地方

self.ageLabel.text = "\(pData.info.detailedInfo?.ageNumber ?? 0.0)"

2 个答案:

答案 0 :(得分:3)

首先,我认为您应该确定您是否真的想在整个程序中将其保留为DoubleOrString。您需要跟踪区别吗?还是可以修改解码器以使其始终保持两倍?您的内部数据模型不必重新创建JSON中的每个错误。

如果您确实想要维护枚举,那么我认为您正在寻找的是符合以下要求的东西:

extension DoubleOrString {
    var doubleValue: Double? {
        switch self {
        case .double(let double): return double
        case .string(let string): return Double(string)
        }
    }
}

self.ageLabel.text = "\(pData.info.detailedInfo?.ageNumber.doubleValue ?? 0.0)"

(当然,这里的首选解决方案是更正JSON,以便它返回一致的类型。我认识到这并不总是可用的选项。)


如果要消除通常是个好主意的DoubleOrString,请在结构中上移一个级别,并以这种方式解码age

guard let age = try
    (try? container.decode(Double.self, forKey: .age)) ??
    Double(container.decode(String.self, forKey: .age))
    else {
        throw DecodingError.typeMismatch(Double.self,
                                         DecodingError.Context(
                                            codingPath: decoder.codingPath,
                                            debugDescription: "Encoded payload conflicts with expected type, (Double or String)"))
}
self.age = age

这将尝试将其解码为双精度型,如果失败,则尝试转换字符串值。如果缺少密钥,这仍将有帮助地抛出正确的错误,而无需一堆do / catch块。

如果您有很多这样的东西,可以这样包装:

struct JSONDouble: Codable {
    let value: Double

    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        guard let value = try
            (try? container.decode(Double.self)) ??
            Double(container.decode(String.self))
            else {
                throw DecodingError.typeMismatch(Double.self,
                                                 DecodingError.Context(
                                                    codingPath: decoder.codingPath,
                                                    debugDescription: "Encoded payload conflicts with expected type, (Double or String)"))
        }
        self.value = value
    }
}

那么您的解码逻辑就是:

self.age = try container.decode(JSONDouble.self, forKey: .age).value

答案 1 :(得分:0)

您需要使用枚举吗?

如果不是这种情况,则可以轻松使用Generics来解决此问题。

使用泛型,您可以使用多种数据类型。