Swift enum avec initialiseur personnalisé perd l’initialiseur rawValue

J’ai essayé de résumer ce problème dans sa forme la plus simple avec ce qui suit.

Installer

Xcode Version 6.1.1 (6A2008a)

Une énumération définie dans MyEnum.swift :

 internal enum MyEnum: Int { case Zero = 0, One, Two } extension MyEnum { init?(ssortingng: Ssortingng) { switch ssortingng.lowercaseSsortingng { case "zero": self = .Zero case "one": self = .One case "two": self = .Two default: return nil } } } 

et code qui initialise l’énumération dans un autre fichier, MyClass.swift :

 internal class MyClass { let foo = MyEnum(rawValue: 0) // Error let fooStr = MyEnum(ssortingng: "zero") func testFunc() { let bar = MyEnum(rawValue: 1) // Error let barStr = MyEnum(ssortingng: "one") } } 

Erreur

Xcode me donne l’erreur suivante lors de la tentative d’initialisation de MyEnum avec son initialiseur de valeur brute:

 Cannot convert the expression's type '(rawValue: IntegerLiteralConvertible)' to type 'MyEnum?' 

Remarques

  1. Selon le guide linguistique Swift :

    Si vous définissez une énumération avec un type de valeur brute, l’énumération reçoit automatiquement un initialiseur qui prend une valeur du type de la valeur brute (en tant que paramètre appelé rawValue ) et retourne un membre d’énumération ou nil .

  2. L’initialiseur personnalisé pour MyEnum été défini dans une extension pour tester si l’initialiseur de valeur brute de l’énum a été supprimé en raison du cas suivant du Guide de langue . Cependant, il obtient le même résultat d’erreur.

    Notez que si vous définissez un initialiseur personnalisé pour un type de valeur, vous n’aurez plus access à l’initialiseur par défaut (ou à l’initialiseur par membre, s’il s’agit d’une structure) pour ce type. […]
    Si vous souhaitez que votre type de valeur personnalisé puisse être initialisé avec l’initialiseur par défaut et l’initialiseur selon les membres, ainsi qu’avec vos propres initialiseurs personnalisés, écrivez vos initialiseurs personnalisés dans une extension plutôt que dans l’implémentation d’origine du type de valeur.

  3. Déplacer la définition enum dans MyClass.swift résout l’erreur pour la bar mais pas pour foo .

  4. La suppression de l’initialiseur personnalisé résout les deux erreurs.

  5. Une solution consiste à inclure la fonction suivante dans la définition enum et à l’utiliser à la place de l’initialiseur de valeur brute fourni. Il semble donc que l’ajout d’un initialiseur personnalisé ait le même effet que le marquage private initialiseur à valeur brute.

     init?(raw: Int) { self.init(rawValue: raw) } 
  6. La déclaration explicite de la conformité du protocole à RawRepresentable dans MyClass.swift résout l’erreur en ligne pour bar , mais entraîne une erreur de l’éditeur de liens à propos des symboles en double (car les RawRepresentable type valeur brute implicitement conformes à RawRepresentable ).

     extension MyEnum: RawRepresentable {} 

Quelqu’un peut-il donner un petit aperçu de ce qui se passe ici? Pourquoi l’initialiseur de valeur brute n’est-il pas accessible?

Ce bogue est résolu dans Xcode 7 et Swift 2

 extension TemplateSlotType { init?(rawSsortingng: Ssortingng) { // Check if ssortingng contains 'carrousel' if rawSsortingng.rangeOfSsortingng("carrousel") != nil { self.init(rawValue:"carrousel") } else { self.init(rawValue:rawSsortingng) } } } 

Dans votre cas, cela entraînerait l’extension suivante:

 extension MyEnum { init?(ssortingng: Ssortingng) { switch ssortingng.lowercaseSsortingng { case "zero": self.init(rawValue:0) case "one": self.init(rawValue:1) case "two": self.init(rawValue:2) default: return nil } } } 

Vous pouvez même rendre le code plus simple et utile sans switch cas, vous n’avez donc pas besoin d’append plus de cas lorsque vous ajoutez un nouveau type.

 enum VehicleType: Int, CustomSsortingngConvertible { case car = 4 case moped = 2 case truck = 16 case unknown = -1 // MARK: - Helpers public var description: Ssortingng { switch self { case .car: return "Car" case .truck: return "Truck" case .moped: return "Moped" case .unknown: return "unknown" } } static let all: [VehicleType] = [car, moped, truck] init?(rawDescription: Ssortingng) { guard let type = VehicleType.all.first(where: { description == rawDescription }) else { return nil } self = type } } 

Oui, c’est un problème ennuyeux. Je suis en train de le contourner en utilisant une fonction de scope globale qui agit comme une usine, c.-à-d.

 func enumFromSsortingng(ssortingng:Ssortingng) -> MyEnum? { switch ssortingng { case "One" : MyEnum(rawValue:1) case "Two" : MyEnum(rawValue:2) case "Three" : MyEnum(rawValue:3) default : return nil } } 

Cela fonctionne pour Swift 4 sur Xcode 9.2 avec mon EnumSequence :

 enum Word: Int, EnumSequenceElement, CustomSsortingngConvertible { case apple, cat, fun var description: Ssortingng { switch self { case .apple: return "Apple" case .cat: return "Cat" case .fun: return "Fun" } } } let Words: [Ssortingng: Word] = [ "A": .apple, "C": .cat, "F": .fun ] extension Word { var letter: Ssortingng? { return Words.first(where: { (_, word) -> Bool in word == self })?.key } init?(_ letter: Ssortingng) { if let word = Words[letter] { self = word } else { return nil } } } for word in EnumSequence() { if let letter = word.letter, let lhs = Word(letter), let rhs = Word(letter), lhs == rhs { print("\(letter) for \(word)") } } 

Sortie

 A for Apple C for Cat F for Fun 

Ajoutez ceci à votre code:

 extension MyEnum { init?(rawValue: Int) { switch rawValue { case 0: self = .Zero case 1: self = .One case 2: self = .Two default: return nil } } }