148

I have the following enum.

enum EstimateItemStatus: Printable {
    case Pending
    case OnHold
    case Done

    var description: String {
        switch self {
        case .Pending: return "Pending"
        case .OnHold: return "On Hold"
        case .Done: return "Done"
        }
    }

    init?(id : Int) {
        switch id {
        case 1:
            self = .Pending
        case 2:
            self = .OnHold
        case 3:
            self = .Done
        default:
            return nil
        }
    }
}

I need to get all the raw values as an array of strings (like so ["Pending", "On Hold", "Done"]).

I added this method to the enum.

func toArray() -> [String] {
    var n = 1
    return Array(
        GeneratorOf<EstimateItemStatus> {
            return EstimateItemStatus(id: n++)!.description
        }
    )
}

But I'm getting the following error.

Cannot find an initializer for type 'GeneratorOf' that accepts an argument list of type '(() -> _)'

Is there is an easier, better or more elegant way to do this?

Isuru
  • 30,617
  • 60
  • 187
  • 303
  • 2
    you can create array like let array : [EstimateItemStatus] = [.Pending, .Onhold, .Done] – Kristijan Delivuk Oct 05 '15 at 15:41
  • 1
    @KristijanDelivuk I want to add this functionality to the enum itself. So I don't have to go and add it everywhere in other places of the codebases if I ever add another value to the enum. – Isuru Oct 05 '15 at 15:47
  • Possible duplicate of [How to enumerate an enum with String type?](http://stackoverflow.com/questions/24007461/how-to-enumerate-an-enum-with-string-type) – mmmmmm May 01 '17 at 00:19
  • I have an answer you can refer to here http://stackoverflow.com/a/48960126/5372480 – MkSMC May 16 '18 at 20:01

13 Answers13

237

For Swift 4.2 (Xcode 10) and later

There's a CaseIterable protocol:

enum EstimateItemStatus: String, CaseIterable {
    case pending = "Pending"
    case onHold = "OnHold"
    case done = "Done"

    init?(id : Int) {
        switch id {
        case 1: self = .pending
        case 2: self = .onHold
        case 3: self = .done
        default: return nil
        }
    }
}

for value in EstimateItemStatus.allCases {
    print(value)
}

For Swift < 4.2

No, you can't query an enum for what values it contains. See this article. You have to define an array that list all the values you have. Also check out Frank Valbuena's solution in "How to get all enum values as an array".

enum EstimateItemStatus: String {
    case Pending = "Pending"
    case OnHold = "OnHold"
    case Done = "Done"

    static let allValues = [Pending, OnHold, Done]

    init?(id : Int) {
        switch id {
        case 1:
            self = .Pending
        case 2:
            self = .OnHold
        case 3:
            self = .Done
        default:
            return nil
        }
    }
}

for value in EstimateItemStatus.allValues {
    print(value)
}
the Tin Man
  • 158,662
  • 42
  • 215
  • 303
Code Different
  • 90,614
  • 16
  • 144
  • 163
90

Swift 5

Add CaseIterable protocol to enum:

enum EstimateItemStatus: String, CaseIterable {
    case pending = "Pending"
    case onHold = "OnHold"
    case done = "Done"
}

Usage:

let values: [String] = EstimateItemStatus.allCases.map { $0.rawValue }
//["Pending", "OnHold", "Done"]
maxwell
  • 3,788
  • 6
  • 26
  • 40
  • 4
    +1. Should be the accepted answer post Swift 5. You can also do `static let values: [String] ...` inside the enum to make it nicer. – Tarun Uday Sep 08 '22 at 20:56
51

Swift 4.2 introduces a new protocol named CaseIterable

enum Fruit : CaseIterable {
    case apple , apricot , orange, lemon
}

that when you conforms to , you can get an array from the enum cases like this

for fruit in Fruit.allCases {
    print("I like eating \(fruit).")
}
Shehata Gamal
  • 98,760
  • 8
  • 65
  • 87
23
enum EstimateItemStatus: String, CaseIterable {
  case pending = "Pending"
  case onHold = "OnHold"
  case done = "Done"

  static var statusList: [String] {
    return EstimateItemStatus.allCases.map { $0.rawValue }
  }
}

["Pending", "OnHold", "Done"]

17

There's another way that at least is safe at compile time:

enum MyEnum {
    case case1
    case case2
    case case3
}

extension MyEnum {
    static var allValues: [MyEnum] {
        var allValues: [MyEnum] = []
        switch (MyEnum.case1) {
        case .case1: allValues.append(.case1); fallthrough
        case .case2: allValues.append(.case2); fallthrough
        case .case3: allValues.append(.case3)
        }
        return allValues
    }
}

Notice that this works for any enum type (RawRepresentable or not) and also if you add a new case then you will get a compiler error which is good since will force you to have this up to date.

Frank Valbuena
  • 179
  • 1
  • 3
  • 2
    Unorthodox, but it works and it warns you if you modify the enum cases. Clever solution! – Chuck Krutsinger Apr 12 '18 at 21:13
  • I've never seen like this way. It looks very interesting. But I don't understand some of your codes. How can you put MyEnum.case1 to switch condition? and how did you avoid the error `Switch condition evaluates to a constant`? – doori Feb 23 '22 at 06:16
13

To get a list for functional purposes, use the expression EnumName.allCases which returns an array e.g.

EnumName.allCases.map{$0.rawValue} 

will give you a list of Strings given that EnumName: String, CaseIterable

Note: use allCases instead of AllCases().

Archy Will He 何魏奇
  • 9,589
  • 4
  • 34
  • 50
12

I found somewhere this code:

protocol EnumCollection : Hashable {}


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

Use:

enum YourEnum: EnumCollection { //code }

YourEnum.cases()

return list of cases from YourEnum

Daniel Kuta
  • 1,634
  • 15
  • 24
  • Seems like a great solution but has quite a few compile errors on Swift 4. – Isuru Sep 28 '17 at 03:58
  • 1
    The "somewhere" may be: https://theswiftdev.com/2017/10/12/swift-enum-all-values/ (among others?). The blogger credits [CoreKit](https://github.com/corekit/corekit). – AmitaiB Apr 10 '18 at 21:14
  • 5
    This breaks in XCode 10 (regardless of Swift-version) as the hashValue of an enum no longer incremental but random, breaking the mechanism. The new way to do this is to upgrade to Swift 4.2 and use CaseIterable – Yasper Sep 20 '18 at 12:48
3

Update for Swift 5

Easiest solution I've found is to use .allCases on an enum that extends CaseIterable

enum EstimateItemStatus: CaseIterable {
    case Pending
    case OnHold
    case Done

    var description: String {
        switch self {
        case .Pending: return "Pending"
        case .OnHold: return "On Hold"
        case .Done: return "Done"
        }
    }

    init?(id : Int) {
        switch id {
        case 1:
            self = .Pending
        case 2:
            self = .OnHold
        case 3:
            self = .Done
        default:
            return nil
        }
    }
}

.allCases on any CaseIterable enum will return a Collection of that element.

var myEnumArray = EstimateItemStatus.allCases

more info about CaseIterable

Christopher Larsen
  • 1,375
  • 15
  • 22
2

You Can Use

enum Status: Int{
    case a
    case b
    case c

}

extension RawRepresentable where Self.RawValue == Int {

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


Status.values.forEach { (st) in
    print(st)
}
Carlos Chaguendo
  • 2,895
  • 19
  • 24
  • Nice! After upgrading from Swift 3.2 to 4.1 this was a solution that I used. We originally had AnyItertor declarations. Your solution was much cleaner and easier to read. thanks! – Nick N Nov 23 '18 at 17:26
  • 3
    One code flaw here though. It misses the first item in the case. Change var index = 1 to var index = 0 – Nick N Nov 26 '18 at 18:28
1

For Swift 2

// Found http://stackoverflow.com/questions/24007461/how-to-enumerate-an-enum-with-string-type
func iterateEnum<T where T: Hashable, T: RawRepresentable>(_: T.Type) -> AnyGenerator<T> {
    var i = 0
    return AnyGenerator {
        let next = withUnsafePointer(&i) {
            UnsafePointer<T>($0).memory
        }
        if next.hashValue == i {
            i += 1
            return next
        } else {
            return nil
        }
    }
}

func arrayEnum<T where T: Hashable, T: RawRepresentable>(type: T.Type) -> [T]{
    return Array(iterateEnum(type))
}

To use it:

arrayEnum(MyEnumClass.self)
1

After inspiration from Sequence and hours of try n errors. I finally got this comfortable and beautiful Swift 4 way on Xcode 9.1:

protocol EnumSequenceElement: Strideable {
    var rawValue: Int { get }
    init?(rawValue: Int)
}

extension EnumSequenceElement {
    func distance(to other: Self) -> Int {
        return other.rawValue - rawValue
    }

    func advanced(by n: Int) -> Self {
        return Self(rawValue: n + rawValue) ?? self
    }
}

struct EnumSequence<T: EnumSequenceElement>: Sequence, IteratorProtocol {
    typealias Element = T

    var current: Element? = T.init(rawValue: 0)

    mutating func next() -> Element? {
        defer {
            if let current = current {
                self.current = T.init(rawValue: current.rawValue + 1)
            }
        }
        return current
    }
}

Usage:

enum EstimateItemStatus: Int, EnumSequenceElement, CustomStringConvertible {
    case Pending
    case OnHold
    case Done

    var description: String {
        switch self {
        case .Pending:
            return "Pending"
        case .OnHold:
            return "On Hold"
        case .Done:
            return "Done"
        }
    }
}

for status in EnumSequence<EstimateItemStatus>() {
    print(status)
}
// Or by countable range iteration
for status: EstimateItemStatus in .Pending ... .Done {
    print(status)
}

Output:

Pending
On Hold
Done
mclam
  • 51
  • 3
1

Extension on an enum to create allValues.

extension RawRepresentable where Self: CaseIterable {
      static var allValues: [Self.RawValue] {
        return self.allCases.map { $0.rawValue}
      }
    }
garg
  • 2,651
  • 1
  • 24
  • 21
  • While this code may provide a solution to OP's problem, it is highly recommended that you provide additional context regarding why and/or how this code answers the question. Code only answers typically become useless in the long-run because future viewers experiencing similar problems cannot understand the reasoning behind the solution. – E. Zeytinci Feb 19 '20 at 08:20
0

If your enum is incremental and associated with numbers, you can use range of numbers that you map to enum values, like so:

// Swift 3
enum EstimateItemStatus: Int {
    case pending = 1,
    onHold
    done
}

let estimateItemStatusValues: [EstimateItemStatus?] = (EstimateItemStatus.pending.rawValue...EstimateItemStatus.done.rawValue).map { EstimateItemStatus(rawValue: $0) }

This doesn't quite work with enums associated with strings or anything other than numbers, but it works great if that is the case!

Ben Patch
  • 1,213
  • 1
  • 9
  • 12