Comment puis-je créer une énumération décodable dans swift 4?

enum PostType: Decodable { init(from decoder: Decoder) throws { // What do i put here? } case Image enum CodingKeys: Ssortingng, CodingKey { case image } } 

Qu’est-ce que je mets pour compléter ceci? Aussi, disons que j’ai changé le case à ceci:

 case image(value: Int) 

Comment puis-je le rendre conforme à Decodable?

EDit Voici mon code complet (qui ne fonctionne pas)

 let jsonData = """ { "count": 4 } """.data(using: .utf8)! do { let decoder = JSONDecoder() let response = try decoder.decode(PostType.self, from: jsonData) print(response) } catch { print(error) } } } enum PostType: Int, Codable { case count = 4 } 

Final Edit Aussi, comment va-t-il gérer un tel enum?

 enum PostType: Decodable { case count(number: Int) } 

C’est assez simple, utilisez simplement les valeurs brutes Ssortingng ou Int qui sont implicitement atsortingbuées.

 enum PostType: Int, Codable { case image, blob } 

image est codée à 0 et blob à 1

Ou

 enum PostType: Ssortingng, Codable { case image, blob } 

image est encodée en "image" et blob en "blob"


Voici un exemple simple d’utilisation:

 enum PostType : Int, Codable { case count = 4 } struct Post : Codable { var type : PostType } let jsonSsortingng = "{\"type\": 4}" let jsonData = jsonSsortingng.data(using: .utf8)! do { let decoded = try JSONDecoder().decode(Post.self, from: jsonData) print("decoded:", decoded.type) } catch { print(error) } 

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

Cette réponse est similaire à celle de @Howard Lovatt mais évite de créer une structure PostTypeCodableForm et utilise à la KeyedEncodingContainer type KeyedEncodingContainer fourni par Apple en tant que propriété sur Encoder and Decoder , ce qui réduit le mode passe-partout.

 enum PostType: Codable { case count(number: Int) case title(Ssortingng) } extension PostType { private enum CodingKeys: Ssortingng, CodingKey { case count case title } enum PostTypeCodingError: Error { case decoding(Ssortingng) } 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(Ssortingng.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 = Ssortingng.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 = Ssortingng.init(data: titleData, encoding: .utf8)! print(titleJSON) // { // "title": "Hello, World!" // } let decodedTitle = try decoder.decode(PostType.self, from: titleData) 

Swift .dataCorrupted une erreur .dataCorrupted s’il rencontre une valeur enum inconnue. Si vos données proviennent d’un serveur, elles peuvent vous envoyer une valeur d’énumération inconnue à tout moment (côté serveur de bogue, nouveau type ajouté dans une version d’API et vous souhaitez que les versions précédentes de votre application gèrent correctement le dossier, etc.) vous feriez mieux de vous préparer et de coder “style défensif” pour décoder vos enums en toute sécurité.

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

  enum MediaType: Decodable { case audio case multipleChoice case other // case other(Ssortingng) -> we could also paramesortingse the enum like that init(from decoder: Decoder) throws { let label = try decoder.singleValueContainer().decode(Ssortingng.self) switch label { case "AUDIO": self = .audio case "MULTIPLE_CHOICES": self = .multipleChoice default: self = .other // default: self = .other(label) } } } 

Et comment l’utiliser dans une structure englobante:

  struct Question { [...] let type: MediaType enum CodingKeys: Ssortingng, 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) } } 

Vous pouvez faire ce que vous voulez, mais c’est un peu compliqué 🙁

 import Foundation enum PostType: Codable { case count(number: Int) case comment(text: Ssortingng) init(from decoder: Decoder) throws { self = try PostTypeCodableForm(from: decoder).enumForm() } func encode(to encoder: Encoder) throws { try PostTypeCodableForm(self).encode(to: encoder) } } struct PostTypeCodableForm: Codable { // All fields must be optional! var countNumber: Int? var commentText: Ssortingng? init(_ enumForm: PostType) { switch enumForm { case .count(let number): countNumber = number case .comment(let text): commentText = text } } func enumForm() throws -> PostType { if let number = countNumber { guard commentText == nil else { throw DecodeError.moreThanOneEnumCase } return .count(number: number) } if let text = commentText { guard countNumber == nil else { throw DecodeError.moreThanOneEnumCase } return .comment(text: text) } throw DecodeError.noRecognizedContent } enum DecodeError: Error { case noRecognizedContent case moreThanOneEnumCase } } let test = PostType.count(number: 3) let data = try JSONEncoder().encode(test) let ssortingng = Ssortingng(data: data, encoding: .utf8)! print(ssortingng) // {"countNumber":3} let result = try JSONDecoder().decode(PostType.self, from: data) print(result) // count(3) 

Pour étendre la réponse de @ Toka, vous pouvez également append une valeur représentable brute à l’énumération et utiliser le constructeur facultatif par défaut pour créer l’énumération sans switch :

 enum MediaType: Ssortingng, Decodable { case audio = "AUDIO" case multipleChoice = "MULTIPLE_CHOICES" case other init(from decoder: Decoder) throws { let label = try decoder.singleValueContainer().decode(Ssortingng.self) self = MediaType(rawValue: label) ?? .other } } 

Il peut être étendu en utilisant un protocole personnalisé qui permet de restructurer 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: Ssortingng, EnumDecodable { static let defaultDecoderValue: MediaType = .other case audio = "AUDIO" case multipleChoices = "MULTIPLE_CHOICES" case other } 

Il peut également être facilement étendu pour générer une erreur si une valeur enum non valide a été spécifiée, plutôt que de définir une valeur par défaut. Gist avec ce changement est disponible ici: https://gist.github.com/stephanecopin/4283175fabf6f0cdaf87fef2a00c8128 .
Le code a été compilé et testé avec Swift 4.1 / Xcode 9.3.

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

 public init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) guard let key = values.allKeys.first else { throw err("No valid keys in: \(values)") } func dec() throws -> T { return try values.decode(T.self, forKey: key) } switch key { case .count: self = try .count(dec()) case .title: self = try .title(dec()) } } func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) switch self { case .count(let x): try container.encode(x, forKey: .count) case .title(let x): try container.encode(x, forKey: .title) } } 

