33

I have an enum in C and the index needs to be represented by a String.

How can a Swift enum of String type be used by integer index?

I would like to copy the enum to Swift, set the type to string and define all of the raw values to display text, and then use the C enum value to extract the raw value text for the Swift String enum.

Otherwise I will just create an array of strings.. But the enum would be more usable.

4 Answers 4

54

Swift 4.2 introduced CaseIterable which does exactly the same thing without the need to declare an allValues array. It works like this:

enum MyEnum: String, CaseIterable {
    case foo = "fooString"
    case bar = "barString"
    case baz = "bazString"
}

and you can access its values by

MyEnum.allCases

or a value at a specific index by

MyEnum.allCases[index]
Sign up to request clarification or add additional context in comments.

Comments

37

In Swift, enum types do not hold its index info of cases (at least, not provided for programmers).

So:

How can a Swift enum of String type be used by integer index?

The answer is "You cannot".


You can bind Int (or enum cases) and String values in many ways other than just create an array of strings..

For example, if your bound Strings can be the same as case labels, you can write something like this:

enum MyEnum: Int {
    case foo
    case bar
    case baz

    var string: String {
        return String(self)
    }
}

if let value = MyEnum(rawValue: 0) {
    print(value.string) //->foo
}

If your Strings need to be a little more complex to display text, you can use Swift Dictionary to bind enum cases and Strings.

enum AnotherEnum: Int {
    case foo
    case bar
    case baz

    static let mapper: [AnotherEnum: String] = [
        .foo: "FooString",
        .bar: "BarString",
        .baz: "BazString"
    ]
    var string: String {
        return AnotherEnum.mapper[self]!
    }
}

if let value = AnotherEnum(rawValue: 1) {
    print(value.string) //->BarString
}

A little bit more readable than a simple array of strings.

1 Comment

First solution doesn't work, I have: Cannot invoke initializer for type 'String' with an argument list of type '(MyEnum)'
18

Simple workaround which is also useful if you want to enumerate a string enum.

enum MyEnum: String {
    case foo = "fooString"
    case bar = "barString"
    case baz = "bazString"

    static let allValues = [foo, bar, baz] //must maintain second copy of values
}

//enumeration advantage
for value in MyEnum.allValues {
    print(value)
}

//get value by index
let value = MyEnum.allValues[1] 

print(value) //barString

1 Comment

utterly brilliant, thank you. I suspect this will make it into the language itself. Unless of course there is a pre-existing methodology to access the same..
5

You can add an index as a part of the enum.

enum StringEnum: String, CaseIterable {
   case pawn, rook, knight, bishop, king, queen

   var name: String { self.rawValue.uppercased() }
   var index: Int { StringEnum.allCases.firstIndex(of: self) ?? 0 }
}

And find enum cases by index with the function:

func findEnum(by index: Int) -> StringEnum? {
   StringEnum.allCases.first(where: { $0.index == index })
}

2 Comments

Cool suggestion, but the construct allCases.firstIndex(of:) might have O(n) performance, leading to n^2 performance if you use it in a loop.
Thanks, you're right. That solution is good for compact enums. In complex cases you can access enum as an array by index (with CaseIterable). `func getEnum(by index: Int) -> StringEnum? { guard StringEnum.allCases.count > index else { return nil } return StringEnum.allCases[index] }

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.