5 votes

Mise à jour des étiquettes avec plusieurs types en Swift 4

J'ai une API qui renvoie parfois une clé spécifique dans le JSON sous la forme d'un Int et d'autres fois, elle renvoie cette même clé sous la forme d'une String, je résous ce problème en créant un enum IntOrString. Le problème est que lorsque j'appelle l'API pour mettre à jour l'étiquette de ces clés spécifiques, le type est incorrect.

Ensuite, j'obtiens l'erreur "cannot convert type Double to 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)
        }
    }
}

Plus bas, c'est là que je mets à jour mon étiquette.

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

3voto

Rob Napier Points 92148

Tout d'abord, je pense que vous devriez décider si vous voulez vraiment garder ceci comme un DoubleOrString tout au long du programme. Avez-vous besoin de garder la trace de la distinction ? Ou bien pouvez-vous modifier votre décodeur pour qu'il s'agisse toujours d'un double ? Votre modèle de données interne n'a pas besoin de recréer chaque erreur dans le JSON.

Si vous souhaitez conserver l'enum, je pense que ce que vous recherchez est quelque chose de ce type :

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

(Bien sûr, la solution préférée ici est de corriger le JSON pour qu'il renvoie des types cohérents. Je reconnais que ce n'est pas toujours une option disponible).


Si vous voulez éliminer DoubleOrString, ce qui est généralement une bonne idée, remontez d'un niveau dans votre structure, et décodez age de cette façon :

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

Elle essaie de la décoder en tant que double, et si cela échoue, elle essaie de convertir une valeur de chaîne de caractères. Cela permettra toujours d'envoyer les bonnes erreurs si la clé est manquante, sans avoir besoin d'un tas de blocs do/catch.

Si vous en avez beaucoup, vous pouvez l'emballer de cette façon :

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

Alors votre logique de décodage est juste :

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

0voto

FloWy Points 503

Avez-vous besoin d'utiliser un enum ?

Si ce n'est pas le cas, vous pouvez facilement utiliser Génériques pour ce numéro.

Avec les génériques, vous pouvez utiliser plusieurs types de données.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X