Cela permet au compilateur de vérifier de manière exhaustive les cas et ne supprime pas non plus le message d’erreur dans le cas où la valeur codée ne correspond pas à la valeur attendue de la clé.

En fait, les réponses ci-dessus sont excellentes mais n’ont pas suffi à ce dont nous avions besoin aujourd’hui. Nous développons une application pendant que notre backend est étendu au fil du temps. Cela signifie que nos cas d’énumération s’étendront aussi. Nous avons donc besoin d’une stratégie de décodage enum qui garantisse que les anciennes applications peuvent décoder les listes de énumérations contenant des cas inconnus. Sans ce décodage de l’object entier qui contient la liste enum échoue simplement.

Ce que j’ai fait est assez simple:

 public enum Direction: Ssortingng, Decodable { case north, south, east, west } public struct DirectionList { public let directions: [Direction] } extension DirectionList: Decodable { public init(from decoder: Decoder) throws { var container = try decoder.unkeyedContainer() var directions: [Direction] = [] while !container.isAtEnd { // Here we just decode the ssortingng from the JSON let value = try container.decode(Ssortingng.self) guard let direction = Direction(rawValue: value) else { // Unknown enum value found - ignore continue } // Add all known enum cases to the list of directions directions.append(direction) } self.directions = directions } } 

Bonus: Cacher l’implémentation> Faites-en une collection

Cacher les détails d’implémentation est toujours une bonne idée. Pour cela, vous aurez besoin d’un peu plus de code. L’astuce consiste à adapter DirectionsList à Collection et à rendre privé votre tableau de list interne:

 struct DirectionList { typealias ArrayType = [Direction] private let directions: ArrayType } extension DirectionList: Collection { typealias Index = ArrayType.Index typealias Element = ArrayType.Element // The upper and lower bounds of the collection, used in iterations var startIndex: Index { return directions.startIndex } var endIndex: Index { return directions.endIndex } // Required subscript, based on a dictionary index subscript(index: Index) -> Element { get { return directions[index] } } // Method that returns the next index when iterating func index(after i: Index) -> Index { return directions.index(after: i) } } 

Vous pouvez en savoir plus sur la conformité aux collections personnalisées dans cet article de John Sundell: https://medium.com/@johnsundell/creating-custom-collections-in-swift-a344e25d0bb0