How to get all enum values as an array
IosArraysSwiftEnumsIos Problem Overview
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
Is there is an easier, better or more elegant way to do this?
Ios Solutions
Solution 1 - Ios
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 "https://stackoverflow.com/questions/32952248/how-to-get-all-enum-values-as-an-array/46853256#46853256";.
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)
}
Solution 2 - Ios
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"]
Solution 3 - Ios
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).")
}
Solution 4 - Ios
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"]
Solution 5 - Ios
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.
Solution 6 - Ios
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()
.
Solution 7 - Ios
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
Solution 8 - Ios
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
Solution 9 - Ios
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)
}
Solution 10 - Ios
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)
Solution 11 - Ios
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
Solution 12 - Ios
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!
Solution 13 - Ios
Extension on an enum to create allValues.
extension RawRepresentable where Self: CaseIterable {
static var allValues: [Self.RawValue] {
return self.allCases.map { $0.rawValue}
}
}