Bagaimana cara mendapatkan nama nilai enumerasi di Swift?

167

Jika saya memiliki enumerasi dengan Integernilai mentah :

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

let city = City.Melbourne

Bagaimana saya bisa mengkonversi citynilai ke string Melbourne? Apakah introspeksi nama jenis seperti ini tersedia dalam bahasa tersebut?

Sesuatu seperti (kode ini tidak akan berfungsi):

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

Jawaban:

139

Pada Xcode 7 beta 5 (Swift versi 2) Anda sekarang dapat mencetak nama-nama jenis dan kasus enum secara default menggunakan print(_:), atau mengkonversi ke Stringmenggunakan String's init(_:)sintaks initializer atau interpolasi string. Jadi untuk contoh Anda:

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"

Jadi tidak ada lagi kebutuhan untuk mendefinisikan & memelihara fungsi kenyamanan yang mengaktifkan setiap case untuk mengembalikan string literal. Selain itu, ini berfungsi secara otomatis untuk enum apa pun, bahkan jika tidak ada tipe nilai mentah yang ditentukan.

debugPrint(_:)& String(reflecting:)dapat digunakan untuk nama yang sepenuhnya memenuhi syarat:

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

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

Perhatikan bahwa Anda dapat menyesuaikan apa yang dicetak dalam masing-masing skenario ini:

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

(Saya belum menemukan cara untuk memanggil nilai "default" ini, misalnya, untuk mencetak "Kota adalah Melbourne" tanpa menggunakan kembali ke pernyataan peralihan. Menggunakan \(self)dalam implementasi description/ debugDescriptionmenyebabkan rekursi tak terbatas.)


Komentar di atas String's init(_:)& init(reflecting:)initializers menjelaskan dengan tepat apa yang dicetak, tergantung pada jenis sesuai tercermin ke:

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


Lihat catatan rilis untuk info tentang perubahan ini.

