18. Swift gist: generic allValues for enums

The basic problem is that you can not get all the available values of an enum type in Swift. There are multiple solutions, and in this post I'll show you all of them.

The first one is the most straightforward, but it works only with integer based enums.

public protocol EnumCollection: RawRepresentable {  
    static var allValues: [Self] { get }
}

extension EnumCollection where RawValue: Integer {

    public static var allValues: [Self] {
        var values: [Self] = []
        var index: Self.RawValue = 0
        let increment: Self.RawValue = 1
        while let element = self.init(rawValue: index) {
            values.append(element)
            index += increment
        }
        return values
    }
}

The second one is just a variation of the first solution. Actually it's just using an AnyIterator instead of the while loop.

protocol EnumCollection: RawRepresentable {  
    static var allValues: [Self] { get }
}

extension EnumCollection where RawValue: Integer {  
    static var allValues: [Self] {
        var index: Self.RawValue = 0
        let increment: Self.RawValue = 1

        return Array(AnyIterator {
            let id: Self.RawValue = index
            index += increment
            return Self(rawValue: id)
        })
    }
}

Ok, we made it for Integers, but what about the String based enums? Let's solve this in an even more generic way... with memory addresses & pointers.

protocol EnumCollection: Hashable {  
    static var allValues: [Self] { get }
}

extension EnumCollection {

    static func cases() -> AnySequence<Self> {
        typealias S = Self
        return AnySequence { () -> AnyIterator<S> in
            var raw = 0
            return AnyIterator {
                let current : Self = withUnsafePointer(to: &raw) { $0.withMemoryRebound(to: S.self, capacity: 1) { $0.pointee } }
                guard current.hashValue == raw else { return nil }
                raw += 1
                return current
            }
        }
    }

    static var allValues: [Self] {
        return Array(self.cases())
    }
}

Now the base type of the enum needs to be Hashable, but that's not a big deal. From now on your enums will have the allValues property if you define them like this:

enum Alpha: String, EnumCollection {  
    case a, b, c, d, e
}

Alpha.allValues

Sources