50

I use enums to store string values like this:

    enum Animals: String {
        case descCat = "I has attitude"
        case descDog = "how can I help"
        case descGator = "I will eat you"
        var s: String {
            get {
                return self.rawValue as String
            }
        }
    }

Then I access them like this:

print("Dogs be like:" + Animals.descDog.s)

My question is can I extend enums like any other struct or object so I don't have to add the var s: String {} property to each enum?

Box Box Box Box
  • 5,094
  • 10
  • 49
  • 67
MindSpiker
  • 1,437
  • 1
  • 14
  • 22
  • `self.rawValue` is already of type `String` in your enum. Why are you casting it? – rob mayoff Oct 30 '15 at 21:09
  • 1
    What do you mean by "add the `var s: String{}` property to each enum?" Do you mean "every kind of enum?" You can't do that with structs or objects. Do you mean something else? – Rob Napier Oct 30 '15 at 21:11
  • Maybe I don't need the as String cast in the s{} property. Seems like I did at some point but don't seem to be able to recreate now. – MindSpiker Oct 30 '15 at 21:22
  • I add the s {} property regularly to enums because it is shorter and cleaner than rawValue. I don't need to add it to every kind of enum just ones of type String. – MindSpiker Oct 30 '15 at 21:24

1 Answers1

103

You want to add a property to all enums whose raw value is a string? This sounds like a case for constrained protocol extensions!

extension RawRepresentable where RawValue == String {
    var description: String {
        return rawValue
    }
}

This works because all enums with a raw value automatically conform to the RawRepresentable protocol, and said protocol has an associated type RawValue that tells you which type the raw value is.

Now your Animals enum will automatically inherit it:

print(Animals.descCat.description) // -> "I has attitude"

Notice that string enums are themselves already CustomStringConvertible, so they already have a description property (that returns the name of the enum case), and yours doesn't override it:

print(Animals.descCat) // -> "descCat"

If you want your description to override the default, just add a declaration of CustomStringConvertible conformance to your enum:

private enum Animals: String, CustomStringConvertible { /*...*/ }
print(Animals.descCat) // -> "I has attitude"

You can also extend this idea to cover other raw value types. For example:

extension RawRepresentable where RawValue: CustomStringConvertible {
    var description: String {
        return rawValue.description
    }
}

Now, you can get automatic descriptions for enums whose raw value is Int or even a custom type (so long as that type has a description of its own).

rickster
  • 124,678
  • 26
  • 272
  • 326
  • better than my answer, got my vote sir! Did not think about that. However even if the rawValue isn't a `String` you can still convert a lot of types to `String` – R Menke Oct 30 '15 at 21:37
  • 1
    Good point. I edited my answer to extended the extensions further. (Yo dawg...) – rickster Oct 30 '15 at 21:45
  • Thanks for this awesome answer. Regarding overriding CustomStringConvertable: print(Animals.descCat) works but assigning to a string variable or passing as an arguement fails e.g.: let fooString: String = Animals.descCat gives a conversion error. My solution was to add the s {} property – MindSpiker Oct 30 '15 at 22:14
  • Right -- `description` is for automatic "use it like a string contextually" cases, like `print`, string interpolation, and the debugger/playground. (Also, I started writing my answer about `description` because your question had that in it before you edited it. :) If you want to extend `RawRepresentable` to add a new property, you can still make use of the type constraints and/or call-through to `rawValue.description` for that. – rickster Oct 30 '15 at 22:21
  • The first solution worked but the version added to the answer yielded error: Type 'RawValue' (aka 'String') does not conform to protocol 'CustomStringConvertible' – MindSpiker Oct 30 '15 at 22:27
  • @MindSpiker: Sorry I wasn't clear on that -- `String` does not itself conform to `CustomStringConvertible`, so you have to use the original solution for `String` enums and the other for otherwise-string-representable enums. – rickster Oct 30 '15 at 22:42
  • @rickster When compiling using top initial solution I get error : type 'RawValue' constrained to non-protocol type 'String' to test i started a new project and tried the same solution and was able to compile no problem??? So the solution works but not for my project. Any ideas? – MindSpiker Oct 30 '15 at 23:01
  • Brilliant. Thank you, rickster – John Rogers May 23 '17 at 01:00