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!
We can extend the enum in two orthogonal directions: we can add new methods (or computed properties), or we can add new cases. Adding new methods won't break existing code. Adding a new case, however, will break any switch statement that doesn't have a default case.
Yes, enums can conform protocols. You can use Swift's own protocols or custom protocols. By using protocols with Enums you can add more capabilities.
In Swift language, we have Structs, Enum and Classes. Struct and Enum are passed by copy but Classes are passed by reference. Only Classes support inheritance, Enum and Struct don't.
Enumerations in Swift are much more flexible, and don't have to provide a value for each case of the enumeration. If a value (known as a raw value) is provided for each enumeration case, the value can be a string, a character, or a value of any integer or floating-point type.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With