type the switch raw protocol not have generic enum does cannot swift enums

swift - the - La enumeración rápida con inicializador personalizado pierde el inicializador rawValue



swift4 enum (6)

Agregue esto a su código:

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

He tratado de reducir este problema a su forma más simple con lo siguiente.

Preparar

Xcode versión 6.1.1 (6A2008a)

Una enumeración definida en 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 } } }

y código que inicializa la enumeración en otro archivo, 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") } }

Error

Xcode me da el siguiente error cuando intento inicializar MyEnum con su inicializador de valor bruto:

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

Notas

  1. Según la guía de idiomas Swift :

    Si define una enumeración con un tipo de valor sin procesar, la enumeración recibe automáticamente un inicializador que toma un valor del tipo del valor sin procesar (como un parámetro llamado rawValue ) y devuelve un miembro de enumeración o nil .

  2. El inicializador personalizado para MyEnum se definió en una extensión para probar si el inicializador de valor sin procesar del enum se estaba eliminando debido al siguiente caso de la Guía de idiomas . Sin embargo, logra el mismo resultado de error.

    Tenga en cuenta que si define un inicializador personalizado para un tipo de valor, ya no tendrá acceso al inicializador predeterminado (o al inicializador miembro, si es una estructura) para ese tipo. [...]
    Si desea que su tipo de valor personalizado se pueda inicializar con el inicializador predeterminado y el inicializador miembro, y también con sus propios inicializadores personalizados, escriba sus inicializadores personalizados en una extensión en lugar de como parte de la implementación original del tipo de valor.

  3. Mover la definición de enumeración a MyClass.swift resuelve el error para bar pero no para foo .

  4. Eliminar el inicializador personalizado resuelve ambos errores.

  5. Una solución alternativa es incluir la siguiente función en la definición de enumeración y usarla en lugar del inicializador de valor bruto proporcionado. Por lo tanto, parece que agregar un inicializador personalizado tiene un efecto similar a marcar el inicializador de valor sin procesar como private .

    init?(raw: Int) { self.init(rawValue: raw) }

  6. La declaración explícita de conformidad del protocolo con RawRepresentable en MyClass.swift resuelve el error en línea para la bar , pero da como resultado un error de enlace sobre símbolos duplicados (porque las enumeraciones de tipo de valor sin procesar se ajustan implícitamente a RawRepresentable ).

    extension MyEnum: RawRepresentable {}

¿Alguien puede proporcionar un poco más de información sobre lo que está sucediendo aquí? ¿Por qué no se puede acceder al inicializador de valor sin procesar?


Este error se resuelve en Xcode 7 y Swift 2


Esto funciona para Swift 4 en Xcode 9.2 junto con mi 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)") } }

Salida

A for Apple C for Cat F for Fun


Incluso puede hacer que el código sea más simple y útil sin switch casos, de esta manera no necesita agregar más casos cuando agrega un nuevo tipo.

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


Sí, este es un problema molesto. Actualmente estoy trabajando en esto usando una función de alcance global que actúa como una fábrica, es decir

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


extension TemplateSlotType { init?(rawString: String) { // Check if string contains ''carrousel'' if rawString.rangeOfString("carrousel") != nil { self.init(rawValue:"carrousel") } else { self.init(rawValue:rawString) } } }

En su caso, esto resultaría en la siguiente extensión:

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