Question or problem with Swift language programming:
enum Suit: String { case spades = "♠" case hearts = "♥" case diamonds = "♦" case clubs = "♣" }
For example, how can I do something like:
for suit in Suit { // do something with suit print(suit.rawValue) }
Resulting example:
♠ ♥ ♦ ♣
How to solve the problem:
Solution 1:
Swift 4.2+
Starting with Swift 4.2 (with Xcode 10), just add protocol conformance to CaseIterable
to benefit from allCases
. To add this protocol conformance, you simply need to write somewhere:
extension Suit: CaseIterable {}
If the enum is your own, you may specify the conformance directly in the declaration:
enum Suit: String, CaseIterable { case spades = "♠"; case hearts = "♥"; case diamonds = "♦"; case clubs = "♣" }
Then the following code will print all possible values:
Suit.allCases.forEach { print($0.rawValue) }
Compatibility with earlier Swift versions (3.x and 4.x)
If you need to support Swift 3.x or 4.0, you may mimic the Swift 4.2 implementation by adding the following code:
#if !swift(>=4.2) public protocol CaseIterable { associatedtype AllCases: Collection where AllCases.Element == Self static var allCases: AllCases { get } } extension CaseIterable where Self: Hashable { static var allCases: [Self] { return [Self](AnySequence { () -> AnyIterator in var raw = 0 var first: Self? return AnyIterator { let current = withUnsafeBytes(of: &raw) { $0.load(as: Self.self) } if raw == 0 { first = current } else if current == first { return nil } raw += 1 return current } }) } } #endif
Solution 2:
This post is relevant here https://www.swift-studies.com/blog/2014/6/10/enumerating-enums-in-swift
Essentially the proposed solution is
enum ProductCategory : String { case Washers = "washers", Dryers = "dryers", Toasters = "toasters" static let allValues = [Washers, Dryers, Toasters] } for category in ProductCategory.allValues{ //Do something }
Solution 3:
I made a utility function iterateEnum()
for iterating cases for arbitrary enum
types.
Here is the example usage:
enum Suit: String { case Spades = "♠" case Hearts = "♥" case Diamonds = "♦" case Clubs = "♣" } for f in iterateEnum(Suit) { println(f.rawValue) }
Which outputs:
♠ ♥ ♦ ♣
But, this is only for debug or test purposes: This relies on several undocumented Swift1.1 compiler behaviors, so, use it at your own risk.
Here is the code:
func iterateEnum(_: T.Type) -> GeneratorOf { var cast: (Int -> T)! switch sizeof(T) { case 0: return GeneratorOf(GeneratorOfOne(unsafeBitCast((), T.self))) case 1: cast = { unsafeBitCast(UInt8(truncatingBitPattern: $0), T.self) } case 2: cast = { unsafeBitCast(UInt16(truncatingBitPattern: $0), T.self) } case 4: cast = { unsafeBitCast(UInt32(truncatingBitPattern: $0), T.self) } case 8: cast = { unsafeBitCast(UInt64($0), T.self) } default: fatalError("cannot be here") } var i = 0 return GeneratorOf { let next = cast(i) return next.hashValue == i++ ? next : nil } }
The underlying idea is:
- Memory representation of
enum
, excludingenum
s with associated types, is just an index of cases when the count of the cases is2...256
, it’s identical toUInt8
, when257...65536
, it’sUInt16
and so on. So, it can beunsafeBitcast
from corresponding unsigned integer types. .hashValue
of enum values is the same as the index of the case..hashValue
of enum values bitcasted from invalid index is0
.
Revised for Swift2 and implemented casting ideas from @Kametrixom’s answer:
func iterateEnum(_: T.Type) -> AnyGenerator { var i = 0 return anyGenerator { let next = withUnsafePointer(&i) { UnsafePointer($0).memory } return next.hashValue == i++ ? next : nil } }
Revised for Swift3:
func iterateEnum(_: T.Type) -> AnyIterator { var i = 0 return AnyIterator { let next = withUnsafePointer(to: &i) { $0.withMemoryRebound(to: T.self, capacity: 1) { $0.pointee } } if next.hashValue != i { return nil } i += 1 return next } }
Revised for Swift3.0.1:
func iterateEnum(_: T.Type) -> AnyIterator { var i = 0 return AnyIterator { let next = withUnsafeBytes(of: &i) { $0.load(as: T.self) } if next.hashValue != i { return nil } i += 1 return next } }
Solution 4:
The other solutions work but they all make assumptions of for example the number of possible ranks and suits, or what the first and last rank may be. True, the layout of a deck of cards probably isn’t going to change much in the foreseeable future. In general, however, it’s neater to write code which makes as little assumptions as possible. My solution:
I’ve added a raw type to the Suit
enum, so I can use Suit(rawValue:)
to access the Suit
cases:
enum Suit: Int { case Spades = 1 case Hearts, Diamonds, Clubs func simpleDescription() -> String { switch self { case .Spades: return "spades" case .Hearts: return "hearts" case .Diamonds: return "diamonds" case .Clubs: return "clubs" } } func color() -> String { switch self { case .Spades: return "black" case .Clubs: return "black" case .Diamonds: return "red" case .Hearts: return "red" } } } enum Rank: Int { case Ace = 1 case Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten case Jack, Queen, King func simpleDescription() -> String { switch self { case .Ace: return "ace" case .Jack: return "jack" case .Queen: return "queen" case .King: return "king" default: return String(self.rawValue) } } }
Below the implementation of Card’s createDeck()
method. init(rawValue:)
is a failable initializer and returns an optional. By unwrapping and checking its value in both while statements, there’s no need to assume the number of Rank
or Suit
cases:
struct Card { var rank: Rank var suit: Suit func simpleDescription() -> String { return "The \(rank.simpleDescription()) of \(suit.simpleDescription())" } func createDeck() -> [Card] { var n = 1 var deck = [Card]() while let rank = Rank(rawValue: n) { var m = 1 while let suit = Suit(rawValue: m) { deck.append(Card(rank: rank, suit: suit)) m += 1 } n += 1 } return deck } }
Here is how to call the createDeck
method:
let card = Card(rank: Rank.Ace, suit: Suit.Clubs) let deck = card.createDeck()
Solution 5:
I stumbled around in the bits and bytes and created an extension that I later found out works very similar to @rintaro‘s answer. It’s used like this:
enum E : EnumCollection { case A, B, C } Array(E.cases()) // [A, B, C]
What’s remarkable is that it’s usable on any enum without associated values. Note that this doesn’t work for enums that have no cases.
As with @rintaro‘s answer, this code uses the underlying representation of an enum. This representation isn’t documented and might change in the future, which would break it. I don’t recommend the usage of this in production.
Code (Swift 2.2, Xcode 7.3.1, not working on Xcode 10):
protocol EnumCollection : Hashable {} extension EnumCollection { static func cases() -> AnySequence { typealias S = Self return AnySequence { () -> AnyGeneratorin var raw = 0 return AnyGenerator { let current : Self = withUnsafePointer(&raw) { UnsafePointer($0).memory } guard current.hashValue == raw else { return nil } raw += 1 return current } } } }
Code (Swift 3, Xcode 8.1, not working on Xcode 10):
protocol EnumCollection : Hashable {} extension EnumCollection { static func cases() -> AnySequence { typealias S = Self return AnySequence { () -> AnyIteratorin 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 } } } }
I have no idea why I need typealias
, but the compiler complains without it.