J'ai essayé de résumer ce problème dans sa forme la plus simple avec ce qui suit.
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?(string: String) {
switch string.lowercaseString {
case "zero": self = .Zero
case "one": self = .One
case "two": self = .Two
default: return nil
}
}
}
et le code qui initialise l'énum dans un autre fichier, MyClass.Swift
:
internal class MyClass {
let foo = MyEnum(rawValue: 0) // Error
let fooStr = MyEnum(string: "zero")
func testFunc() {
let bar = MyEnum(rawValue: 1) // Error
let barStr = MyEnum(string: "one")
}
}
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?'
Per the Swift Guide de la langue :
Si vous définissez une énumération avec un type de valeur brute, l'énumération reçoit automatiquement un initialiseur prenant une valeur du type de la valeur brute (en tant que paramètre appelé
rawValue
) et renvoie soit un membre d'énumération, soitnil
.
L'initialiseur personnalisé pour MyEnum
a été défini dans une extension pour tester si l'initialiseur de valeur brute de l'énum était supprimé, en raison du cas suivant de Guide de la langue . Cependant, le même résultat d'erreur est obtenu.
Notez que si vous définissez un initialiseur personnalisé pour un type de valeur, vous n'aurez plus accès à l'initialiseur par défaut (ni à l'initialiseur memberwise, s'il s'agit d'une structure) pour ce type. [...]
Si vous voulez que votre type de valeur personnalisé soit initialisable avec l’initialiseur par défaut et l’initialiseur memberwise, ainsi que vos propres initialiseurs personnalisés, écrivez vos initialiseurs personnalisés dans une extension plutôt que dans le cadre de l’implémentation originale du type de valeur.
Déplacement de la définition d’énumération vers MyClass.Swift
résout l'erreur pour bar
mais pas pour foo
.
La suppression de l'initialiseur personnalisé résout les deux erreurs.
Une solution de contournement 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 un effet similaire à celui de marquer l'initialiseur de valeur brute private
.
init?(raw: Int) {
self.init(rawValue: raw)
}
Déclarant explicitement la conformité du protocole à RawRepresentable
dans MyClass.Swift
résout l'erreur en ligne pour bar
, mais génère une erreur de l'éditeur de liens concernant les symboles en double (car les énumérations de type de valeur brute sont implicitement conformes à RawRepresentable
).
extension MyEnum: RawRepresentable {}
Quelqu'un peut-il donner un 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?(rawString: String) {
// Check if string contains 'carrousel'
if rawString.rangeOfString("carrousel") != nil {
self.init(rawValue:"carrousel")
} else {
self.init(rawValue:rawString)
}
}
}
Dans votre cas, cela entraînerait l'extension suivante:
extension MyEnum {
init?(string: String) {
switch string.lowercaseString {
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, de cette manière, vous n'avez pas besoin d'ajouter d'autres cas lorsque vous ajoutez un nouveau type.
enum VehicleType: Int, CustomStringConvertible {
case car = 4
case moped = 2
case truck = 16
case unknown = -1
// MARK: - Helpers
public var description: String {
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: String) {
guard let type = VehicleType.all.first(where: { description == rawDescription })
else { return nil }
self = type
}
}
Oui, c'est un problème embêtant. Je travaille actuellement autour de celui-ci en utilisant une fonction globale qui agit comme une usine, c.-à-d.
func enumFromString(string:String) -> MyEnum? {
switch string {
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 my EnumSequence :
enum Word: Int, EnumSequenceElement, CustomStringConvertible {
case Apple, cat, fun
var description: String {
switch self {
case .Apple:
return "Apple"
case .cat:
return "Cat"
case .fun:
return "Fun"
}
}
}
let Words: [String: Word] = [
"A": .Apple,
"C": .cat,
"F": .fun
]
extension Word {
var letter: String? {
return Words.first(where: { (_, Word) -> Bool in
Word == self
})?.key
}
init?(_ letter: String) {
if let Word = Words[letter] {
self = Word
} else {
return nil
}
}
}
for Word in EnumSequence<Word>() {
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