If I have an enumeration with raw Integer
values:
enum City: Int {
case Melbourne = 1, Chelyabinsk, Bursa
}
let city = City.Melbourne
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
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.
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
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:
https://forums.swift.org/t/why-is-an-enum-returning-enumname-rather-than-caselabel-for-string-describing/27327
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.
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"
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 the
LosslessStringConvertible` 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"
}
}