web-dev-qa-db-fra.com

Comment obtenir le nom de la valeur d'énumération dans Swift?

Si j'ai une énumération avec des valeurs brutes Integer:

enum City: Int {
  case Melbourne = 1, Chelyabinsk, Bursa
}

let city = City.Melbourne

Comment convertir une valeur city en une chaîne Melbourne? Ce type d'introspection de noms de types est-il disponible dans la langue?

Quelque chose comme (ce code ne fonctionnera pas):

println("Your city is \(city.magicFunction)")
> Your city is Melbourne
143
Evgenii

À partir de Xcode 7 beta 5 (Swift version 2), vous pouvez désormais imprimer les noms de types et les majuscules par défaut à l'aide de print(_:), ou les convertir en String à l'aide de String's init(_:) initialiseur ou syntaxe d'interpolation de chaîne. Donc pour votre exemple:

enum City: Int {
    case Melbourne = 1, Chelyabinsk, Bursa
}
let city = City.Melbourne

print(city)
// prints "Melbourne"

let cityName = "\(city)"   // or `let cityName = String(city)`
// cityName contains "Melbourne"

Il n’est donc plus nécessaire de définir et de maintenir une fonction pratique qui bascule sur chaque cas pour renvoyer un littéral de chaîne. De plus, cela fonctionne automatiquement pour toutes les énumérations, même si aucun type de valeur brute n'est spécifié.

debugPrint(_:) & String(reflecting:) peut être utilisé pour un nom complet:

debugPrint(city)
// prints "App.City.Melbourne" (or similar, depending on the full scope)

let cityDebugName = String(reflecting: city)
// cityDebugName contains "App.City.Melbourne"

Notez que vous pouvez personnaliser ce qui est imprimé dans chacun de ces scénarios:

extension City: CustomStringConvertible {
    var description: String {
        return "City \(rawValue)"
    }
}

print(city)
// prints "City 1"

extension City: CustomDebugStringConvertible {
    var debugDescription: String {
        return "City (rawValue: \(rawValue))"
    }
}

debugPrint(city)
// prints "City (rawValue: 1)"

