Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I write a protocol extension to get all the rawValues from an Swift enum

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!

like image 883
JonahGabriel Avatar asked Sep 27 '22 06:09

JonahGabriel


People also ask

Can you extend an enum Swift?

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.

Can enum implement protocol in Swift?

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.

Can enum inherit Swift?

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.

What is Rawvalue in Swift?

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.


1 Answers

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.

like image 174
Carlos Macasaet Avatar answered Sep 29 '22 08:09

Carlos Macasaet