Stuart
sumber
8
Juga jika Anda ingin nilai string tanpa menggunakan print(enum)Anda dapat menggunakanString(enum)
Kametrixom
44
Tangkapan penting, ini hanya berfungsi untuk enums Swift. Jika Anda memberi tag @objc untuk memungkinkan dukungan yang mengikat pada OS X, ini tidak akan berfungsi.
Claus Jørgensen
11
Jawaban khusus Swift-spesifik; namun, jika Anda perlu melakukan ini pada non-swift enum, seperti untuk mencetak nilai (Objective C) CLAuthorizationStatusenum di dalam locationManager didChangeAuthorizationStatuscallback delegate Anda, Anda perlu mendefinisikan ekstensi protokol. Misalnya: extension CLAuthorizationStatus: CustomStringConvertable { public var description: String { switch self { case .AuthorizedAlways: return "AuthorizedAlways" <etc> } } }- setelah Anda melakukan ini, itu akan berfungsi seperti yang Anda harapkan: print ("Auth status: (\ status))".
Jeffro
3
"Pada Xcode 7 beta 5" tidak ada artinya. Bukan Xcode yang mendefinisikan semua ini, itu adalah kompiler Swift dan Swift Runtime Libaries. Saya dapat menggunakan Xcode 9.3 tetapi Kode saya masih bisa Swift 3 dan kemudian saya tidak akan bisa menggunakan fitur Swift 4. Menggunakan Xcode 9.3, kode ini tidak berfungsi meskipun Xcode 9.3 jauh lebih baru daripada Xcode 7.
Mecki
8
Saya mendapatkan inisialisasi 'init (_ :)' mengharuskan City untuk 'LosslessStringConvertible' pada xcode 10.2, Swift 5. Bagaimana kita melakukannya sekarang?
rockgecko
73

Tidak ada introspeksi pada kasus enum saat ini. Anda harus mendeklarasikannya secara manual:

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

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

Jika Anda ingin jenis mentah menjadi Int, Anda harus melakukan pergantian sendiri:

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"
      }
    }
  }
}
drewag
sumber
2
Noob pertanyaan, tetapi mengapa menempatkan get {return self.rawValue} alih-alih hanya mengembalikan self.value? Saya mencoba yang terakhir dan itu berfungsi dengan baik.
Chuck Krutsinger
Anda juga dapat menghilangkan get { ... }bagian untuk singkatnya jika Anda tidak mendefinisikan setter.
iosdude
1
Terima kasih atas jawabannya. Di Xcode 7.3, saya mendapatkan: "Printable telah diubah namanya menjadi CustomStringConvertible". Solusinya mudah - dalam contoh kode pertama di atas, ubah baris pertama menjadi enum City : String, CustomStringConvertible {. Sebagai bagian dari protokol CSC, Anda kemudian perlu mengubah properti menjadi publik , misalnya:public var description : String {
Jeffro
44

Di Swift-3 (diuji dengan Xcode 8.1) Anda dapat menambahkan metode berikut di enum Anda:

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

Anda kemudian dapat menggunakannya sebagai pemanggilan metode normal pada instance enum Anda. Mungkin juga bekerja di versi Swift sebelumnya, tetapi saya belum mengujinya.

Dalam contoh Anda:

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"

Jika Anda ingin memberikan fungsionalitas ini ke semua enum Anda, Anda bisa menjadikannya sebagai ekstensi:

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

Ini hanya berfungsi untuk enums Swift.

Matthias Voss
sumber
18

Untuk Objective-C enum, satu-satunya cara saat ini tampaknya, misalnya, untuk memperpanjang enum dengan CustomStringConvertibleberakhir dengan sesuatu seperti:

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

Dan kemudian casting enumsebagai String:

String(UIDevice.currentDevice().batteryState)
Markus Rautopuro
sumber
12

The String(describing:)initializer dapat digunakan untuk mengembalikan nama label kasus bahkan untuk enum dengan 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"

Perhatikan bahwa ini tidak berfungsi jika enum menggunakan @objcpengubah:

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

Antarmuka Swift yang Dihasilkan untuk tipe Objective-C terkadang tidak termasuk @objcpengubah. Namun demikian, Enums tersebut didefinisikan dalam Objective-C, dan karenanya tidak berfungsi seperti di atas.

pkamb
sumber
7

Di atas dukungan String (...) (CustomStringConvertible) untuk enum di Swift 2.2, ada juga dukungan refleksi yang agak rusak untuk mereka. Untuk kasus enum dengan nilai terkait, dimungkinkan untuk mendapatkan label kasus enum menggunakan refleksi:

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"

Namun, dengan dipatahkan, maksud saya adalah untuk enum "sederhana", labelproperti yang dihitung berdasarkan refleksi di atas baru saja kembali nil(boo-hoo).

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

Situasi dengan refleksi seharusnya menjadi lebih baik setelah Swift 3, tampaknya. Solusi untuk saat ini adalah String(…), seperti yang disarankan dalam salah satu jawaban lain:

print(String(City.Chelyabinsk)) // prints out Cheylabinsk
mz2
sumber
2
Ini tampaknya bekerja pada Swift 3.1 tanpa perlu membuatnya opsional: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

Ini sangat mengecewakan.

Untuk kasus ketika Anda membutuhkan nama-nama (bahwa kompiler dengan sempurna mengetahui ejaan yang tepat, tetapi menolak untuk membiarkan akses - terima kasih tim Swift !! -) tetapi tidak ingin atau tidak dapat menjadikan String sebagai basis enum Anda, sebuah verbose, alternatif rumit adalah sebagai berikut:

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

Anda dapat menggunakan hal di atas sebagai berikut:

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

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

Dan Anda akan mendapatkan hasil yang diharapkan (kode untuk Kolom serupa, tetapi tidak ditampilkan)

fetching element Title, column: Collections, row: 0

Di atas, saya telah membuat descriptionproperti merujuk kembali ke stringmetode, tetapi ini adalah masalah selera. Perhatikan juga bahwa staticvariabel yang disebut harus memiliki ruang lingkup yang memenuhi syarat dengan nama tipe penutupnya, karena kompiler terlalu amnesik dan tidak dapat mengingat konteksnya dengan sendirinya ...

Tim Swift harus benar-benar diperintahkan. Mereka menciptakan enum yang tidak bisa Anda gunakan enumeratedan yang bisa Anda gunakan enumerateadalah "Urutan" tetapi tidak enum!

verec
sumber
Tampaknya agak panjang lebar daripada hanya melakukan return String (mencerminkan: diri) dalam deskripsi.
Boon
4

Saya menabrak pertanyaan ini dan ingin berbagi cara sederhana untuk membuat Fungsi sulap yang disebutkan

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

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

let city = City.Melbourne
city.magicFunction() //prints Melbourne
Sev
sumber
3

Swift sekarang memiliki apa yang dikenal sebagai Nilai Baku yang Ditetapkan Secara Implisit . Pada dasarnya jika Anda tidak memberikan nilai mentah untuk setiap case dan enum bertipe String, itu menyimpulkan bahwa nilai mentah case itu sendiri dalam format string. Ayo coba.

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

let city = City.Melbourne.rawValue

// city is "Melbourne"
NSCoder
sumber
3

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

}

jika variabel "batteryState" Anda kemudian panggil:

self.batteryState.description
xevser
sumber
1

Sederhana tapi berfungsi ...

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")
    }
}
Jimbo Jones
sumber