223

If I have an enumeration with raw Integer values:

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

let city = City.Melbourne

How can I convert a city value to a string Melbourne? Is this kind of a type name introspection available in the language?

Something like (this code will not work):

println("Your city is \(city.magicFunction)")
> Your city is Melbourne
Templar
  • 1,843
  • 7
  • 29
  • 42
Evgenii
  • 36,389
  • 27
  • 134
  • 170

12 Answers12

180

As of Xcode 7 beta 5 (Swift version 2) you can now print type names and enum cases by default using print(_:), or convert to String using String's init(_:) initializer or string interpolation syntax. So for your example:

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"

So there is no longer a need to define & maintain a convenience function that switches on each case to return a string literal. In addition, this works automatically for any enum, even if no raw-value type is specified.

debugPrint(_:) & String(reflecting:) can be used for a fully-qualified name:

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

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

Note that you can customise what is printed in each of these scenarios:

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

(I haven't found a way to call into this "default" value, for example, to print "The city is Melbourne" without resorting back to a switch statement. Using \(self) in the implementation of description/debugDescription causes an infinite recursion.)


The comments above String's init(_:) & init(reflecting:) initializers describe exactly what is printed, depending on what the reflected type conforms to:

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


See the release notes for info about this change.

pkamb
  • 33,281
  • 23
  • 160
  • 191
Stuart
  • 36,683
  • 19
  • 101
  • 139
  • 8
    Also if you want the string value without using `print(enum)` you can use `String(enum)` – Kametrixom Aug 08 '15 at 14:30
  • I don't know why, but XCode 6.4 is giving me an error for the line `let cityDebugName = String(reflecting: city)` – nonopolarity Aug 31 '15 at 15:21
  • 61
    Important catch, this *only* works for Swift enums. If you tag it @objc to allow binding support on OS X, this will not work. – Claus Jørgensen Feb 04 '16 at 23:17
  • 1
    release note link broken – Mikael Feb 05 '16 at 03:29
  • 1
    To add to the comment by @Claus, this technique doesn't seem to work with C-based enums, like, say, NSLocaleLanguageDirection (try it). However, it is possible to add an extension to such a type which conforms to CustomStringConvertible, but it's a pain to have to do it. – jules Feb 25 '16 at 21:51
  • 17
    Great Swift-specific answer; however, if you need to do this on a non-swift enum, such as to print the (Objective C) `CLAuthorizationStatus` enum's value inside your `locationManager didChangeAuthorizationStatus` delegate callback, you'd need to define a protocol extension. For example: `extension CLAuthorizationStatus: CustomStringConvertable { public var description: String { switch self { case .AuthorizedAlways: return "AuthorizedAlways" } } }` - once you've done this, it should work as you'd expect: print("Auth status: (\status))". – Goffredo Apr 27 '16 at 19:41
  • 2
    Thanks Jeffro. Slight correction: it's **CustomStringConvertible** not CustomStringConvertable. – devios1 Jun 08 '16 at 17:02
  • @Stuart can we do same in objective-c? not able to figure anything in objective -c – Nitin Mar 03 '17 at 02:54
  • @Nit This question relates specifically to Swift, so I'd recommend searching/asking a separate question about enums in Objective-C. – Stuart Mar 03 '17 at 12:48
  • 4
    "As of Xcode 7 beta 5" is meaningless. It's not Xcode that defines any of this, it's the Swift compiler and the Swift Runtime Libaries. I can use Xcode 9.3 but my Code can still be Swift 3 and then I wont' be able to use Swift 4 features. Using Xcode 9.3, this code doesn't work despite Xcode 9.3 being much newer than Xcode 7. – Mecki May 09 '18 at 17:22
  • 10
    I got initializer 'init(_:)' requires that City conform to 'LosslessStringConvertible' on xcode 10.2, Swift 5. How do we do it now? – rockgecko May 26 '19 at 14:13
  • 2
    why does `print(UNNotificationSetting.disabled)` not print `disabled` but `UNNotificationSetting`? – rolling_codes Jan 08 '20 at 22:15
  • Add `@objc` to the enum, to use it also outside of Swift and you sample will just print "City" for every city. – Mecki Feb 28 '20 at 17:04
  • 1
    To avoid error: Initializer 'init(_:)' requires that City conform to 'LosslessStringConvertible', either use let cityName = "\\(city)", or let cityName = String(describing: city) – P. Stern May 03 '20 at 22:00
  • 1
    Doesn't work for `UIBlurEffect.Style` .... `print(UIBlurEffect.Style.systemChromeMaterialLight)` prints `UIBlurEffectStyle` and not `systemChromeMaterialLight` .. using Swift 5 in iOS 14, Xcode 12.3 – andrewz Jan 22 '21 at 17:28
87

There is no introspection on enum cases at the moment. You will have to declare them each manually:

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

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

If you need the raw type to be an Int, you will have to do a switch yourself:

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
  • 93,393
  • 28
  • 139
  • 128
  • 2
    Noob question, but why put get { return self.rawValue } instead of just return self.value? I tried the latter and it works just fine. – Chuck Krutsinger Mar 02 '15 at 03:22
  • 2
    You can also omit the `get { ... }` part for brevity if you don't define a setter. – iosdude Feb 02 '16 at 05:06
  • 1
    Thanks for the great answer. In Xcode 7.3, I get: "Printable has been renamed to CustomStringConvertible". The solution is easy - in the first code example above, change the first line to `enum City : String, CustomStringConvertible {`. As part of the CSC protocol, you will then need to change the property to be **public**, for example: `public var description : String {` – Goffredo Apr 27 '16 at 19:50
63

In Swift-3 (tested with Xcode 8.1) you can add the following methods in your 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) }
}

