Dans Swift, est-il possible de convertir une chaîne en une énumération?

93

Si j'ai une énumération avec les cas a, b, c, d est-il possible pour moi de transtyper la chaîne "a" comme énumération?

rmaddy
la source
3
Ces «moulages» sont appelés conversions littérales.
Vatsal Manot

Réponses:

136

Sûr. Les énumérations peuvent avoir une valeur brute. Pour citer les documents:

Les valeurs brutes peuvent être des chaînes, des caractères ou tout type de nombre entier ou à virgule flottante

- Extrait de: Apple Inc. «The Swift Programming Language». iBooks. https://itun.es/us/jEUH0.l ,

Vous pouvez donc utiliser un code comme celui-ci:

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

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

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

Remarque: vous n'avez pas besoin d'écrire = "un" etc. après chaque cas. Les valeurs de chaîne par défaut sont les mêmes que les noms de cas, donc l'appel .rawValueretournera simplement une chaîne

ÉDITER

Si vous avez besoin que la valeur de chaîne contienne des éléments tels que des espaces qui ne sont pas valides dans le cadre d'une valeur de cas, vous devez définir explicitement la chaîne. Alors,

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

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

donne

anEnum = "un"

Mais si vous souhaitez case oneafficher la «valeur un», vous devrez fournir les valeurs de chaîne:

enum StringEnum: String 
{
  case one   = "value one"
  case two   = "value two"
  case three = "value three"
}
Duncan C
la source
La valeur brute doit être convertible littéralement. Vous ne pouvez pas utiliser n'importe quel Hashabletype.
Vatsal Manot
1
Ok ... J'ai cité la documentation Apple, qui répertorie les types de valeurs pouvant être utilisées comme valeurs brutes d'énumération. Les chaînes, la question du PO, sont l'un des types pris en charge.
Duncan C
1
Hum, imaginez case one = "uno". Maintenant, comment analyser la "one"valeur enum? (impossible d'utiliser les fichiers bruts, car ils sont utilisés pour la localisation)
Agent_L
Peut-être que vous pourriez initialiser la chaîne brute lors de l'initialisation en fonction de la localisation ... ou simplement avoir une énumération différente pour une localisation différente. Dans tous les cas, tout le but d'avoir une énumération est d'abstraire le brut sous-jacent, c'est-à-dire la localisation. Une bonne conception de code ne passerait pas "uno" comme paramètre n'importe où, mais s'appuierait sur StringEnum.one
SkyWalker
5
Vous n'avez pas besoin d'écrire = "one"etc. après chaque cas. Les valeurs de chaîne par défaut sont les mêmes que les noms de cas.
emlai
38

Tout ce dont tu as besoin c'est:

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

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

let 💩 = Foo(rawValue: "💩")
assert(💩 == nil)
emlai
la source
Ce n'est pas techniquement la bonne réponse car cela vérifie la valeur brute. Dans l'exemple donné ici, aucune valeur brute n'est spécifiée, elle correspond donc implicitement au nom du cas, mais si vous avez une énumération avec une valeur brute, cela se brise.
Mark A. Donohoe
26

Dans Swift 4.2, le protocole CaseIterable peut être utilisé pour une énumération avec rawValues, mais la chaîne doit correspondre aux étiquettes de cas 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 }
    }
}

usage:

print(MyCode.withLabel("one")) // Optional(MyCode.one)
print(MyCode(rawValue: "uno"))  // Optional(MyCode.one)
djruss70
la source
2
C'est une excellente réponse! Cela répond en fait à la question.
Matt Rundle
3
C'est la seule réponse qui fonctionne réellement comme l'OP l'a demandé, qui concernait les noms de cas et non les valeurs brutes. Bonne réponse!
Mark A. Donohoe
1
Bien que cela fonctionne, c'est une chose très stupide à faire. Veuillez ne pas baser la fonctionnalité sur les noms de cas dans le code.
Sulthan
7
Qu'est-ce qu'il est censé faire d'autre? Que faire s'il écrit une énumération dans une base de données et doit ensuite la renvoyer?
Joe le
15

Dans le cas d'une énumération avec le type Int, vous pouvez le faire:

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

Et utilise:

let string = "Two"
if let item = MenuItem.enumFromString(string) {
    //in this case item = 1 
    //your code
} 
Igor
la source
1
C'est fou que vous ne puissiez pas simplement utiliser des fonctionnalités similaires intégrées dans le langage. Je peux imaginer que vous stockez des valeurs dans JSON, par exemple par le nom d'énumération, puis lors de l'analyse, vous devez les reconvertir. Ecrire une enumFromStringméthode pour chaque énumération que vous utilisez semble fou.
Peterdk
1
@Peterdk, veuillez suggérer la meilleure alternative possible. La solution Igor a fonctionné pour moi.
Hemang
@Hemang Cela fonctionne bien, d'accord, mais une meilleure solution serait le support Swift pour le faire automatiquement. C'est fou de faire cela manuellement pour chaque énumération. Mais oui, cela fonctionne.
Peterdk
@Peterdk, pouvez-vous s'il vous plaît ajouter une réponse distincte pour la même chose? Cela aiderait sûrement tout le monde ici.
Hemang
1
Ce n'est pas fou que Swift ne le supporte pas de manière native. Le plus fou est que la fonctionnalité repose sur le nom d'un type. Lorsque la valeur change, vous devrez refactoriser et renommer toutes les utilisations. Ce n'est pas la bonne façon de résoudre ce problème.
Sulthan
2

Prolonger la réponse 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
la source
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
    }
}
monsieur boyfox
la source
1

Pour Int enum et leur représentation String, je déclare enum comme suit:

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

Usage:

order.orderState = OrderState.waiting.rawValue

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