简体   繁体   中英

Enum class in swift

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.

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, eg

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 ++ , eg 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.

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]
}

If your rawValue s are sequential Int s, 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]

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

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) }

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
        }
    }
}

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