You can then use it as a normal method call on your enum instance. It might also work in previous Swift versions, but I haven't tested it.

In your example:

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"

If you want to provide this functionality to all your enums, you can make it an 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) }
  }
}

This only works for Swift enums.

pkamb
  • 33,281
  • 23
  • 160
  • 191
Matthias Voss
  • 631
  • 5
  • 2
35

The String(describing:) initializer can be used to return the case label name even for enums with non-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"

Note that this does not work if the enum uses the @objc modifier:

Generated Swift interfaces for Objective-C types sometimes do not include the @objc modifier. Those Enums are nevertheless defined in Objective-C, and thus do not work like above.

pkamb
  • 33,281
  • 23
  • 160
  • 191
  • also addressed in *[Getting Name of ObjC Enum in Swift 3?](https://stackoverflow.com/a/46850276/192373)* – Alex Cohn Apr 07 '21 at 09:42
23

For Objective-C enums the only way currently seems to be, for example, to extend the enum with CustomStringConvertible ending up with something like:

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

And then casting the enum as String:

String(UIDevice.currentDevice().batteryState)
joshperry
  • 41,167
  • 16
  • 88
  • 103
Markus Rautopuro
  • 7,997
  • 6
  • 47
  • 60
12

On top of the String(…) (CustomStringConvertible) support for enums in Swift 2.2, there's also somewhat broken reflection support for them. For enum cases with associated values it is possible to get the label of the enum case using reflection:

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"

By being broken, I however meant that for "simple" enums, the above reflection based label computed property just returns nil (boo-hoo).

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

The situation with reflection should be getting better after Swift 3, apparently. The solution for now though is String(…), as suggested in one of the other answers:

print(String(City.Chelyabinsk)) // prints out Cheylabinsk
mz2
  • 4,672
  • 1
  • 27
  • 47
  • 2
    This seems to work on Swift 3.1 without needing to make it optional: `var label:String { let mirror = Mirror(reflecting: self); if let label = mirror.children.first?.label { return label } else { return String(describing:self) } }` – David James May 15 '17 at 16:26
7

I bumped into this question and wanted to share a simple way to create the magicFunction mentioned

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

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

let city = City.Melbourne
city.magicFunction() //prints Melbourne
Sev
  • 195
  • 3
  • 10
6

Swift now has what are known as Implicitly Assigned Raw Value. Basically if you don't give raw values to each case and the enum is of type String, it deduces that the case's raw value is itself in string format. Go on give it a try.

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

let city = City.Melbourne.rawValue

// city is "Melbourne"
NSCoder
  • 1,594
  • 5
  • 25
  • 47
5

This is so disappointing.

For the case when you need those names (that the compiler perfectly knows the exact spelling of, but refuses to let access -- thank you Swift team!! --) but do not want or cannot make String the base of your enum, a verbose, cumbersome alternative is as follows:

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

You can use the above as follows:

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

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

And you'll get the expected result (code for the Column similar, but not shown)

fetching element Title, column: Collections, row: 0

In the above, I have made the description property refer back to the string method, but this is a matter of taste. Also note that so called static variables need to be scope qualified by the name of their enclosing type, as the compiler is too amnesic and cannot recall the context all by itself...

The Swift team must really be commanded. They created enum that you cannot enumerate and that which you can use enumerate on are "Sequences" but not enum!

verec
  • 5,224
  • 5
  • 33
  • 40
  • That seems rather long winded than just doing return String(reflecting: self) in description. – Boon Apr 06 '20 at 13:53
4

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

}

if your variable "batteryState" then call:

self.batteryState.description
pkamb
  • 33,281
  • 23
  • 160
  • 191
xevser
  • 369
  • 4
  • 5
1

Introspection in Swift Enums seems to work partially.

I saw @drewag's response, and found that an Enum with no rawValues can indeed have introspection in Swift 5.X with Xcode 11.5. This code works.

public enum Domain: String {
    case network
    case data
    case service
    case sync
    var description: String {
        return "\(self)"     // THIS INTROSPECTION WORKS
    }
}
enum ErrorCode: Int, CustomStringConvertible {
    case success = 200
    case created = 201
    case accepted = 202
    case badRequest = 400
    case unauthorized = 401
    case forbidden = 403
    case notFound = 404
    var code: Int {
        return self.rawValue
    }
    var description: String {
        return "\(self)"      //THIS DOES NOT WORK - EXEC_BAD_ACCESS
    }
}
let errorCode = ErrorCode.notFound
let domain = Domain.network
print(domain.description, errorCode.code, errorCode.description)

Replace the "\(self)" for "string" in the second Enum and you will get this printout: network 404 string

NOTE: Using String(self) instead of "\(self)" in the first Enumwill require the Enum to conform to theLosslessStringConvertible` protocol, and also add other initializers, so a string interpolation seems to be a good workaround.

To Add a var description: String to the enum, you will have to use a Switch statement will all the enum cases as pointed before

var description: String {
    switch self {
    case .success: return "Success"
    case .created: return "Created"
    case .accepted: return "Accepted"
    }
}
eharo2
  • 2,553
  • 1
  • 29
  • 39
  • 2
    `"\(self)"` in your ErrorCode enum should work. The reason you get an EXEC_BAD_ACCESS there is because of the infinite recursion on `description`. If you remove the `var description` implementation and just write something like `print("\(ErrorCode.success)")` independently, you'll be able to get your enum case names just fine. – John Estropia Dec 10 '20 at 00:31
  • return "\(self)" works. I used it learning from yours – Mert Köksal Sep 12 '22 at 12:37
-1

Simple but works...

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