How to get next case of enum(i.e. write a circulating method) in Swift 4.2

前端 未结 2 912
感情败类
感情败类 2021-01-11 14:59

Swift 4.2 introduces a new CaseIterable protocol that automatically generates an array property of all cases in an enum.
Now I want to implement a default m

相关标签:
2条回答
  • 2021-01-11 15:06

    Some problems with your approach are:

    • The Collection protocol does not define a last property.
    • In order to compare the elements with == they have to be Equatable.
    • 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 also Equatable (but the compiler does not figure out that by itself). Therefore Self: 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 concrete enum Direction type is Equatable, and its Direction.allCases is an Array – which has integer indices and a last property.
    0 讨论(0)
  • 2021-01-11 15:31

    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]
        }
    }
    
    0 讨论(0)
提交回复
热议问题