11

I have an enum defined as follows

enum Fruit {
    case Apple(associatedValue: String)
    case Orange(associatedValue: String)
}

I have a function that takes an argument of type Fruit

func printNameOnly(fruit: Fruit) {

}

In this function I want to get the enum case as a string, i.e. I want to get the string "Apple" or "Orange" without regard to whatever the associated value is. Is this possible with Swift?

I could obviously write a function which takes the fruit enum and returns a string using a case statement, but I am trying to find a way to avoid that since the string I want is the enum case name itself.

pkamb
  • 33,281
  • 23
  • 160
  • 191
Puneet
  • 736
  • 9
  • 17
  • you could add additional associate value as same as your case string. – antonio081014 Feb 13 '16 at 00:41
  • But then everytime the enum has to be used I would have to specify a string like let v = .Apple("Apple") – Puneet Feb 13 '16 at 00:43
  • 1
    In Swift, enum's rawvalue and associate value could not coexist. You might want to explain why you do it this way, which might have another solution to your problem. – antonio081014 Feb 13 '16 at 00:51

5 Answers5

20

Try this (Swift 3.1). Covers associated or regular cases.

enum Fruit {
    case banana
    case apple(String)
    case orange(String)

    var label:String {
        let mirror = Mirror(reflecting: self)
        if let label = mirror.children.first?.label {
            return label
        } else {
            return String(describing:self)
        }
    }
}

print(Fruit.banana.label) // "banana"
print(Fruit.apple("yum").label) // "apple"
David James
  • 2,430
  • 1
  • 26
  • 35
4

So you want a Swift enum with a RawValue and an associated value.

You can't

The best solution I can think of is adding a computed property to your enum (similarly to what you suggested in your own question).

enum Fruit {
    case Apple(name:String)
    case Orange(name:String)

    var fruitDesc: String {
        switch self {
        case .Apple: return "Apple"
        case .Orange: return "Orange"
        }
    }
}

let fruit = Fruit.Apple(name: "McIntosh")
print(fruit.fruitDesc) // Apple
Community
  • 1
  • 1
Luca Angeletti
  • 58,465
  • 13
  • 121
  • 148
  • We can have a Swift enum with a RawValue and an associated value, but we need to be careful if we do, which I tried to spot in my answer. – Paul B Sep 03 '19 at 13:35
1

If you want to get the enumeration case label for debugging purposes, this neat private reflection function from the Swift runtime might come in handy:

/// Returns the case label for the given enumeration value.
public func getEnumCaseName<T>(_ value: T) -> String? {
    return __getEnumCaseName(value).flatMap { String(validatingUTF8: $0) }
}

/// A private Swift function from the compiler which returns the case
/// label of the given enumeration value, represented as a C string.
@_silgen_name("swift_EnumCaseName")
fileprivate func __getEnumCaseName<T>(_ value: T) -> UnsafePointer<CChar>?

I've tested this approach with Swift 4 only. As mentioned above, I would not recommend using this for production code but rather only for debugging utilities as it is private/undocumented API and it might break in a future Swift version.

Lukáš Kubánek
  • 946
  • 1
  • 15
  • 27
1

Yes, reflection API can help. This one will work both in debugging and in production.

enum Fruit {
    case apple(associatedValue: String)
    case orange(associatedValue: String)
}

func fruitNameOnly(fruit: Fruit) -> String {
    return Mirror(reflecting: fruit).children.first!.label!
}

let greenApple = Fruit.apple(associatedValue: "Green")

print(fruitNameOnly(fruit: greenApple)) // apple

An extended version making use of both associated and raw values:

enum Fruit: RawRepresentable {
    case apple(associatedValue: String)
    case orange(associatedValue: String)

   typealias RawValue = String
    var rawValue: String {

        //return Mirror(reflecting: self).children.first!.label!
        // we rather use a regular switch this time which allows for custom values
        switch self {
        case .apple:     return "apple"
        case .orange(let av):   return "orange " + av // Edge case, careful!
        // Normally rawValues should form one to one relationship.
        // RawRepresentable protocol indicates, that you can "switch back and forth between a custom type and an associated RawValue type without losing the value of the original RawRepresentable type" (developer.apple.com/documentation/swift/rawrepresentable)

        }
    }

    init?(rawValue: String) {
        switch rawValue {
        case "apple":
            self = .apple(associatedValue: "")
        case "orange":
            self = .orange(associatedValue: "")
        default:
            return nil
        }
    }
}

func fruitNameOnly(fruit: Fruit) -> String {
    return Mirror(reflecting: fruit).children.first!.label!
}

let greenApple = Fruit.apple(associatedValue: "green")
print(fruitNameOnly(fruit: greenApple)) // apple

if let yellowOrange = Fruit.init(rawValue: "orange") {
   print(yellowOrange) // orange(associatedValue: "")
}
print(greenApple.rawValue) //apple
let redOrange = Fruit.orange(associatedValue: "red")
print(redOrange.rawValue) //orange red
Paul B
  • 3,989
  • 33
  • 46
0

When enums have associated values, printing them will list all of the values along with the enum name. For example:

let anApple = Fruit.Apple("myApple")
print(anApple)

This will produce:

Apple("myApple")

Therefore to get just "Apple", extract the part up to the first "(".

pkamb
  • 33,281
  • 23
  • 160
  • 191
Michael
  • 8,891
  • 3
  • 29
  • 42
  • 4
    I think that it would be very hacky to rely on the string representation produced by the default description. – Puneet Feb 13 '16 at 02:35
  • Maybe, but that's what you do for all enum values when you convert them to string. You rely on `description` producing the enum name. – Michael Feb 13 '16 at 02:38