(Je n'ai pas trouvé de moyen d'appeler cette valeur "par défaut", par exemple, pour imprimer "The city is Melbourne" sans avoir recours à une instruction switch. Utiliser \(self) dans l'implémentation de description/debugDescription provoque une récursion infinie.)


Les commentaires ci-dessus _ Les initialiseurs de Stringinit(_:) & init(reflecting:) décrivent exactement ce qui est imprimé, en fonction de la conformité du type réfléchi:

extension String {
    /// Initialize `self` with the textual representation of `instance`.
    ///
    /// * If `T` conforms to `Streamable`, the result is obtained by
    ///   calling `instance.writeTo(s)` on an empty string s.
    /// * Otherwise, if `T` conforms to `CustomStringConvertible`, the
    ///   result is `instance`'s `description`
    /// * Otherwise, if `T` conforms to `CustomDebugStringConvertible`,
    ///   the result is `instance`'s `debugDescription`
    /// * Otherwise, an unspecified result is supplied automatically by
    ///   the Swift standard library.
    ///
    /// - SeeAlso: `String.init<T>(reflecting: T)`
    public init<T>(_ instance: T)

    /// Initialize `self` with a detailed textual representation of
    /// `subject`, suitable for debugging.
    ///
    /// * If `T` conforms to `CustomDebugStringConvertible`, the result
    ///   is `subject`'s `debugDescription`.
    ///
    /// * Otherwise, if `T` conforms to `CustomStringConvertible`, the result
    ///   is `subject`'s `description`.
    ///
    /// * Otherwise, if `T` conforms to `Streamable`, the result is
    ///   obtained by calling `subject.writeTo(s)` on an empty string s.
    ///
    /// * Otherwise, an unspecified result is supplied automatically by
    ///   the Swift standard library.
    ///
    /// - SeeAlso: `String.init<T>(T)`
    public init<T>(reflecting subject: T)
}


Voir le notes de publication pour plus d'informations sur ce changement.

117
Stuart

Il n'y a pas d'introspection sur les cas d'enum pour le moment. Vous devrez les déclarer chacun manuellement:

enum City: String, CustomStringConvertible {
    case Melbourne = "Melbourne"
    case Chelyabinsk = "Chelyabinsk"
    case Bursa = "Bursa"

    var description: String {
        get {
            return self.rawValue
        }
    }
}

Si vous souhaitez que le type brut soit un Int, vous devez effectuer vous-même un changement:

enum City: Int, CustomStringConvertible {
  case Melbourne = 1, Chelyabinsk, Bursa

  var description: String {
    get {
      switch self {
        case .Melbourne:
          return "Melbourne"
        case .Chelyabinsk:
          return "Chelyabinsk"
        case .Bursa:
          return "Bursa"
      }
    }
  }
}
73
drewag

Dans Swift-3 (testé avec Xcode 8.1), vous pouvez ajouter les méthodes suivantes dans votre enum:

/**
 * The name of the enumeration (as written in case).
 */
var name: String {
    get { return String(describing: self) }
}

/**
 * The full name of the enumeration
 * (the name of the enum plus dot plus the name as written in case).
 */
var description: String {
    get { return String(reflecting: self) }
}

Vous pouvez ensuite l'utiliser comme un appel de méthode normal sur votre instance enum. Cela pourrait également fonctionner dans les versions précédentes de Swift, mais je ne l’ai pas testé.

Dans votre exemple:

enum City: Int {
    case Melbourne = 1, Chelyabinsk, Bursa
    var name: String {
        get { return String(describing: self) }
    }
    var description: String {
        get { return String(reflecting: self) }
    }
}
let city = City.Melbourne

print(city.name)
// prints "Melbourne"

print(city.description)
// prints "City.Melbourne"

Si vous souhaitez fournir cette fonctionnalité à tous vos enums, vous pouvez en faire une extension:

/**
 * Extend all enums with a simple method to derive their names.
 */
extension RawRepresentable where RawValue: Any {
  /**
   * The name of the enumeration (as written in case).
   */
  var name: String {
    get { return String(describing: self) }
  }

  /**
   * The full name of the enumeration
   * (the name of the enum plus dot plus the name as written in case).
   */
  var description: String {
    get { return String(reflecting: self) }
  }
}

Cela ne fonctionne que pour Swift enum.

32
Matthias Voss

Pour Objective-C enums, le seul moyen semble actuellement être, par exemple, d'étendre l'énumération avec CustomStringConvertible pour aboutir à quelque chose comme:

extension UIDeviceBatteryState: CustomStringConvertible {
    public var description: String {
        switch self {
        case .Unknown:
            return "Unknown"
        case .Unplugged:
            return "Unplugged"
        case .Charging:
            return "Charging"
        case .Full:
            return "Full"
        }
    }
}

Et puis jetter la enum comme String:

String(UIDevice.currentDevice().batteryState)
15
Markus Rautopuro

En plus du support String (…) (CustomStringConvertible) pour les énumérations dans Swift 2.2, il existe également un support de réflexion cassé pour eux. Pour les cas enum avec des valeurs associées, il est possible d’obtenir l’étiquette du cas enum en utilisant la réflexion:

enum City {
    case Melbourne(String)
    case Chelyabinsk
    case Bursa

    var label:String? {
        let mirror = Mirror(reflecting: self)
        return mirror.children.first?.label
    }
}

print(City.Melbourne("Foobar").label) // prints out "Melbourne"

En étant brisé, je voulais toutefois dire que pour les "simples" énumérations, la propriété calculée calculée ci-dessus label renvoie simplement nil (boo-hoo).

print(City.Chelyabinsk.label) // prints out nil

La situation avec réflexion devrait s'améliorer après Swift 3, apparemment. La solution pour l'instant est cependant String(…), comme suggéré dans l'une des autres réponses:

print(String(City.Chelyabinsk)) // prints out Cheylabinsk
7
mz2

C'est tellement décevant.

Pour le cas où vous avez besoin de ces noms (que le compilateur connaît parfaitement l’orthographe exacte, mais refuse de laisser l’accès - merci Swift team !! -) mais ne veut pas ou ne peut pas faire de String le base de votre enum, une alternative prolixe et encombrante est la suivante:

enum ViewType : Int, Printable {

    case    Title
    case    Buttons
    case    View

    static let all = [Title, Buttons, View]
    static let strings = ["Title", "Buttons", "View"]

    func string() -> String {
        return ViewType.strings[self.rawValue]
    }

    var description:String {
        get {
            return string()
        }
    }
}

Vous pouvez utiliser ce qui précède comme suit:

let elementType = ViewType.Title
let column = Column.Collections
let row = 0

println("fetching element \(elementType), column: \(column.string()), row: \(row)")

Et vous obtiendrez le résultat attendu (code de la colonne similaire, mais non affiché)

fetching element Title, column: Collections, row: 0

Dans ce qui précède, j'ai fait en sorte que la propriété description renvoie à la méthode string, mais il s'agit d'une question de goût. Notez également que les variables dites static doivent être qualifiées de portée par le nom de leur type englobant, car le compilateur est trop amnésique et ne peut pas rappeler le contexte tout seul ...

L'équipe Swift doit vraiment être commandée. Ils ont créé un enum que vous ne pouvez pas utiliser enumerate et que vous pouvez utiliser enumerate sur sont des "séquences" mais pas enum!

5
verec

Pour Swift:

extension UIDeviceBatteryState: CustomStringConvertible {

    public var description: String {
        switch self {
        case .unknown:
            return "unknown"
        case .unplugged:
            return "unplugged"
        case .charging:
            return "charging"
        case .full:
            return "full"
        }
    }

}

si votre variable "batteryState", appelez:

self.batteryState.description
3
xevser

Swift a maintenant ce qu'on appelle valeur brute attribuée implicitement . Fondamentalement, si vous ne donnez pas de valeur brute à chaque cas et que l'énumération est de type String, elle en déduit que la valeur brute de la cas est elle-même au format chaîne. Allez l'essayer.

enum City: String {
  case Melbourne, Chelyabinsk, Bursa
}

let city = City.Melbourne.rawValue

// city is "Melbourne"
2
NSCoder

Simple mais fonctionne ...

enum ViewType : Int {
    case    Title
    case    Buttons
    case    View
}

func printEnumValue(enum: ViewType) {

    switch enum {
    case .Title: println("ViewType.Title")
    case .Buttons: println("ViewType.Buttons")
    case .View: println("ViewType.View")
    }
}
2
Jimbo Jones

L'initialiseur String(describing:) peut être utilisé pour renvoyer le nom de l'étiquette de cas, même pour les énumérations avec des valeurs rawValues ​​non-String:

enum Numbers: Int {
    case one = 1
    case two = 2
}

let one = String(describing: Numbers.one) // "one"
let two = String(describing: Numbers.two) // "two"

Notez que cela ne fonctionne pas si si l'enum utilise le modificateur @objc:

https://forums.Swift.org/t/why-is-an-en-enen-returning-enumname-rather-than-caselabel-for-string-describing/27327

Les interfaces Swift générées pour les types Objective-C n'incluent parfois pas le modificateur @objc. Ces énumérations sont néanmoins définies dans Objective-C et ne fonctionnent donc pas comme ci-dessus.

1
pkamb