Swift needs to allow iterating over enums

Hi


I wanted to try out interating over the different styles of data formatting. I tried this code:


let date = NSDate()

var i: UInt = 0

while let style = NSDateFormatterStyle(rawValue: i) {

format.timeStyle = style

print("For style \(i) Date/time = \(format.stringFromDate(date))")

++i

}


It went into an endless loop even though the enum is clearly defined:


enum NSDateFormatterStyle : UInt {

case NoStyle

case ShortStyle

case MediumStyle

case LongStyle

case FullStyle

}


A simple syntax to allow a range over an enum would be a great improvement, I think.

Your code above works fine - the bug must be somewhere else. Also, it's easy enough to construct a struct that does what you want:

enum NSDateFormatterStyle : UInt {
  case NoStyle
  case ShortStyle
  case MediumStyle
  case LongStyle
  case FullStyle
}

public struct EnumGenerator<I : ForwardIndexType, T> : GeneratorType, SequenceType {
  private var i: I
  private let en: I -> T?
  public mutating func next() -> T? { return en(i++) }
  public func generate() -> EnumGenerator<I, T> { return self }
  public init(_ initFunc: I -> T?, start: I) {
    self.en = initFunc
    self.i = start
  }
}

for enumCase in EnumGenerator(NSDateFormatterStyle.init, start: 0) {
 //
}

That's not good enough. We need something that will enumerate all cases regardless of whether ++ makes any sense.


https://msdn.microsoft.com/en-us/library/system.enum.getvalues(v=vs.110).aspx

The NSDateFormatterStyle enum comes from Cocoa, so it is a C-style NS_ENUM enum and not a Swift enum.

In your EnumGenerator test you are hiding the original enum with your redeclaration, so your code only sees the redeclared Swift enum.


Using init(rawValue:) for NS_ENUM enums doesn't return nil for raw values which don't match any of the listed cases, so your EnumGenerator struct wouldn't work for an NS_ENUM either.


You can compare an NS_ENUM and a Swift enum in the playground (Xcode 7 beta 3, but same result in 6.4)

import Cocoa

let date = NSDate()
let formatter = NSDateFormatter()
var i: UInt = 0
while let style = NSDateFormatterStyle(rawValue: i) {
    formatter.timeStyle = style
    print("For style \(i) \(style.rawValue) Date/time = \(formatter.stringFromDate(date))")
    ++i
    if (i > 100) {break}
}

enum SwiftDateStyle: UInt
{
    case NoStyle
    case ShortStyle
    case MediumStyle
    case LongStyle
    case FullStyle
}

var j: UInt = 0
while let style = SwiftDateStyle(rawValue: j) {
    formatter.timeStyle = NSDateFormatterStyle(rawValue: style.rawValue)!
    print("For style \(j) \(style.rawValue) Date/time = \(formatter.stringFromDate(date))")
    ++j
    if (j > 100) {break}
}


The NS_ENUM enum won't return nil from init(rawValue:) and will continue to run until stopped, but the Swift enum will return nil from init(rawValue:) and stop.

Oh, right! I didn't know that. Sorry logistes!

Thanks, guys. I was guessing it was a problem with the fact that it comes from an objective-C definition. I guess this little feature missed out on the good attentions of the swift compiler writers.


I think there should be a simple syntax to alloe you to iterate over an enum, as there is in Dephi:


type

TMyEnum = (meOne, meTwo, meBucklemyshoe);


i: TMyEnum;

begin

for i := Low(TMyEnum) to High(TMyEnum) do ..

Have any of you posted MSDN links? When I do it, I get "Currently being moderated.", but nobody actually moderates. It's annoying because it makes other people have to type the things I did. (For example, that Delphi code is effectively equivalent to how C# handles this, as far as I can tell.)

C# was written by the lead developer of Delphi, so they have similar thinking:
http://delphi.about.com/od/delphifornet/a/conspiracydnet_2.htm

Thank you, that struct works great. But can I go further and pass to EnumGenerator's init not enum's init function but the enum itself as a type and then get it's rawvalue init in code?

"Using init(rawValue:) for NS_ENUM enums doesn't return nil for raw values which don't match any of the listed cases"


Is this intended behavior? If so, is this documented anywhere?


And furthermore: What is the rationale behind this?


It looks like a bug to me.


Alex

Is this intended behavior?

Yes.

If so, is this documented anywhere?

Not sure.

And furthermore: What is the rationale behind this?

Because many Cocoa enumerations have values that you can’t see (private API, basically).

Ultimately Swift is going to have to be able to handle this natively. I expect to see this covered as part of the Swift 3 Stable ABI and Resilience tasks (with the caveat that I’m not directly involved in that work so I’m not making any promises :-).

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"
Swift needs to allow iterating over enums
 
 
Q