Как получить имя значения перечисления в Swift?

Если у меня есть перечисление с необработанными значениями Integer:

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

let city = City.Melbourne

Как преобразовать значение city в строку Melbourne? Является ли этот тип интроспекции имени типа доступным на языке?

Что-то вроде (этот код не будет работать):

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

Ответ 1

Начиная с Xcode 7 beta 5 (Swift версии 2), теперь вы можете печатать имена типов и регистры по умолчанию, используя print(_:), или конвертировать в String используя init(_:) String init(_:) или синтаксис строковой интерполяции. Итак, для вашего примера:

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"

Таким образом, больше нет необходимости определять и поддерживать вспомогательную функцию, которая включает каждый случай для возврата строкового литерала. Кроме того, это работает автоматически для любого перечисления, даже если тип необработанного значения не указан.

debugPrint(_:) & String(reflecting:) можно использовать для полного имени:

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

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

Обратите внимание, что вы можете настроить то, что напечатано в каждом из этих сценариев:

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

(Я не нашел способа вызвать это значение "по умолчанию", например, чтобы напечатать "Город - Мельбурн", не возвращаясь к инструкции switch. Использование \(self) в реализации description/debugDescription вызывает бесконечная рекурсия.)


Комментарии выше String init(_:) & init(reflecting:) инициализаторы точно описывают, что печатается, в зависимости от того, чему соответствует отраженный тип:

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' 'description'
    /// * Otherwise, if 'T' conforms to 'CustomDebugStringConvertible',
    ///   the result is 'instance' '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' 'debugDescription'.
    ///
    /// * Otherwise, if 'T' conforms to 'CustomStringConvertible', the result
    ///   is 'subject' '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)
}


Смотрите информацию о выпуске для получения информации об этом изменении.

Ответ 2

В настоящее время нет никакого самоанализа по случаям перечисления. Вы должны будете объявить их каждый вручную:

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

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

Если вам нужно, чтобы необработанный тип был Int, вам нужно будет переключиться самостоятельно:

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

Ответ 3

В Swift-3 (протестировано с Xcode 8.1) вы можете добавить следующие методы в свой 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) }
}

Затем вы можете использовать его как обычный вызов метода для вашего экземпляра enum. Это может также работать в предыдущих версиях Swift, но я не проверял это.

В вашем примере:

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"

Если вы хотите предоставить эту функциональность всем своим перечислениям, вы можете сделать это расширением:

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

Это работает только для перечислений Swift.

Ответ 4

Для enum Objective-C в настоящее время кажется, что единственным способом является, например, расширение перечисления с помощью CustomStringConvertible заканчивающееся чем-то вроде:

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

И затем приведение enum качестве String:

String(UIDevice.currentDevice().batteryState)

Ответ 5

В верхней части поддержки String (...) (CustomStringConvertible) для перечислений в Swift 2.2 также имеется некоторая разбитая поддержка отражения для них. Для случаев перечисления со связанными значениями можно получить метку случая перечисления с использованием отражения:

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"

Разбившись, я, однако, имел в виду, что для "простых" перечислений приведенное выше основанное на отражении свойство label возвращает nil (boo-hoo).

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

Ситуация с отражением должна улучшаться после Swift 3, по-видимому. Решение на данный момент, однако, String(…), как предлагается в одном из других ответов:

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

Ответ 6

Это так разочаровывает.

В случае, когда вам нужны эти имена (что компилятор прекрасно знает точное написание, но отказывается предоставить доступ - спасибо команде Swift!!), но не хотите или не могу сделать String базой вашего перечисление, сложная, громоздкая альтернатива заключается в следующем:

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

Вы можете использовать вышеуказанное:

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

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

И вы получите ожидаемый результат (код для столбца аналогичный, но не показан)

fetching element Title, column: Collections, row: 0

В приведенном выше примере свойство description ссылается на метод string, но это вопрос вкуса. Также обратите внимание, что так называемые переменные static должны быть областью, определяемой именем их вложенного типа, поскольку компилятор слишком амнесичен и не может полностью вспомнить контекст...

Команде Swift действительно нужно командовать. Они создали перечисление, что вы не можете enumerate, и то, что вы можете использовать enumerate on, это "Последовательности", но не enum!

Ответ 7

Для Свифта:

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

}

если ваша переменная "batteryState", то вызовите:

self.batteryState.description

Ответ 8

Swift теперь имеет так называемое Неявно присвоенное исходное значение. В принципе, если вы не даете исходные значения каждому случаю, а перечисление имеет тип String, оно выводит, что исходное значение case само по себе является строковым. Продолжайте, дайте ему попробовать.

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

let city = City.Melbourne.rawValue

// city is "Melbourne"

Ответ 9

Инициализатор String(describing:) можно использовать для возврата имени метки регистра даже для перечислений с не-String rawValues:

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

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

Обратите внимание, что это не работает, если перечисление использует модификатор @objc:

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

Сгенерированные интерфейсы Swift для типов Objective-C иногда не включают модификатор @objc. Эти перечисления, тем не менее, определены в Objective-C и, следовательно, не работают, как описано выше.

Ответ 10

Простой, но работает...

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