Ist es in Swift möglich, einen String in eine Aufzählung umzuwandeln?


92

Wenn ich eine Aufzählung mit den Fällen a, b, c, d habe, kann ich dann die Zeichenfolge "a" als Aufzählung verwenden?


3
Diese "Casts" werden als wörtliche Konvertierungen bezeichnet.
Vatsal Manot

Antworten:


133

Sicher. Aufzählungen können einen Rohwert haben. So zitieren Sie die Dokumente:

Rohwerte können Zeichenfolgen, Zeichen oder beliebige Ganzzahl- oder Gleitkommazahlentypen sein

- Auszug aus: Apple Inc. "The Swift Programming Language". iBooks. https://itun.es/us/jEUH0.l ,

Sie können also folgenden Code verwenden:

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

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

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

Hinweis: Sie müssen nicht nach jedem Fall = "eins" usw. schreiben. Die Standardwerte für Zeichenfolgen entsprechen den Fallnamen, sodass beim Aufrufen .rawValuenur eine Zeichenfolge zurückgegeben wird

BEARBEITEN

Wenn der Zeichenfolgenwert beispielsweise Leerzeichen enthalten soll, die als Teil eines Fallwerts nicht gültig sind, müssen Sie die Zeichenfolge explizit festlegen. So,

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

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

gibt

anEnum = "eins"

Wenn Sie case onejedoch "Wert eins" anzeigen möchten , müssen Sie die folgenden Zeichenfolgenwerte angeben:

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

Der Rohwert muss wörtlich konvertierbar sein. Sie können nicht irgendeinen HashableTyp verwenden.
Vatsal Manot

1
Ok ... Ich habe die Apple-Dokumente zitiert, in denen die Wertetypen aufgeführt sind, die als Enum-Rohwerte verwendet werden können. Strings, die Frage des OP, sind einer der unterstützten Typen.
Duncan C

1
Hmm, stell dir vor case one = "uno". Wie kann man "one"nun den Enum-Wert analysieren ? (kann keine Rohdaten verwenden, da sie für die Lokalisierung verwendet werden)
Agent_L

Vielleicht könnten Sie den rohen String bei der Initialisierung abhängig von der Lokalisierung initialisieren ... oder einfach jeweils eine andere Aufzählung für eine andere Lokalisierung haben. In jedem Fall besteht der gesamte Zweck einer Aufzählung darin, das zugrunde liegende Rohmaterial, dh die Lokalisierung, zu abstrahieren. Ein gutes Code-Design würde nirgendwo "uno" als Parameter übergeben, sondern sich auf StringEnum.one
SkyWalker

5
Sie müssen nicht = "one"nach jedem Fall schreiben usw. Die Standardwerte für Zeichenfolgen entsprechen den Fallnamen.
Emlai

37

Alles was Sie brauchen ist:

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

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

let 💩 = Foo(rawValue: "💩")
assert(💩 == nil)

Dies ist technisch nicht die richtige Antwort, da hierdurch der Rohwert überprüft wird. In dem hier angegebenen Beispiel ist kein Rohwert angegeben, daher wird er implizit mit dem Fallnamen abgeglichen. Wenn Sie jedoch eine Aufzählung mit einem Rohwert haben, wird dies unterbrochen.
Mark A. Donohoe

25

In Swift 4.2 kann das CaseIterable-Protokoll für eine Aufzählung mit rawValues ​​verwendet werden, die Zeichenfolge sollte jedoch mit den Aufzählungszeichen übereinstimmen:

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

Verwendung:

print(MyCode.withLabel("one")) // Optional(MyCode.one)
print(MyCode(rawValue: "uno"))  // Optional(MyCode.one)

2
Das ist eine großartige Antwort! Es geht tatsächlich um die Frage.
Matt Rundle

3
Dies ist die einzige Antwort, die tatsächlich funktioniert, wenn das OP gefragt wird, bei der es um Fallnamen und nicht um Rohwerte ging. Gute Antwort!
Mark A. Donohoe

1
Während dies funktioniert, ist es eine sehr dumme Sache zu tun. Bitte stützen Sie die Funktionalität nicht auf die Namen der Fälle im Code.
Sulthan

7
Was soll er sonst noch tun? Was ist, wenn er eine Aufzählung in eine Datenbank schreibt und diese dann zurücksetzen muss?
Joe

15

Bei einer Aufzählung mit Int-Typ können Sie dies folgendermaßen tun:

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

Und benutze:

let string = "Two"
if let item = MenuItem.enumFromString(string) {
    //in this case item = 1 
    //your code
} 

1
Es ist verrückt, dass Sie nicht einfach ähnliche Funktionen verwenden können, die in die Sprache integriert sind. Ich kann mir vorstellen, dass Sie Werte in JSON beispielsweise unter dem Namen enum speichern und sie dann beim Parsen zurückkonvertieren müssen. Das Schreiben einer enumFromStringMethode für jede Aufzählung, die Sie verwenden, scheint verrückt zu sein.
Peterdk

1
@Peterdk, bitte schlagen Sie die bestmögliche Alternative vor. Die Igor-Lösung hat eigentlich nur für mich funktioniert.
Hemang

@Hemang Es funktioniert in Ordnung, in Ordnung, aber eine bessere Lösung wäre die schnelle Unterstützung, um dies automatisch zu tun. Es ist verrückt, dies manuell für jede Aufzählung zu tun. Aber ja, das funktioniert.
Peterdk

@Peterdk, können Sie bitte eine separate Antwort für das gleiche hinzufügen? Es würde sicherlich allen hier draußen helfen.
Hemang

1
Es ist nicht verrückt, dass Swift es nicht von Haus aus unterstützt. Das Verrückte ist, dass die Funktionalität vom Namen eines Typs abhängt. Wenn sich der Wert ändert, müssen Sie alle Verwendungen umgestalten und umbenennen. Dies ist nicht der richtige Weg, um dies zu lösen.
Sulthan

2

Die Antwort von Duncan C erweitern

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

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

1

Für Int enum und ihre String-Darstellung deklariere ich enum wie folgt:

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

Verwendung:

order.orderState = OrderState.waiting.rawValue

let orderState = OrderState.init(rawValue: order.orderState)
let orderStateStr = orderState?.description ?? ""
print("orderStateStr = \(orderStateStr)")
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.