5

What I am trying to do is create a protocol extension to fetch an array of raw values from an enum. For example say I have the following:

enum TestType: String, EnumIteratable {
    case unitTest = "Unit Test"
    case uiTest = "UI Test"
}

class EnumIterator: NSObject {
    class 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
        }
    }

   class func getValues<T: Hashable>(_: T.Type) -> [T] {
        let iterator = self.iterateEnum(T)
        var returnArray = [T]()
        for val in iterator {
            returnArray.append(val)
        }
        return returnArray
    }

}

How can I implement the protocol EnumIteratable so that I can call TestType.getRawValues() and have it return an string array of all the raw enum values?

Thanks!

Carlos Macasaet
  • 1,176
  • 7
  • 23
JonahGabriel
  • 3,066
  • 2
  • 18
  • 28
  • Maybe using MirrorType? – Marcelo Nov 08 '15 at 01:26
  • @MarceloFabri A mirror won't work for several reasons, one of which is that you actually need an instance of the enum, which I am trying to avoid. Also, enums don't have properties :) – JonahGabriel Nov 08 '15 at 18:40
  • Possible duplicate of [How to enumerate an enum with String type?](https://stackoverflow.com/questions/24007461/how-to-enumerate-an-enum-with-string-type) – Cœur Mar 31 '18 at 12:39

2 Answers2

3

Scott's solution is probably the one you want. But if you were looking for something more generic that you can apply to arbitrary future enumerations and allows for additional cases, you could try this:

First, you need a method to iterate over Enum cases. I used this implementation from here: https://stackoverflow.com/a/28341290/914887

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

Then, you can create your protocol, that defines the static functions you want:

protocol EnumIteratable {
    typealias ENUM_TYPE:Hashable, RawRepresentable = Self
    static func getAllValues() -> [ ENUM_TYPE ]
    static func getRawValues() -> [ ENUM_TYPE.RawValue ]
}

I used an associated type to allow the conforming enums to specify their type to the protocol. getAllValues is not strictly necessary, but it simplifies the logic.

Then, you can define your generic default implementations:

extension EnumIteratable {
    static func getAllValues() -> [ ENUM_TYPE ]
    {
        var retval = [ ENUM_TYPE ]()
        for item in iterateEnum( ENUM_TYPE )
        {
            retval.append( item )
        }
        return retval
    }

    static func getRawValues() -> [ ENUM_TYPE.RawValue ]
    {
        return getAllValues().map( { ( item:ENUM_TYPE ) -> ENUM_TYPE.RawValue in item.rawValue } )
    }
}

Finally, all you need to do is conform to that protocol any time you need to iterate over the enum:

enum TestType: String, EnumIteratable {
    case unitTest = "Unit Test"
    case uiTest = "UI Test"
}

TestType.getRawValues()

The advantage here, is that I can add a new case for integrationTest and I only need to add that in one place.

Community
  • 1
  • 1
Carlos Macasaet
  • 1,176
  • 7
  • 23
  • 1
    This is pretty cool and close to what I was trying to accomplish originally :) Thanks for the alternative solution! – JonahGabriel Nov 08 '15 at 21:13
1

You could just add a static property to return all enum values. For example:

 enum RelationshipStatus: String {

  case Single = "Single"
  case Married = "Married"
  case ItsComplicated = "It's complicated"

  static let all: [RelationshipStatus] = [.Single, .Married, .ItsComplicated]

}

for status in RelationshipStatus.all {
  print(status.rawValue)
}
Scott Gardner
  • 8,603
  • 1
  • 44
  • 36