How to get next case of enum(i.e. write a circulating method) in Swift 4.2
Some problems with your approach are:
- The
Collection
protocol does not define alast
property. - In order to compare the elements with
==
they have to beEquatable
. - Collection indices are not necessarily integers, they must be incremented
with
index(after:)
.
This seems to be a working solution (tested with Xcode 10.0 beta 2):
extension CaseIterable where Self: Equatable {
func next() -> Self {
let all = Self.allCases
let idx = all.index(of: self)!
let next = all.index(after: idx)
return all[next == all.endIndex ? all.startIndex : next]
}
}
Example:
enum Direction: CaseIterable {
case east, south, west, north
}
print(Direction.east.next()) // south
print(Direction.north.next()) // east
Remarks:
- Only enumerations without associated values are
CaseIterable
, and those are alsoEquatable
(but the compiler does not figure out that by itself). ThereforeSelf: Equatable
is not a real restriction. Self.allCases
can be used in Swift 4.2 to access the type property from an instance method.- The forced unwrapping is safe because we know that the value is
an element of
allCases
. - Your
enum Direction: CaseIterable
compiles because the concreteenum Direction
type isEquatable
, and itsDirection.allCases
is anArray
– which has integer indices and alast
property.
If someone is interested into both previous
and next
cases, here's an upgrade of the previous answer:
extension CaseIterable where Self: Equatable, AllCases: BidirectionalCollection {
func previous() -> Self {
let all = Self.allCases
let idx = all.index(of: self)!
let previous = all.index(before: idx)
return all[previous < all.startIndex ? all.index(before: all.endIndex) : previous]
}
func next() -> Self {
let all = Self.allCases
let idx = all.index(of: self)!
let next = all.index(after: idx)
return all[next == all.endIndex ? all.startIndex : next]
}
}