Em Swift, é possível converter uma string em um enum?

93

Se eu tiver um enum com os casos a, b, c, d, é possível lançar a string "a" como o enum?

rmaddy
fonte
3
Essas 'conversões' são chamadas de conversões literais.
Vatsal Manot

Respostas:

135

Certo. Enums podem ter um valor bruto. Para citar os documentos:

Os valores brutos podem ser strings, caracteres ou qualquer um dos tipos de número inteiro ou de ponto flutuante

- Trecho de: Apple Inc. “The Swift Programming Language”. iBooks. https://itun.es/us/jEUH0.l ,

Portanto, você pode usar um código como este:

enum StringEnum: String 
{
  case one = "one"
  case two = "two"
  case three = "three"
}

let anEnum = StringEnum(rawValue: "one")!

print("anEnum = \"\(anEnum.rawValue)\"")

Nota: Você não precisa escrever = "um" etc. após cada caso. Os valores de string padrão são iguais aos nomes de caso, então a chamada .rawValueirá retornar apenas uma string

EDITAR

Se você precisa que o valor da string contenha coisas como espaços que não são válidos como parte de um valor case, então você precisa definir explicitamente a string. Assim,

enum StringEnum: String 
{
  case one
  case two
  case three
}

let anEnum = StringEnum.one
print("anEnum = \"\(anEnum)\"")

anEnum = "um"

Mas se quiser case oneexibir o "valor um", você precisará fornecer os valores da string:

enum StringEnum: String 
{
  case one   = "value one"
  case two   = "value two"
  case three = "value three"
}
Duncan C
fonte
O valor bruto deve ser literalmente conversível. Você não pode usar qualquer Hashabletipo.
Vatsal Manot
1
Ok ... citei os documentos da Apple, que listam os tipos de valores que podem ser usados ​​como valores brutos enum. Strings, a pergunta do OP, são um dos tipos suportados.
Duncan C
1
Hmm, imagine case one = "uno". Agora, como analisar "one"para valor enum? (não é possível usar raws, pois são usados ​​para localização)
Agent_L
Talvez você possa inicializar a String bruta na inicialização, dependendo da localização ... ou simplesmente ter enum diferente cada para uma localização diferente. Em qualquer caso, todo o propósito de ter um enum é abstrair o bruto subjacente, ou seja, a localização. Um bom design de código não seria passar "uno" como parâmetro em qualquer lugar, mas confiar em StringEnum.one
SkyWalker
5
Você não precisa escrever = "one"etc. após cada caso. Os valores de string padrão são iguais aos nomes de caso.
emlai
37

Tudo o que você precisa é:

enum Foo: String {
   case a, b, c, d
}

let a = Foo(rawValue: "a")
assert(a == Foo.a)

let 💩 = Foo(rawValue: "💩")
assert(💩 == nil)
emlai
fonte
Esta não é tecnicamente a resposta certa, pois verifica o valor bruto. No exemplo aqui fornecido, não há valor bruto especificado, portanto, é implicitamente correspondido ao nome do caso, mas se você tiver um enum com um valor bruto, isso será interrompido.
Mark A. Donohoe
26

No Swift 4.2, o protocolo CaseIterable pode ser usado para um enum com rawValues, mas a string deve corresponder aos rótulos case enum:

enum MyCode : String, CaseIterable {

    case one   = "uno"
    case two   = "dos"
    case three = "tres"

    static func withLabel(_ label: String) -> MyCode? {
        return self.allCases.first{ "\($0)" == label }
    }
}

uso:

print(MyCode.withLabel("one")) // Optional(MyCode.one)
print(MyCode(rawValue: "uno"))  // Optional(MyCode.one)
Djruss70
fonte
2
Esta é uma ótima resposta! Na verdade, trata da questão.
Matt Rundle
3
Esta é a única resposta que realmente funciona como o OP pediu, que era sobre nomes de caso, não valores brutos. Boa resposta!
Mark A. Donohoe
1
Enquanto isso funciona, é uma coisa muito boba de se fazer. Não baseie a funcionalidade em nomes de casos no código.
Sulthan
7
O que mais ele deve fazer? E se ele estiver gravando um enum em um banco de dados e precisar convertê-lo de volta?
Joe
15

No caso de um enum com tipo Int, você pode fazer isso:

enum MenuItem: Int {
    case One = 0, Two, Three, Four, Five //... as much as needs

    static func enumFromString(string:String) -> MenuItem? {
        var i = 0
        while let item = MenuItem(rawValue: i) {
            if String(item) == string { return item }
            i += 1
        }
        return nil
    }
}

E use:

let string = "Two"
if let item = MenuItem.enumFromString(string) {
    //in this case item = 1 
    //your code
} 
Igor
fonte
1
É uma loucura que você não pode simplesmente usar uma funcionalidade semelhante embutida na linguagem. Posso imaginar que você armazene valores em JSON, por exemplo, pelo nome de enum e, em seguida, na análise, precise convertê-los de volta. Escrever um enumFromStringmétodo para cada enum que você usa parece loucura.
Peterdk
1
@Peterdk, por favor sugira a melhor alternativa possível. A solução Igor realmente funcionou para mim.
Hemang
@Hemang Funciona bem, certo, mas uma solução melhor seria o suporte do Swift para fazer isso automaticamente. É uma loucura fazer isso manualmente para cada enum. Mas sim, isso funciona.
Peterdk
@Peterdk, você poderia adicionar uma resposta separada para o mesmo? Certamente ajudaria a todos aqui.
Hemang
1
Não é loucura que o Swift não o suporte nativamente. A loucura é que a funcionalidade depende do nome de um tipo. Quando o valor mudar, você terá que refatorar e renomear todos os usos. Esta não é a maneira correta de resolver isso.
Sulthan
2

Ampliando a resposta de Duncan C

extension StringEnum: StringLiteralConvertible {

    init(stringLiteral value: String){
        self.init(rawValue: value)!
    }

    init(extendedGraphemeClusterLiteral value: String) {
        self.init(stringLiteral: value)
    }

    init(unicodeScalarLiteral value: String) {
        self.init(stringLiteral: value)
    }
}
gujci
fonte
2

Swift 4.2:

public enum PaymentPlatform: String, CaseIterable {
    case visa = "Visa card"
    case masterCard = "Master card"
    case cod = "Cod"

    var nameEnum: String {
        return Mirror(reflecting: self).children.first?.label ?? String(describing: self)
    }

    func byName(name: String) -> PaymentPlatform {
        return PaymentPlatform.allCases.first(where: {$0.nameEnum.elementsEqual(name)}) ?? .cod
    }
}
sr.boyfox
fonte
1

Para Int enum e sua representação em String, declaro enum da seguinte forma:

enum OrderState: Int16, CustomStringConvertible {

    case waiting = 1
    case inKitchen = 2
    case ready = 3

    var description: String {
        switch self {
        case .waiting:
            return "Waiting"
        case .inKitchen:
            return "InKitchen"
        case .ready:
            return "Ready"
        }
    }

    static func initialize(stringValue: String)-> OrderState? {
        switch stringValue {
        case OrderState.waiting.description:
            return OrderState.waiting
        case OrderState.inKitchen.description:
            return OrderState.inKitchen
        case OrderState.ready.description:
            return OrderState.ready

        default:
            return nil
        }
    }
}

Uso:

order.orderState = OrderState.waiting.rawValue

let orderState = OrderState.init(rawValue: order.orderState)
let orderStateStr = orderState?.description ?? ""
print("orderStateStr = \(orderStateStr)")
Ammar Mujeeb
fonte