简体   繁体   English

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

[英]Updating Labels with multiple types in Swift 4

I have an API that will sometimes return a specific key in the JSON as an Int and other times it will return that same key as a String, I solve this issue by creating an enum IntOrString. 我有一个API,有时会将JSON中的特定键作为Int返回,有时它将返回与String相同的键,我通过创建枚举IntOrString解决了这个问题。 Now the issue is when I call the API to update the label of these specific key the type is wrong. 现在的问题是,当我调用API来更新这些特定键的标签时,类型是错误的。

Then I'm getting the error cannot convert type Double to type DoubleOrString 然后我得到的错误无法将Double类型转换为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)
        }
    }
}

Lower down this is where I'm updating my label 降低这是我更新标签的地方

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

First, I think you should decide if you really want to keep this as a DoubleOrString throughout the program. 首先,我认为您应该决定是否真的希望在整个程序中将其保留为DoubleOrString Do you need to keep track of the distinction? 你需要跟踪区别吗? Or could you modify your decoder to always make this a double? 或者你可以修改你的解码器,以使其成为双倍? Your internal data model doesn't have to recreate every mistake in the JSON. 您的内部数据模型不必重新创建JSON中的每个错误。

If you do want to maintain the enum, then I think what you're looking for is something along these lines: 如果你想保持枚举,那么我认为你正在寻找的东西是这样的:

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)"

(Of course the preferred solution here is to correct the JSON so that it returns consistent types. I recognize that this is not always an available option.) (当然,这里的首选解决方案是更正JSON,以便返回一致的类型。我认识到这并不总是一个可用的选项。)


If you want to eliminate DoubleOrString, which is usually a good idea, then move up a level in your structure, and decode age this way: 如果你想消除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

This tries to decode it as a double, and if that fails, it tries converting a string value. 这会尝试将其解码为double,如果失败,则会尝试转换字符串值。 This will still helpfully throw the right errors if the key is missing, without needing a bunch of do/catch blocks. 如果缺少密钥,这仍将有助于抛出正确的错误,而不需要一堆do / catch块。

If you have a lot of this, you could wrap it up this way: 如果你有很多这个,你可以这样包装:

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
    }
}

Then your decoding logic is just: 那你的解码逻辑就是:

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

Do you need to use an enum? 你需要使用枚举吗?

If that is not the case you can easily use Generics for this issue. 如果不是这种情况,您可以轻松地使用泛型来解决此问题。

With generics you can use multiple datatypes. 使用泛型,您可以使用多种数据类型。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM