简体   繁体   中英

Get enumeration name when using associated values

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, ie 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.

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"

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

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 "(".

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.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM