如何枚举一个枚举字符串类型?

enum Suit: String {
case spades = "♠"
case hearts = "♥"
case diamonds = "♦"
case clubs = "♣"
}

例如,我该怎么做:

for suit in Suit {
// do something with suit
print(suit.rawValue)
}

例如:

♠
♥
♦
♣
296653 次浏览

在Swift上,enum类型可以像EnumType.Case一样访问:

# EYZ0

大多数情况下,只有当您有几个选项可以使用,并且确切地知道在每个选项上要做什么的时候,您才会使用enum类型。

在处理enum类型时,使用for-in结构没有太大意义。

你可以这样做,例如:

func sumNumbers(numbers : Int...) -> Int {
var sum = 0


for number in numbers{
sum += number
}


return sum
}

枚举有toRaw()fromRaw()方法。所以如果你的原始值是Int,你可以从第一个迭代到最后一个enum:

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


for i in Suit.Spades.toRaw()...Suit.Clubs.toRaw() {
if let covertedSuit = Suit.fromRaw(i) {
let description = covertedSuit.simpleDescription()
}
}

一个问题是,在运行simpleDescription方法之前,您需要测试可选值,因此我们首先将convertedSuit设置为我们的值,然后将一个常量设置为convertedSuit.simpleDescription()

原则上,如果你不为enum的大小写使用原始值赋值,这样做是可能的:

enum RankEnum: Int {
case Ace
case One
case Two
}


class RankEnumGenerator: Generator {
var i = 0
typealias Element = RankEnum
func next() -> Element? {
let r = RankEnum.fromRaw(i)
i += 1
return r
}
}


extension RankEnum {
static func enumerate() -> SequenceOf<RankEnum> {
return SequenceOf<RankEnum>({ RankEnumGenerator() })
}
}


for r in RankEnum.enumerate() {
println("\(r.toRaw())")
}

这看起来像一个黑客,但如果你使用原始值,你可以这样做

enum Suit: Int {
case Spades = 0, Hearts, Diamonds, Clubs
...
}


var suitIndex = 0
while var suit = Suit.fromRaw(suitIndex++) {
...
}

它花了我一点,而不仅仅是像swift书中所要求的结构中的一个方法,但我在enum中设置了下一个函数。我会使用一个协议,我不知道为什么,但有秩设置为int混乱。

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.toRaw())
}
}
mutating func next() -> Rank {
var rank = self
var rawrank = rank.toRaw()
var nrank: Rank = self
rawrank = rawrank + 1
if let newRank = Rank.fromRaw(rawrank) {
println("\(newRank.simpleDescription())")
nrank = newRank
} else {
return self
}
return nrank
}
}


enum Suit {
case Spades, Hearts, Diamonds, Clubs
func color() -> String {
switch self {
case .Spades, .Clubs:
return "black"
default:
return "red"
}
}
func simpleDescription() -> String {
switch self {
case .Spades:
return "spades"
case .Hearts:
return "hearts"
case .Diamonds:
return "diamonds"
case .Clubs:
return "clubs"
}
}
mutating func next() -> Suit {
switch self {
case .Spades:
return Hearts
case .Hearts:
return Diamonds
case .Diamonds:
return Clubs
case .Clubs:
return Spades
}
}
}


struct Card {
var rank: Rank
var suit: Suit
func deck() -> Card[] {
var tRank = self.rank
var tSuit = self.suit
let tcards = 52 // we start from 0
var cards: Card[] = []
for i in 0..tcards {
var card = Card(rank: tRank, suit: tSuit)
cards.append(card)
tRank = tRank.next()
tSuit = tSuit.next()
}
return cards
}
func simpleDescription() -> String {
return "The \(rank.simpleDescription()) of \(suit.simpleDescription())"
}
}


var card = Card(rank: .Ace, suit: .Spades)
var deck = card.deck()

我使用了一些常识,但这可以通过将花色乘以等级来轻松纠正(如果你没有使用标准的桥牌,你必须相应地改变枚举,如果基本上只是通过不同的枚举进行步骤)。为了节省时间,我使用了排名rawValues,如果你愿意,你也可以为套装做同样的事情。然而,这个例子没有,所以我决定在不改变西装的情况下弄清楚

我使用了下面的方法,假设我知道哪个是Rank enum中的最后一个值,所有的排名在Ace之后都有增量值

我喜欢这种方式,因为它干净,小,容易理解

 func cardDeck() -> Card[] {
var cards: Card[] = []
let minRank = Rank.Ace.toRaw()
let maxRank = Rank.King.toRaw()


for rank in minRank...maxRank {
if var convertedRank: Rank = Rank.fromRaw(rank) {
cards.append(Card(rank: convertedRank, suite: Suite.Clubs))
cards.append(Card(rank: convertedRank, suite: Suite.Diamonds))
cards.append(Card(rank: convertedRank, suite: Suite.Hearts))
cards.append(Card(rank: convertedRank, suite: Suite.Spades))
}
}


return cards
}

有一种聪明的方法,尽管令人沮丧,但它说明了两种不同类型的枚举之间的区别。

试试这个:

    func makeDeck() -> Card[] {
var deck: Card[] = []
var suits: Suit[] = [.Hearts, .Diamonds, .Clubs, .Spades]
for i in 1...13 {
for suit in suits {
deck += Card(rank: Rank.fromRaw(i)!, suit: suit)
}
}
return deck
}

交易是,一个由数字(原始值)支持的枚举是隐式显式有序的,而一个没有数字支持的枚举是显式隐式无序的。

< p >。当我们为枚举值提供数字时,该语言足够狡猾,可以计算出数字的顺序。 另一方面,如果我们不给它任何排序,当我们试图迭代这些值时,语言就会举起手来,并执行“是的,但是你想先去哪一个??”

其他可以做到这一点(迭代无序枚举)的语言可能是相同的语言,其中所有内容实际上都是一个地图或字典,你可以迭代地图的键,无论是否有任何逻辑顺序。

诀窍是给它提供一些显式排序的东西,在这个例子中,suit的实例在数组中按照我们想要的顺序。一旦你给了它,霉霉就像“那你为什么不一开始就说呢?”

另一个简写技巧是在fromRaw函数上使用强制操作符。这说明了关于枚举的另一个“陷阱”,即可能传入的值的范围通常大于枚举的范围。例如,如果我们说Rank.fromRaw(60),就不会返回值,所以我们使用语言的可选特性,在我们开始使用可选项的地方,强迫很快就会出现。(或者在我看来还是有点奇怪的如果让结构)

enum Rank: Int
{
case Ace = 0
case Two, Three, Four, Five, Six, Seve, Eight, Nine, Ten
case Jack, Queen, King
case Count
}


enum Suit : Int
{
case Spades = 0
case Hearts, Diamonds, Clubs
case Count
}


struct Card
{
var rank:Rank
var suit:Suit
}


class Test
{
func makeDeck() -> Card[]
{
let suitsCount:Int = Suit.Count.toRaw()
let rankCount:Int = Rank.Count.toRaw()
let repeatedCard:Card = Card(rank:Rank.Ace, suit:Suit.Spades)
let deck:Card[] = Card[](count:suitsCount*rankCount, repeatedValue:repeatedCard)


for i:Int in 0..rankCount
{
for j:Int in 0..suitsCount
{
deck[i*suitsCount+j] = Card(rank: Rank.fromRaw(i)!, suit: Suit.fromRaw(j)!)
}
}
return deck
}
}

根据Rick的回答:这要快5倍

该解决方案在可读性和可维护性之间取得了适当的平衡。

struct Card {


// ...


static func deck() -> Card[] {
var deck = Card[]()
for rank in Rank.Ace.toRaw()...Rank.King.toRaw() {
for suit in [Suit.Spades, .Hearts, .Clubs, .Diamonds] {
let card = Card(rank: Rank.fromRaw(rank)!, suit: suit)
deck.append(card)
}
}
return deck
}
}


let deck = Card.deck()

如果您仍然想为RankSuit使用枚举,这里有一个不那么神秘的例子。如果你想使用for-in loop来遍历每个对象,只需将它们收集到一个数组中。

标准52张牌的例子:

enum Rank: Int {
case Ace = 1, Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten, Jack, Queen, King
func name() -> String {
switch self {
case .Ace:
return "ace"
case .Jack:
return "jack"
case .Queen:
return "queen"
case .King:
return "king"
default:
return String(self.toRaw())
}
}
}


enum Suit: Int {
case Diamonds = 1, Clubs, Hearts, Spades
func name() -> String {
switch self {
case .Diamonds:
return "diamonds"
case .Clubs:
return "clubs"
case .Hearts:
return "hearts"
case .Spades:
return "spades"
default:
return "NOT A VALID SUIT"
}
}
}


let Ranks = [
Rank.Ace,
Rank.Two,
Rank.Three,
Rank.Four,
Rank.Five,
Rank.Six,
Rank.Seven,
Rank.Eight,
Rank.Nine,
Rank.Ten,
Rank.Jack,
Rank.Queen,
Rank.King
]


let Suits = [
Suit.Diamonds,
Suit.Clubs,
Suit.Hearts,
Suit.Spades
]




class Card {
var rank: Rank
var suit: Suit


init(rank: Rank, suit: Suit) {
self.rank = rank
self.suit = suit
}
}


class Deck {
var cards = Card[]()


init() {
for rank in Ranks {
for suit in Suits {
cards.append(Card(rank: rank, suit: suit))
}
}
}
}


var myDeck = Deck()
myDeck.cards.count  // => 52

这篇文章是相关的https://www.swift-studies.com/blog/2014/6/10/enumerating-enums-in-swift

基本上,提议的解决方案是

enum ProductCategory : String {
case Washers = "washers", Dryers = "dryers", Toasters = "toasters"


static let allValues = [Washers, Dryers, Toasters]
}


for category in ProductCategory.allValues{
//Do something
}

其他的解决方案工作但是它们都假设了可能的等级和花色的数量,或者第一和最后的等级是什么。的确,在可预见的未来,一副纸牌的布局可能不会有太大变化。然而,一般来说,编写尽可能少假设的代码会更简洁。我的解决方案:

我已经添加了一个原始类型的Suit enum,所以我可以使用Suit(rawValue:)访问Suit案例:

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

下面是Card的createDeck()方法的实现。init(rawValue:)是一个可失败的初始化式,返回一个可选值。通过在两个while语句中展开并检查它的值,不需要假设RankSuit情况的数量:

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

下面是如何调用createDeck方法:

let card = Card(rank: Rank.Ace, suit: Suit.Clubs)
let deck = card.createDeck()

您可以通过实现ForwardIndexType协议来迭代枚举。

ForwardIndexType协议要求您定义一个successor()函数来逐级遍历元素。

enum Rank: Int, ForwardIndexType {
case Ace = 1
case Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten
case Jack, Queen, King


// ... other functions


// Option 1 - Figure it out by hand
func successor() -> Rank {
switch self {
case .Ace:
return .Two
case .Two:
return .Three


// ... etc.


default:
return .King
}
}


// Option 2 - Define an operator!
func successor() -> Rank {
return self + 1
}
}


// NOTE: The operator is defined OUTSIDE the class
func + (left: Rank, right: Int) -> Rank {
// I'm using to/from raw here, but again, you can use a case statement
// or whatever else you can think of


return left == .King ? .King : Rank(rawValue: left.rawValue + right)!
}

在打开或关闭范围(..<...)上迭代将在内部调用successor()函数,它允许你这样写:

// Under the covers, successor(Rank.King) and successor(Rank.Ace) are called to establish limits
for r in Rank.Ace...Rank.King {
// Do something useful
}
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.toRaw())
}
}
}


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, .Clubs: return "black"
case .Hearts, .Diamonds: return "red"
}
}
}


struct Card {
var rank: Rank
var suit: Suit
func simpleDescription() -> String {
return "The \(rank.simpleDescription()) of \(suit.simpleDescription())"
}


static func createPokers() -> Card[] {
let ranks = Array(Rank.Ace.toRaw()...Rank.King.toRaw())
let suits = Array(Suit.Spades.toRaw()...Suit.Clubs.toRaw())
let cards = suits.reduce(Card[]()) { (tempCards, suit) in
tempCards + ranks.map { rank in
Card(rank: Rank.fromRaw(rank)!, suit: Suit.fromRaw(suit)!)
}
}
return cards
}
}

以下是我的建议。这不是完全令人满意的(我对Swift和OOP很陌生!),但也许有人可以改进它。这个想法是让每个枚举提供自己的范围信息作为.first.last属性。它只向每个枚举添加了两行代码:仍然有点硬编码,但至少它没有复制整个集合。它确实需要将Suit enum修改为像Rank enum一样的Int类型,而不是un类型化。

而不是重复整个解决方案,下面是我添加到.枚举的代码,在case语句之后的某个地方(Suit枚举类似):

var first: Int { return Ace.toRaw() }
var last: Int { return King.toRaw() }

以及我用来将deck构建为String数组的循环。(问题定义没有说明牌组是如何构造的。)

func createDeck() -> [String] {
var deck: [String] = []
var card: String
for r in Rank.Ace.first...Rank.Ace.last {
for s in Suit.Hearts.first...Suit.Hearts.last {
card = Rank.simpleDescription( Rank.fromRaw(r)!)() + " of " + Suit.simpleDescription( Suit.fromRaw(s)!)()
deck.append( card)
}
}
return deck
}

这并不令人满意,因为属性与元素而不是enum相关联。但它确实为“for”循环增加了清晰度。我想说Rank.first而不是Rank.Ace.first。它适用于任何元素,但很难看。有人能演示一下如何将其提升到enum级别吗?

为了使它工作,我从Card结构中提取了createDeck方法。我不知道如何从该结构返回一个[String]数组,这似乎是一个糟糕的地方,把这样的方法无论如何。

有时,您可能会处理具有底层原始整数类型的枚举类型,这种类型在整个软件开发生命周期中都会发生变化。下面是一个很适合这种情况的例子:

public class MyClassThatLoadsTexturesEtc
{
//...


// Colors used for gems and sectors.
public enum Color: Int
{
// Colors arranged in order of the spectrum.
case First = 0
case Red, Orange, Yellow, Green, Blue, Purple, Pink
// --> Add more colors here, between the first and last markers.
case Last
}


//...


public func preloadGems()
{
// Preload all gems.
for i in (Color.First.toRaw() + 1) ..< (Color.Last.toRaw())
{
let color = Color.fromRaw(i)!
loadColoredTextures(forKey: color)
}
}


//...
}

我使用计算属性,它返回所有值的数组(感谢这个帖子http://natecook.com/blog/2014/10/loopy-random-enum-ideas/)。但是,它也使用int原始值,但我不需要在单独的属性中重复枚举的所有成员。

更新 Xcode 6.1改变了一点如何获得枚举成员使用rawValue,所以我修正了列表。还修复了错误的第一个rawValue小错误。

enum ValidSuits: Int {
case Clubs = 0, Spades, Hearts, Diamonds
func description() -> String {
switch self {
case .Clubs:
return "♣︎"
case .Spades:
return "♠︎"
case .Diamonds:
return "♦︎"
case .Hearts:
return "♥︎"
}
}


static var allSuits: [ValidSuits] {
return Array(
SequenceOf {
() -> GeneratorOf<ValidSuits> in
var i=0
return GeneratorOf<ValidSuits> {
return ValidSuits(rawValue: i++)
}
}
)
}
}
实验结果是: 实验< / p >

在Card中添加一个方法,用于创建一副完整的牌,每一副牌都是rank和花色的组合。

因此,除了添加方法之外,没有修改或增强给定的代码(并且没有使用还没有教过的东西),我想出了这个解决方案:

struct Card {
var rank: Rank
var suit: Suit


func simpleDescription() -> String {
return "The \(rank.simpleDescription()) of \(suit.simpleDescription())"
}


func createDeck() -> [Card] {
var deck: [Card] = []
for rank in Rank.Ace.rawValue...Rank.King.rawValue {
for suit in Suit.Spades.rawValue...Suit.Clubs.rawValue {
let card = Card(rank: Rank(rawValue: rank)!, suit: Suit(rawValue: suit)!)
//println(card.simpleDescription())
deck += [card]
}
}
return deck
}
}
let threeOfSpades = Card(rank: .Three, suit: .Spades)
let threeOfSpadesDescription = threeOfSpades.simpleDescription()
let deck = threeOfSpades.createDeck()

我做了一个实用函数iterateEnum(),用于迭代任意enum类型的情况。

下面是示例用法:

enum Suit: String {
case Spades = "♠"
case Hearts = "♥"
case Diamonds = "♦"
case Clubs = "♣"
}


for f in iterateEnum(Suit) {
println(f.rawValue)
}

输出:

♠
♥
♦
♣

但是,这是仅用于调试或测试的目的:它依赖于一些未记录的Swift1.1编译器行为,所以,使用它的风险由你自己承担。

代码如下:

func iterateEnum<T: Hashable>(_: T.Type) -> GeneratorOf<T> {
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
}
}

其基本思想是:

  • enum的内存表示,不包括具有相关类型的enum,只是一个案例的索引,当案例的计数是2...256时,它与UInt8相同,当257...65536时,它是UInt16,以此类推。因此,它可以是对应无符号整数类型的unsafeBitcast
  • enum值的.hashValue与case的索引相同。
  • 无效的索引位转换的enum值.hashValue0

为Swift2修改,并实现了@Kametrixom的回答的选角想法:

func iterateEnum<T: Hashable>(_: T.Type) -> AnyGenerator<T> {
var i = 0
return anyGenerator {
let next = withUnsafePointer(&i) { UnsafePointer<T>($0).memory }
return next.hashValue == i++ ? next : nil
}
}

对Swift3的修订:

func iterateEnum<T: Hashable>(_: T.Type) -> AnyIterator<T> {
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
}
}

针对Swift3.0.1修订:

func iterateEnum<T: Hashable>(_: T.Type) -> AnyIterator<T> {
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
}
}

我的解决方案是声明一个包含所有enum可能性的数组。因此,for循环可以遍历所有这些。

//Function inside struct Card
static func generateFullDeck() -> [Card] {
let allRanks = [Rank.Ace, Rank.Two, Rank.Three, Rank.Four, Rank.Five, Rank.Six, Rank.Seven, Rank.Eight, Rank.Nine, Rank.Ten, Rank.Jack, Rank.Queen, Rank.King]
let allSuits = [Suit.Hearts, Suit.Diamonds, Suit.Clubs, Suit.Spades]
var myFullDeck: [Card] = []


for myRank in allRanks {
for mySuit in allSuits {
myFullDeck.append(Card(rank: myRank, suit: mySuit))
}
}
return myFullDeck
}


//actual use:
let aFullDeck = Card.generateFullDeck()    //Generate the desired full deck


var allDesc: [String] = []
for aCard in aFullDeck {
println(aCard.simpleDescription())    //You'll see all the results in playground
}
enum Rank: Int {
...
static let ranks = (Rank.Ace.rawValue ... Rank.King.rawValue).map{Rank(rawValue: $0)! }


}
enum Suit {
...
static let suits = [Spades, Hearts, Diamonds, Clubs]
}


struct Card {
...
static func fullDesk() -> [Card] {
var desk: [Card] = []
for suit in Suit.suits {
for rank in Rank.ranks {
desk.append(Card(rank: rank,suit: suit))
}
}
return desk
}
}

这个怎么样?

在处理Swift 2.0时,我的建议是:

我已经添加了原始类型Suit enum

enum Suit: Int {

然后:

struct Card {
var rank: Rank
var suit: Suit




func fullDeck()-> [Card] {


var deck = [Card]()


for i in Rank.Ace.rawValue...Rank.King.rawValue {


for j in Suit.Spades.rawValue...Suit.Clubs.rawValue {


deck.append(Card(rank:Rank(rawValue: i)! , suit: Suit(rawValue: j)!))
}
}


return deck
}
}

对不起,我的回答是具体到我如何在我需要做的事情中使用这篇文章。对于那些无意中遇到这个问题的人,寻找一种方法来找到一个enum中的case,这是一种方法(Swift 2中的新功能):

编辑:小写驼峰现在是Swift 3 enum值的标准

// From apple docs: If the raw-value type is specified as String and you don’t assign values to the cases explicitly, each unassigned case is implicitly assigned a string with the same text as the name of that case.


enum Theme: String
{
case white, blue, green, lavender, grey
}


func loadTheme(theme: String)
{
// this checks the string against the raw value of each enum case (note that the check could result in a nil value, since it's an optional, which is why we introduce the if/let block
if let testTheme = Theme(rawValue: theme)
{
// testTheme is guaranteed to have an enum value at this point
self.someOtherFunction(testTheme)
}
}

对于那些对枚举感到疑惑的人来说,本页上给出的答案包括一个包含所有枚举值的数组的静态var/let是正确的。最新的苹果tvOS示例代码包含了完全相同的技术。

也就是说,他们应该在语言中构建一个更方便的机制(苹果,你在听吗?)

如果你给枚举原始Int值,它会使循环更容易。

例如,你可以使用anyGenerator来获得一个可以枚举你的值的生成器:

enum Suit: Int, CustomStringConvertible {
case Spades, Hearts, Diamonds, Clubs
var description: String {
switch self {
case .Spades:   return "Spades"
case .Hearts:   return "Hearts"
case .Diamonds: return "Diamonds"
case .Clubs:    return "Clubs"
}
}
static func enumerate() -> AnyGenerator<Suit> {
var nextIndex = Spades.rawValue
return anyGenerator { Suit(rawValue: nextIndex++) }
}
}
// You can now use it like this:
for suit in Suit.enumerate() {
suit.description
}
// or like this:
let allSuits: [Suit] = Array(Suit.enumerate())

然而,这看起来像一个相当常见的模式,如果我们可以通过简单地遵循协议使任何枚举类型可枚举,这不是很好吗?有了Swift 2.0和协议扩展,现在我们可以了!

简单地添加到你的项目:

protocol EnumerableEnum {
init?(rawValue: Int)
static func firstValue() -> Int
}
extension EnumerableEnum {
static func enumerate() -> AnyGenerator<Self> {
var nextIndex = firstRawValue()
return anyGenerator { Self(rawValue: nextIndex++) }
}
static func firstRawValue() -> Int { return 0 }
}

现在,任何时候你创建一个枚举(只要它有一个Int原始值),你可以通过遵循协议使它可枚举:

enum Rank: Int, EnumerableEnum {
case Ace, Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten, Jack, Queen, King
}
// ...
for rank in Rank.enumerate() { ... }

如果你的枚举值不是以0开头(默认值),重写firstRawValue方法:

enum DeckColor: Int, EnumerableEnum {
case Red = 10, Blue, Black
static func firstRawValue() -> Int { return Red.rawValue }
}
// ...
let colors = Array(DeckColor.enumerate())

最终的Suit类,包括用更标准的CustomStringConvertible协议替换simpleDescription,看起来像这样:

enum Suit: Int, CustomStringConvertible, EnumerableEnum {
case Spades, Hearts, Diamonds, Clubs
var description: String {
switch self {
case .Spades:   return "Spades"
case .Hearts:   return "Hearts"
case .Diamonds: return "Diamonds"
case .Clubs:    return "Clubs"
}
}
}
// ...
for suit in Suit.enumerate() {
print(suit.description)
}

Swift 3语法:

protocol EnumerableEnum {
init?(rawValue: Int)
static func firstRawValue() -> Int
}


extension EnumerableEnum {
static func enumerate() -> AnyIterator<Self> {
var nextIndex = firstRawValue()


let iterator: AnyIterator<Self> = AnyIterator {
defer { nextIndex = nextIndex + 1 }
return Self(rawValue: nextIndex)
}


return iterator
}


static func firstRawValue() -> Int {
return 0
}
}

我在比特和字节中跌跌撞撞,并创建了一个扩展,后来我发现它与@rintaro的答案非常相似。它是这样使用的:

enum E : EnumCollection {
case A, B, C
}


Array(E.cases())    // [A, B, C]

值得注意的是,它可以在任何没有关联值的enum上使用。注意,这对于没有大小写的枚举不起作用。

@rintaro的答案一样,这段代码使用枚举的底层表示。这种表示没有文档化,将来可能会改变,这会破坏它。# EYZ1

代码(Swift 2.2, Xcode 7.3.1,不工作在Xcode 10):

protocol EnumCollection : Hashable {}
extension EnumCollection {
static func cases() -> AnySequence<Self> {
typealias S = Self
return AnySequence { () -> AnyGenerator<S> in
var raw = 0
return AnyGenerator {
let current : Self = withUnsafePointer(&raw) { UnsafePointer($0).memory }
guard current.hashValue == raw else { return nil }
raw += 1
return current
}
}
}
}

代码(Swift 3, Xcode 8.1,不工作在Xcode 10):

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

我不知道为什么我需要typealias,但编译器抱怨没有它。

这个问题现在简单多了。以下是我的Swift 4.2解决方案:

enum Suit: Int, CaseIterable {
case None
case Spade, Heart, Diamond, Club


static let allNonNullCases = Suit.allCases[Spade.rawValue...]
}


enum Rank: Int, CaseIterable {
case Joker
case Two, Three, Four, Five, Six, Seven, Eight
case Nine, Ten, Jack, Queen, King, Ace


static let allNonNullCases = Rank.allCases[Two.rawValue...]
}


func makeDeck(withJoker: Bool = false) -> [Card] {
var deck = [Card]()
for suit in Suit.allNonNullCases {
for rank in Rank.allNonNullCases {
deck.append(Card(suit: suit, rank: rank))
}
}
if withJoker {
deck.append(Card(suit: .None, rank: .Joker))
}
return deck
}

前4.2:

我喜欢这个解决方案,我找到“Swift中的列表理解”后把它放在一起。

它使用Int rawws而不是string,但它避免了键入两次,它允许自定义范围,并且不硬编码原始值。

这是我最初解决方案的Swift 4版本,但请参阅上面的4.2改进:

enum Suit: Int {
case None
case Spade, Heart, Diamond, Club


static let allRawValues = Suit.Spade.rawValue...Suit.Club.rawValue
static let allCases = Array(allRawValues.map{ Suit(rawValue: $0)! })
}
enum Rank: Int {
case Joker
case Two, Three, Four, Five, Six
case Seven, Eight, Nine, Ten
case Jack, Queen, King, Ace


static let allRawValues = Rank.Two.rawValue...Rank.Ace.rawValue
static let allCases = Array(allRawValues.map{ Rank(rawValue: $0)! })
}
func makeDeck(withJoker: Bool = false) -> [Card] {
var deck = [Card]()
for suit in Suit.allCases {
for rank in Rank.allCases {
deck.append(Card(suit: suit, rank: rank))
}
}
if withJoker {
deck.append(Card(suit: .None, rank: .Joker))
}
return deck
}

更新到斯威夫特2.2+

func iterateEnum<T: Hashable>(_: 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
}
}
}

它更新代码到Swift 2.2表单@Kametrixom的回答

对于斯威夫特3.0 +(非常感谢@Philip)

func iterateEnum<T: Hashable>(_: T.Type) -> AnyIterator<T> {
var i = 0
return AnyIterator {
let next = withUnsafePointer(&i) {
UnsafePointer<T>($0).pointee
}
if next.hashValue == i {
i += 1
return next
} else {
return nil
}
}
}
这是一个相当老的帖子,来自Swift 2.0。现在有一些更好的解决方案,使用了swift 3.0的新特性: # EYZ0 < / p > 关于这个问题,有一个解决方案,它使用了Swift 4.2的一个新功能(在我写这篇编辑时尚未发布): # EYZ0 < / p >

在这个帖子中有很多好的解决方案,但其中一些非常复杂。我喜欢尽可能地简化。这里有一个解决方案,可能适用于不同的需求,但我认为它在大多数情况下都很好:

enum Number: String {
case One
case Two
case Three
case Four
case EndIndex


func nextCase () -> Number
{
switch self {
case .One:
return .Two
case .Two:
return .Three
case .Three:
return .Four
case .Four:
return .EndIndex


/*
Add all additional cases above
*/
case .EndIndex:
return .EndIndex
}
}


static var allValues: [String] {
var array: [String] = Array()
var number = Number.One


while number != Number.EndIndex {
array.append(number.rawValue)
number = number.nextCase()
}
return array
}
}

迭代:

for item in Number.allValues {
print("number is: \(item)")
}

与@Kametrixom回答在这里一样,我相信返回一个数组会比返回AnySequence更好,因为你可以访问数组的所有好东西,如计数等。

以下是改写后的内容:

public protocol EnumCollection : Hashable {}
extension EnumCollection {
public static func allValues() -> [Self] {
typealias S = Self
let retVal = AnySequence { () -> AnyGenerator<S> in
var raw = 0
return AnyGenerator {
let current : Self = withUnsafePointer(&raw) { UnsafePointer($0).memory }
guard current.hashValue == raw else { return nil }
raw += 1
return current
}
}


return [S](retVal)
}
}

我发现自己在代码中经常使用.allValues。我终于找到了一种方法来简单地遵循Iteratable协议和rawValues()方法。

protocol Iteratable {}
extension RawRepresentable where Self: RawRepresentable {


static func iterateEnum<T: Hashable>(_: T.Type) -> AnyIterator<T> {
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
}
}
}


extension Iteratable where Self: RawRepresentable, Self: Hashable {
static func hashValues() -> AnyIterator<Self> {
return iterateEnum(self)
}


static func rawValues() -> [Self.RawValue] {
return hashValues().map({$0.rawValue})
}
}




// Example
enum Grocery: String, Iteratable {
case Kroger = "kroger"
case HEB = "h.e.b."
case Randalls = "randalls"
}


let groceryHashes = Grocery.hashValues() // AnyIterator<Grocery>
let groceryRawValues = Grocery.rawValues() // ["kroger", "h.e.b.", "randalls"]

Xcode 10与Swift 4.2

enum Filter: String, CaseIterable {


case salary = "Salary"
case experience = "Experience"
case technology = "Technology"
case unutilized = "Unutilized"
case unutilizedHV = "Unutilized High Value"


static let allValues = Filter.allCases.map { $0.rawValue }
}

叫它

print(Filter.allValues)

打印:

[“薪水”、“经验”、“技术”、“未被利用”、“未被利用的高价值”]


旧版本

对于enum表示Int

enum Filter: Int {
case salary
case experience
case technology
case unutilized
case unutilizedHV
    

static let allRawValues = salary.rawValue...unutilizedHV.rawValue  // First to last case
static let allValues = allRawValues.map { Filter(rawValue: $0)!.rawValue }
}

这样叫它:

print(Filter.allValues)

打印:

[0,1,2,3,4]


对于enum表示String

enum Filter: Int {
case salary
case experience
case technology
case unutilized
case unutilizedHV
    

static let allRawValues = salary.rawValue...unutilizedHV.rawValue  // First to last case
static let allValues = allRawValues.map { Filter(rawValue: $0)!.description }
}


extension Filter: CustomStringConvertible {
var description: String {
switch self {
case .salary: return "Salary"
case .experience: return "Experience"
case .technology: return "Technology"
case .unutilized: return "Unutilized"
case .unutilizedHV: return "Unutilized High Value"
}
}
}

叫它

print(Filter.allValues)

打印:

[“薪水”、“经验”、“技术”、“未被利用”、“未被利用的高价值”]

在Swift 3中,当底层枚举有rawValue时,你可以实现Strideable协议。优点是不像其他建议那样创建值数组,并且标准的Swift“for in”循环工作,这是一个很好的语法。

// "Int" to get rawValue, and Strideable so we can iterate
enum MyColorEnum: Int, Strideable {
case Red
case Green
case Blue
case Black


// required by Strideable
typealias Stride = Int


func advanced(by n:Stride) -> MyColorEnum {
var next = self.rawValue + n
if next > MyColorEnum.Black.rawValue {
next = MyColorEnum.Black.rawValue
}
return MyColorEnum(rawValue: next)!
}


func distance(to other: MyColorEnum) -> Int {
return other.rawValue - self.rawValue
}


// just for printing
func simpleDescription() -> String {
switch self {
case .Red: return "Red"
case .Green: return "Green"
case .Blue: return "Blue"
case .Black: return "Black"
}
}
}


// this is how you use it:
for i in MyColorEnum.Red ... MyColorEnum.Black {
print("ENUM: \(i)")
}
< p >编辑: Swift Evolution Proposal SE-0194枚举案例的派生集合针对这个问题提出了一个级别为解决方案的提案。我们在Swift 4.2和更新版本中看到了它。该提案还指出了一些解决方法,与这里已经提到的一些类似,但可能会很有趣。

为了完整起见,我也会保留我原来的职位。


这是基于@Peymmankh的回答的另一种方法,适用于斯威夫特3

public protocol EnumCollection: Hashable {}


extension EnumCollection {


public static func allValues() -> [Self] {
typealias S = Self


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


return [S](retVal)
}

我添加了函数count(),并迭代这些值:

public enum MetricType: Int {
case mvps = 0
case allNBA = 1
case championshipRings = 2
case finalAppearances = 3
case gamesPlayed = 4
case ppg = 5


static func count() -> Int {
return (ppg.rawValue) + 1
}


static var allValues: [MetricType] {
var array: [MetricType] = Array()
var item : MetricType = MetricType.mvps
while item.rawValue < MetricType.count() {
array.append(item)
item = MetricType(rawValue: (item.rawValue + 1))!
}
return array
}
}

另一个解决方案:

enum Suit: String {
case spades = "♠"
case hearts = "♥"
case diamonds = "♦"
case clubs = "♣"


static var count: Int {
return 4
}


init(index: Int) {
switch index {
case 0: self = .spades
case 1: self = .hearts
case 2: self = .diamonds
default: self = .clubs
}
}
}


for i in 0..<Suit.count {
print(Suit(index: i).rawValue)
}

我发现了一种有点俗气但更安全的方法,它不需要键入两次值或引用枚举值的内存,因此不太可能损坏。

基本上,与其使用枚举,不如创建一个具有单个实例的结构体,并将所有enum-values设置为常量。然后可以使用Mirror查询变量

public struct Suit{


// the values
let spades = "♠"
let hearts = "♥"
let diamonds = "♦"
let clubs = "♣"


// make a single instance of the Suit struct, Suit.instance
struct SStruct{static var instance: Suit = Suit()}
static var instance : Suit{
get{return SStruct.instance}
set{SStruct.instance = newValue}
}


// an array with all of the raw values
static var allValues: [String]{
var values = [String]()


let mirror = Mirror(reflecting: Suit.instance)
for (_, v) in mirror.children{
guard let suit = v as? String else{continue}
values.append(suit)
}


return values
}
}

如果使用此方法,则需要使用Suit.instance.clubsSuit.instance.spades来获取单个值

但所有这些都太无聊了……让我们做一些事情,使它更像一个真正的enum!

public struct SuitType{


// store multiple things for each suit
let spades = Suit("♠", order: 4)
let hearts = Suit("♥", order: 3)
let diamonds = Suit("♦", order: 2)
let clubs = Suit("♣", order: 1)


struct SStruct{static var instance: SuitType = SuitType()}
static var instance : SuitType{
get{return SStruct.instance}
set{SStruct.instance = newValue}
}


// a dictionary mapping the raw values to the values
static var allValuesDictionary: [String : Suit]{
var values = [String : Suit]()


let mirror = Mirror(reflecting: SuitType.instance)
for (_, v) in mirror.children{
guard let suit = v as? Suit else{continue}
values[suit.rawValue] = suit
}


return values
}
}


public struct Suit: RawRepresentable, Hashable{
public var rawValue: String
public typealias RawValue = String


public var hashValue: Int{
// find some integer that can be used to uniquely identify
// each value. In this case, we could have used the order
// variable because it is a unique value, yet to make this
// apply to more cases, the hash table address of rawValue
// will be returned, which should work in almost all cases
//
// you could also add a hashValue parameter to init() and
// give each suit a different hash value
return rawValue.hash
}


public var order: Int
public init(_ value: String, order: Int){
self.rawValue = value
self.order = order
}


// an array of all of the Suit values
static var allValues: [Suit]{
var values = [Suit]()


let mirror = Mirror(reflecting: SuitType.instance)
for (_, v) in mirror.children{
guard let suit = v as? Suit else{continue}
values.append(suit)
}


return values
}


// allows for using Suit(rawValue: "♦"), like a normal enum
public init?(rawValue: String){
// get the Suit from allValuesDictionary in SuitType, or return nil if that raw value doesn't exist
guard let suit = SuitType.allValuesDictionary[rawValue] else{return nil}
// initialize a new Suit with the same properties as that with the same raw value
self.init(suit.rawValue, order: suit.order)
}
}

你现在可以做

let allSuits: [Suit] = Suit.allValues

for suit in Suit.allValues{
print("The suit \(suit.rawValue) has the order \(suit.order)")
}

然而,要得到一个单,你仍然需要使用SuitType.instance.spadesSuitType.instance.hearts。为了使这更直观,您可以向Suit添加一些代码,允许您使用Suit.type.*而不是SuitType.instance.*

public struct Suit: RawRepresentable, Hashable{
// ...your code...


static var type = SuitType.instance


// ...more of your code...
}

您现在可以使用Suit.type.diamonds代替SuitType.instance.diamonds,或Suit.type.clubs代替SuitType.instance.clubs

你可以试着像这样列举

enum Planet: String {
case Mercury
case Venus
case Earth
case Mars


static var enumerate: [Planet] {
var a: [Planet] = []
switch Planet.Mercury {
case .Mercury: a.append(.Mercury); fallthrough
case .Venus: a.append(.Venus); fallthrough
case .Earth: a.append(.Earth); fallthrough
case .Mars: a.append(.Mars)
}
return a
}
}


Planet.enumerate // [Mercury, Venus, Earth, Mars]

下面是我用来迭代enum和从一个enum提供多个值类型的方法

enum IterateEnum: Int {
case Zero
case One
case Two
case Three
case Four
case Five
case Six
case Seven


//tuple allows multiple values to be derived from the enum case, and
//since it is using a switch with no default, if a new case is added,
//a compiler error will be returned if it doesn't have a value tuple set
var value: (french: String, spanish: String, japanese: String) {
switch self {
case .Zero: return (french: "zéro", spanish: "cero", japanese: "nuru")
case .One: return (french: "un", spanish: "uno", japanese: "ichi")
case .Two: return (french: "deux", spanish: "dos", japanese: "ni")
case .Three: return (french: "trois", spanish: "tres", japanese: "san")
case .Four: return (french: "quatre", spanish: "cuatro", japanese: "shi")
case .Five: return (french: "cinq", spanish: "cinco", japanese: "go")
case .Six: return (french: "six", spanish: "seis", japanese: "roku")
case .Seven: return (french: "sept", spanish: "siete", japanese: "shichi")
}
}


//Used to iterate enum or otherwise access enum case by index order.
//Iterate by looping until it returns nil
static func item(index: Int) -> IterateEnum? {
return IterateEnum.init(rawValue: index)
}


static func numberFromSpanish(number: String) -> IterateEnum? {
return findItem { $0.value.spanish == number }
}


//use block to test value property to retrieve the enum case
static func findItem(predicate: ((_: IterateEnum) -> Bool)) -> IterateEnum? {


var enumIndex: Int = -1
var enumCase: IterateEnum?


//Iterate until item returns nil
repeat {
enumIndex += 1
enumCase = IterateEnum.item(index: enumIndex)


if let eCase = enumCase {


if predicate(eCase) {
return eCase
}
}
} while enumCase != nil
return nil
}
}


var enumIndex: Int = -1
var enumCase: IterateEnum?


// Iterate until item returns nil
repeat {
enumIndex += 1
enumCase = IterateEnum.item(index: enumIndex)
if let eCase = enumCase {
print("The number \(eCase) in french: \(eCase.value.french), spanish: \(eCase.value.spanish), japanese: \(eCase.value.japanese)")
}
} while enumCase != nil


print("Total of \(enumIndex) cases")


let number = IterateEnum.numberFromSpanish(number: "siete")


print("siete in japanese: \((number?.value.japanese ?? "Unknown"))")

输出如下:

法语:zéro,西班牙语:cero,日语:nuru
法语中的“1”是un,西班牙语中的“uno”,日语中的“ichi”
法语中的数字二:deux,西班牙语:dos,日语:ni
数字三在法语中:trois,西班牙语:tres,日语:san
数字4在法语中是quatre,西班牙语中是cuatro,日语中是shi
数字5在法语中是cinq,西班牙语中是cinco,日语中是go
数字6在法语中是Six,西班牙语是seis,日语是roku
数字7在法语:sept,西班牙语:siete,日语:shichi

共8例

Siete在日语中的意思是:shichi


< em > < / em >更新

我最近创建了一个协议来处理枚举。该协议需要一个Int原始值的enum:

protocol EnumIteration {


//Used to iterate enum or otherwise access enum case by index order. Iterate by looping until it returns nil


static func item(index:Int) -> Self?
static func iterate(item:((index:Int, enumCase:Self)->()), completion:(()->())?) {
static func findItem(predicate:((enumCase:Self)->Bool)) -> Self?
static func count() -> Int
}


extension EnumIteration where Self: RawRepresentable, Self.RawValue == Int {


//Used to iterate enum or otherwise access enum case by index order. Iterate by looping until it returns nil
static func item(index:Int) -> Self? {
return Self.init(rawValue: index)
}


static func iterate(item:((index:Int, enumCase:Self)->()), completion:(()->())?) {


var enumIndex:Int = -1
var enumCase:Self?


//Iterate until item returns nil
repeat {
enumIndex += 1
enumCase = Self.item(enumIndex)


if let eCase = enumCase {
item(index: enumIndex, enumCase: eCase)
}
} while enumCase != nil
completion?()
}


static func findItem(predicate:((enumCase:Self)->Bool)) -> Self? {


var enumIndex:Int = -1
var enumCase:Self?


//Iterate until item returns nil
repeat {
enumIndex += 1
enumCase = Self.item(enumIndex)


if let eCase = enumCase {


if predicate(enumCase:eCase) {
return eCase
}
}
} while enumCase != nil
return nil
}


static func count() -> Int {
var enumIndex:Int = -1
var enumCase:Self?


//Iterate until item returns nil
repeat {
enumIndex += 1
enumCase = Self.item(enumIndex)
} while enumCase != nil


//last enumIndex (when enumCase == nil) is equal to the enum count
return enumIndex
}
}

斯威夫特4.2 +

斯威夫特4.2 (Xcode 10)开始,只需将协议一致性添加到CaseIterable,就可以从allCases中受益。要添加这个协议一致性,你只需要在某个地方写:

extension Suit: CaseIterable {}

如果枚举是你自己的,你可以直接在声明中指定一致性:

enum Suit: String, CaseIterable { case spades = "♠"; case hearts = "♥"; case diamonds = "♦"; case clubs = "♣" }

然后下面的代码将打印所有可能的值:

Suit.allCases.forEach {
print($0.rawValue)
}

与早期Swift版本的兼容性(3。X和4.x)

如果您需要支持Swift 3。x或4.0,你可以通过添加以下代码来模仿Swift 4.2的实现:

#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<Self> 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

(改进Karthik Kumar的回答)

这个解决方案是使用编译器来保证你不会错过一个case。

enum Suit: String {
case spades = "♠"
case hearts = "♥"
case diamonds = "♦"
case clubs = "♣"


static var enumerate: [Suit] {
switch Suit.spades {
// make sure the two lines are identical ^_^
case        .spades, .hearts, .diamonds, .clubs:
return [.spades, .hearts, .diamonds, .clubs]
}
}
}

Swift 5解决方案:

enum Suit: String, CaseIterable {
case spades = "♠"
case hearts = "♥"
case diamonds = "♦"
case clubs = "♣"
}


// access cases like this:


for suitKey in Suit.allCases {
print(suitKey)
}

更新代码:Swift 4.2/Swift 5

enum Suit: String, CaseIterable {
case spades = "♠"
case hearts = "♥"
case diamonds = "♦"
case clubs = "♣"
}

按问题访问输出:

for suitKey in Suit.allCases {
print(suitKey.rawValue)
}

输出:

♠
♥
♦
♣

CaseIterable:提供了它所有值的集合。 符合CaseIterable协议的类型通常是没有关联值的枚举。当使用CaseIterable类型时,您可以通过使用类型的allCases属性访问该类型的所有case的集合

对于访问案例,我们使用.allCases。更多信息请点击https://developer.apple.com/documentation/swift/caseiterable