4

Im trying to create an enum class with a type and a function that returns all the types.

Initially I have my enum in my object class and a function that returns these as an array:

class someClass: Mappable {

enum Type: Int {
    case A = 0
    case B = 1
    case C = 2
        }
    }
}

func getAllTypes() -> [Type] {
    return [Type.A, Type.B, Type.C]
}
}

The reason i want to extract this out of my object class is because this type is also used in other classes and i don't want to duplicate any unnecessary code.

I can manage to subclass the enum but not the function that returns all the types in an array.

Any help will be appreciated.

1
  • you might probably need to rename your enum as the Type is reserved for something else in a Swift class; and the class's name should be capitalised as well, like SomeClass. Commented Aug 11, 2016 at 11:43

6 Answers 6

4

Initialize all using an executed-one-time-only closure and AnyGenerator

@vacawama showed how to initialize the all array in a generalized manner using an executed-one-time-only closure in combination with the failable init(rawValue:) initialer of enum's. A slight variation of the method in the fore mentioned answer is to exchange the explicit while loop and array appending with an AnyGenerator:

enum Type: Int {
    case A = 0, B, C, D, E, F, G, H

    static let all: [Type] = {
        var rValue = 0
        return AnyGenerator<Type> {
            defer { rValue += 1 }
            return Type(rawValue: rValue)
        }.map{$0}
    }()
}

print(Type.all) 
    // [Type.A, Type.B, Type.C, Type.D, Type.E, Type.F, Type.G, Type.H]

This works under the condition that rawValue's are simply ++ increasing (0, 1, 2, ...).


Initialize all by conforming the enum to SequenceType

As another alternative, since your rawValue's are integer-wise sequential (++), you could let your enum conform to SequenceType, in which case the array of all cases can be easily generated simply by using .map

enum Type: Int, SequenceType {
    case A = 0, B, C, D, E, F, G, H
    
    init() { self = A } // Default (first) case intializer
    
    /* conform Type to SequenceType (here: enables only 
       simple (self.rawValue)...last traversing) */
    func generate() -> AnyGenerator<Type> {
        var rValue = self.rawValue
        return AnyGenerator {
            defer { rValue += 1 }
            return Type(rawValue: rValue)
        }
    }
    
    /* use the fact that Type conforms to SequenceType to neatly
     initialize your static all array */
     static let all = Type().map { $0 }
}

print(Type.all) 
    // [Type.A, Type.B, Type.C, Type.D, Type.E, Type.F, Type.G, Type.H]

Applying this conformance only to initialize the static all array is possibly overkill, but a plausible alternative in case you'd like to be able to able to use other aspects of your enum conforming to SequenceType

for typeCase in Type() {
    // treat each type ...
    print(typeCase, terminator: " ")
} // A B C D E F G H

for caseFromEAndForward in Type.E {
    print(caseFromEAndForward, terminator: " ")
} // E F G H

Using flatMap to initialize cases based on a range of rawValue's

Finally, as a variation of @appzYourLife neat solution, in case you have very many cases, you can use a flatMap operation on the range of rawValue's to initialize the static allValues array, e.g.

enum Type: Int {
    case A = 0, B, C, D, E, F, G, H
    static let all = (A.rawValue...H.rawValue)
       .flatMap{ Type(rawValue: $0) }
}

print(Type.all) 
    // [Type.A, Type.B, Type.C, Type.D, Type.E, Type.F, Type.G, Type.H]

There's, however, not really any practical use for this method as the general size methods (@vacawama:s or the SequenceType above) would always be preferable for enums of many cases. The possibly use case I can see for this is if the rawValue's of the different cases are sequential but not simply by ++, e.g. if the rawValue's are used for some bitmasking

// ...
case A = 0, B = 2, C = 4, D = 8, E = 16, F = 32, G = 64, H = 128

The method above would use a method of brute-forcing through the range of rawValue's, where a minority will actually correspond to actual cases. Somewhat wasteful, but since it's a one-time static initialization, that shouldn't really be an issue.

Sign up to request clarification or add additional context in comments.

4 Comments

Nice solution. I changed var into let since the all array won't change after compilation. I hope someday Swift will provide an automatic way to retrieve all the values into an enum.
@appzYourLife Ah yes, will update here as well, thanks! In this context, I believe using the explicit cases as in your solution is way better though (since we have them all in the definition above anyway :) ), but if we could retrieve the rawValue range of an enum, then perhaps the flatMap solution could be more useful (or, in another context: perhaps if we need this in an external function outside of the enum definition).
@dfri Your solution is also viable. Thanks. Altho i used the above as I don't have many types :)
In your flapMap example, you could use the range (A.rawValue...H.rawValue) to eliminate the magic numbers.
3

Why don't you simply add a static property to your enum Type?

enum Type: Int {
    case A = 0, B, C
    static let all = [A, B, C]
}

2 Comments

Since within the enum definition, you can omit . for all cases in the array (i.e., ... = [A, B, C]).
This is the one i have used. Thanks @appzYourLife
2

If your rawValues are sequential Ints, you can take advantage of the fact that the Type(rawValue:) initializer will fail and return nil for illegal values to compute Type.all automatically without having to explicitly list the values or set the range. If you add more cases to your enum, it will automatically adjust:

enum Type: Int {
    case A, B, C, D, E, F, G, H, I, J, K
    static let all: [Type] = {
        var all: [Type] = []
        var value = 0
        while let e = Type(rawValue: value) {
            all.append(e)
            value += 1
        }
        return all
    }()
}

print(Type.all)
[Type.A, Type.B, Type.C, Type.D, Type.E, Type.F, Type.G, Type.H, Type.I, Type.J, Type.K]

3 Comments

Neat! Might be worth pointing out (to future readers of this thread) that this (as well as my own solution) works under the condition the rawValue:s are simply ++ sequential (e.g. no bitmasking 1, 2, 4, ... or similar rawValues).
I wont include this in my own answer since cleverly using a executed-one-time-only closure was the idea of this answer, but note that also here you may alternatively make use of AnyGenerator for a (possibly) slightly more neat version of the above.
@dfri, go ahead and use the executed-one-time-only closure for your answer. The goal is to produce the best answer, and iterating on other's answers frequently produces better answers.
2

Swift 5

Now you have CaseIterable. Apple docs. Another topic.

Types that conform to the CaseIterable protocol are typically enumerations without associated values. When using a CaseIterable type, you can access a collection of all of the type’s cases by using the type’s allCases property.

enum Direction: CaseIterable {
  case left
  case right
  case up
  case down
}

Direction.allCases.count
Direction.allCases.forEach { print($0) }

Comments

1

You can define a static array that contains all possible values of enum:

static let allValues = [A, B, C]

Then you can use it where you want as :

var allEnumValues = Type.allValues

Comments

0

If it is used in other classes as well then don't put it inside a class

class YourClass : Mappable {

}

enum Type: Int {
    case A = 0
    case B = 1
    case C = 2
        }
    }
}

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.