Swiftで列挙値の名前を取得するにはどうすればよいですか?


167

生の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

回答:


139

Xcode 7ベータ5(Swiftバージョン2)以降では、デフォルトでを使用して型名と列挙型のケースを出力しprint(_:)たり、の初期化子または文字列補間構文をString使用して変換したりできます。だからあなたの例では:Stringinit(_:)

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無限の再帰が発生します。)


上記のコメントStringinit(_:)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`'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)
}


この変更に関する情報について は、リリースノートを参照してください。


8
また、print(enum)使用せずに文字列値が必要な場合は使用できますString(enum)
Kametrixom

44
重要なキャッチ、これはSwift列挙型でのみ機能します。OS Xでバインディングサポートを許可するために@objcにタグを付けると、これは機能しません。
クラウスヨルゲンセン、2016

11
素晴らしいSwift固有の答え。ただし、デリゲートコールバックCLAuthorizationStatus内に(Objective C)列挙型の値を出力するなど、Swift以外の列挙型でこれを行う必要がある場合locationManager didChangeAuthorizationStatusは、プロトコル拡張を定義する必要があります。次に例を示しextension CLAuthorizationStatus: CustomStringConvertable { public var description: String { switch self { case .AuthorizedAlways: return "AuthorizedAlways" <etc> } } }ます。-これを実行すると、期待どおりに機能するはずです:print( "Auth status:(\ status))"。
Jeffro

3
「Xcode 7ベータ5以降」は意味がありません。これを定義するのはXcodeではなく、SwiftコンパイラとSwiftランタイムライブラリです。Xcode 9.3を使用できますが、私のコードはSwift 3のままにすることができ、Swift 4の機能を使用できなくなります。Xcodeの9.3を使用して、このコードは、Xcodeの7よりもはるかに新しいもののXcode 9.3にもかかわらず、動作しません
Mecki

8
イニシャライザ 'init(_ :)'を取得するには、Cityがxcode 10.2、Swift 5の 'LosslessStringConvertible'に準拠している必要があります。
rockgecko

73

現時点では、列挙型のケースに関する内省はありません。それぞれを手動で宣言する必要があります。

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

2
Noobの質問ですが、なぜ単にself.valueを返すのではなく、get {return self.rawValue}を置くのですか?私は後者を試しましたが、それはうまくいきます。
チャッククルツィンガー2015年

get { ... }セッターを定義しない場合は、簡潔にするためにこの部分を省略できます。
iosdude 2016

1
素晴らしい答えをありがとう。Xcode 7.3では、「Printableの名前がCustomStringConvertibleに変更されました」と表示されます。解決策は簡単enum City : String, CustomStringConvertible {です。上の最初のコード例では、最初の行をに変更します。次に、CSCプロトコルの一部として、プロパティをpublicに変更する必要があります。例:public var description : String {
Jeffro

44

Swift-3(Xcode 8.1でテスト済み)では、列挙型に次のメソッドを追加できます。

/**
 * 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列挙型でのみ機能します。


18

Objective-C enumの場合、現在のところ唯一の方法は、たとえば、列挙型を拡張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)

12

String(describing:)初期化子がさえ非文字列rawValuesと列挙型のためのcaseラベル名を返すために使用することができます。

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

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

enumが修飾子を使用している場合、これは機能しないことに注意してください@objc

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

Objective-Cタイプ用に生成されたSwiftインターフェースには、@objc修飾子が含まれていない場合があります。これらの列挙型はObjective-Cで定義されているため、上記のように機能しません。


7

Swift 2.2の列挙型に対するString(…)(CustomStringConvertible)サポートに加えて、それらに対するリフレクションのサポートもいくらか壊れています。関連付けられた値を持つ列挙型ケースの場合、リフレクションを使用して列挙型ケースのラベルを取得できます。

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(…)、他の回答の1つで提案されているように、現時点での解決策はです。

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

2
これは、オプションにする必要なしにSwift 3.1で機能するようです:var label:String { let mirror = Mirror(reflecting: self); if let label = mirror.children.first?.label { return label } else { return String(describing:self) } }
David James

5

これはとても残念です。

それらの名前が必要な場合(コンパイラは正確なスペルを完全に知っているが、アクセスを許可しない-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は「シーケンス」ですが、ではありませんenum


これは、説明で単にreturn String(reflecting:self)を実行するよりもかなり長いようです。
ブーン

4

私はこの質問にぶつかり、言及されたmagicFunctionを作成する簡単な方法を共有したいと思いました

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

    func magicFunction() -> String {
        return "\(self)"
    }
}

let city = City.Melbourne
city.magicFunction() //prints Melbourne

3

Swiftには、Implicitly Assigned Raw Valueと呼ばれるものがあります。基本的に、各ケースに未加工の値を指定せず、列挙型がString型である場合、ケースの未加工の値自体が文字列形式であると推定されます。試してみてください。

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

let city = City.Melbourne.rawValue

// city is "Melbourne"

3

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

}

変数「batteryState」の場合、次を呼び出します。

self.batteryState.description

1

シンプルですが動作します...

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")
    }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.