January 31, 2018 · Ralf Ebert » iOS Developer Blog »

Using ranges for enums and other types

Swift comes with a nice shortcut syntax for ranges:

0...5    // [0, 1, 2, 3, 4, 5]
0..<5    // [0, 1, 2, 3, 4]
(0...5).contains(3)   // true
(0...5).forEach { (i) in print(i) }

By default this is supported for the numeric types. But can this be used for enums and custom types as well? For example, in one application I have an enum type to represent musical notes:

public enum NoteLetter: Int {

    case C = 0, D, E, F, G, A, B

}

It would be nice to be able to define ranges based on this type like this:

NoteLetter.C..<NoteLetter.F

By default, this will give an error:

* binary operator '..<' cannot be applied to two '...' operands
* `expected an argument list of type '(Bound, Bound)'

To be used as a range, the type needs to implement the Strideable protocol:

public enum NoteLetter: Int, Strideable {

    case C, D, E, F, G, A, B

    public func distance(to other: NoteLetter) -> NoteLetter.Stride {
    return Stride(other.rawValue) - Stride(self.rawValue)
}

public func advanced(by n: NoteLetter.Stride) -> NoteLetter {
    return NoteLetter(rawValue: numericCast(Stride(self.rawValue) + n))!
}

public typealias Stride = Int
}

The same is possible for structs, for example for this struct that represents a musical interval:

public struct Interval : Strideable {
    var semitones : Int

    public static let unison = Interval(semitones: 0)
    public static let minorSecond = Interval(semitones: 1), majorSecond = Interval(semitones: 2)
    public static let minorThird = Interval(semitones: 3), majorThird = Interval(semitones: 4)
    public static let fourth = Interval(semitones: 5)
    public static let tritone = Interval(semitones: 6)
    public static let fifth = Interval(semitones: 7)
    public static let minorSixth = Interval(semitones: 8), majorSixth = Interval(semitones: 9)
    public static let minorSeventh = Interval(semitones: 10), majorSeventh = Interval(semitones: 11)
    public static let octave = Interval(semitones: 12)

    public func distance(to other: Interval) -> Interval.Stride {
    return Stride(other.semitones) - Stride(self.semitones)
}

public func advanced(by n: Interval.Stride) -> Interval {
    return Interval(semitones: numericCast(Stride(self.semitones) + n))
}

public typealias Stride = Int

}
Btn read 3c0e607615 iOS Developer Blog
Btn subscribe 930758687e Subscribe: Email · Twitter
Btn training bbbdf557d2 Next iOS training: 25. Februar - 01. März 2019, Stuttgart
Btn about 5378472193 About me · Contact