208 votes

Comment faire un enum Decodable dans swift 4 ?

<pre><code></code><p>Que dois-je mettre pour compléter ce ? <code></code> pour cela :</p><pre><code></code></pre><p>Comment puis-je faire cela conforme aux Decodable ?</p><p><strong>Modifier</strong> Voici mon code complet (ce qui ne fonctionne pas)</p><pre><code></code></pre><p><strong>Edit final</strong> Aussi, comment il gérera un enum comme ça ?</p><pre><code></code></pre></pre>

345voto

vadian Points 29149

Il est assez facile, il suffit d’utiliser ou des valeurs brutes qui leur sont assignées implicitement.

est encodée en et à

Ou

est encodée en et à


Il s’agit d’exemple simple comment l’utiliser :

147voto

proxpero Points 1021

Comment faire des énumérations associés à des types conformes à l' Codable

Cette réponse est similaire à @Howard Lovatt mais évite de créer un PostTypeCodableForm struct et utilise à la place de l' KeyedEncodingContainer type fourni par Apple comme une propriété sur Encoder et Decoder, ce qui réduit réutilisable.

enum PostType: Codable {
    case count(number: Int)
    case title(String)
}

extension PostType {

    private enum CodingKeys: String, CodingKey {
        case count
        case title
    }

    enum PostTypeCodingError: Error {
        case decoding(String)
    }

    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        if let value = try? values.decode(Int.self, forKey: .count) {
            self = .count(number: value)
            return
        }
        if let value = try? values.decode(String.self, forKey: .title) {
            self = .title(value)
            return
        }
        throw PostTypeCodingError.decoding("Whoops! \(dump(values))")
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        switch self {
        case .count(let number):
            try container.encode(number, forKey: .count)
        case .title(let value):
            try container.encode(value, forKey: .title)
        }
    }
}

Ce code fonctionne pour moi sur Xcode 9b3.

import Foundation // Needed for JSONEncoder/JSONDecoder

let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
let decoder = JSONDecoder()

let count = PostType.count(number: 42)
let countData = try encoder.encode(count)
let countJSON = String.init(data: countData, encoding: .utf8)!
print(countJSON)
//    {
//      "count" : 42
//    }

let decodedCount = try decoder.decode(PostType.self, from: countData)

let title = PostType.title("Hello, World!")
let titleData = try encoder.encode(title)
let titleJSON = String.init(data: titleData, encoding: .utf8)!
print(titleJSON)
//    {
//        "title": "Hello, World!"
//    }
let decodedTitle = try decoder.decode(PostType.self, from: titleData)

51voto

Toka Points 313

Swift serait jeter un .dataCorrupted d'erreur s'il rencontre des inconnus valeur d'enum. Si vos données sont à venir à partir d'un serveur, il peut vous envoyer un inconnu de la valeur d'enum à tout moment (bug côté serveur, ce nouveau type de ajouté dans une version de l'API et vous voulez que les précédentes versions de votre application pour gérer le cas gracieusement, etc), vous feriez mieux d'être préparé, et le code "style défensif" en toute sécurité décoder vos énumérations.

Voici un exemple sur la façon de le faire, avec ou sans valeur

    enum MediaType: Decodable {
       case audio
       case multipleChoice
       case other
       // case other(String) -> we could also parametrise the enum like that

       init(from decoder: Decoder) throws {
          let label = try decoder.singleValueContainer().decode(String.self)
          switch label {
             case "AUDIO": self = .audio
             case "MULTIPLE_CHOICES": self = .multipleChoice
             default: self = .other
             // default: self = .other(label)
          }
       }
    }

Et comment l'utiliser dans un enfermant struct:

    struct Question {
       [...]
       let type: MediaType

       enum CodingKeys: String, CodingKey {
          [...]
          case type = "type"
       }


   extension Question: Decodable {
      init(from decoder: Decoder) throws {
         let container = try decoder.container(keyedBy: CodingKeys.self)
         [...]
         type = try container.decode(MediaType.self, forKey: .type)
      }
   }

42voto

Stéphane Copin Points 660

Pour prolonger sur @Toka réponse, vous pouvez aussi ajouter un raw représentable valeur de l'enum, et utiliser la valeur par défaut option constructeur pour construire l'enum sans switch:

enum MediaType: String, Decodable {
  case audio = "AUDIO"
  case multipleChoice = "MULTIPLE_CHOICES"
  case other

  init(from decoder: Decoder) throws {
    let label = try decoder.singleValueContainer().decode(String.self)
    self = MediaType(rawValue: label) ?? .other
  }
}

Il peut être étendu à l'aide d'un protocole personnalisé qui permet de refactoriser le constructeur:

protocol EnumDecodable: RawRepresentable, Decodable {
  static var defaultDecoderValue: Self { get }
}

extension EnumDecodable where RawValue: Decodable {
  init(from decoder: Decoder) throws {
    let value = try decoder.singleValueContainer().decode(RawValue.self)
    self = Self(rawValue: value) ?? Self.defaultDecoderValue
  }
}

enum MediaType: String, EnumDecodable {
  static let defaultDecoderValue: MediaType = .other

  case audio = "AUDIO"
  case multipleChoices = "MULTIPLE_CHOICES"
  case other
}

Il peut également être facilement étendu pour lancer une erreur si une défaillance de valeur d'énumération a été spécifié, plutôt que de faire défaut sur une valeur. Gist avec ce changement est disponible ici: https://gist.github.com/stephanecopin/4283175fabf6f0cdaf87fef2a00c8128.
Le code a été compilé et testé à l'aide de Swift 4.1/Xcode 9.3.

7voto

mprudhom Points 2093

Une variante de la réponse de @proxpero qui est terser serait de formuler le décodeur comme :

Cela permet au compilateur de vérifier limitativement les cas et également ne supprimer le message d’erreur pour le cas où la valeur codée ne correspond pas à la valeur de clé attendue.

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