swiftenumsprotocolsrawrepresentable

Defining a Swift Protocol for Arbitrary, Int-based Enums


I have this enumeration representing a color, and I have added several methods to conveniently obtain new instances based on arithmetic operations on the original's raw value:

enum Color : Int
{
    case Red = 0
    case Green
    case Blue

    case Cyan
    case Magenta
    case Yellow


    static func random() -> Color
    {
        return Color(rawValue: Int(arc4random_uniform(6)))!
    }

    func shifted(by offset:Int) -> Color
    {
        return Color(rawValue: (self.rawValue + offset) % 6)!
        // Cyclic: wraps around
    }
}

(This harks back to the old enums being just int constants)

The problem is, I have several other int-based enums where I would like to introduce similar functionality, but without duplicating code.

I think I should define a protocol extension on RawRepresentable where RawValue == Int:

extension RawRepresentable where RawValue == Int
{

...but that's where my understanding of the syntax ends.

Ideally, I would like to require a static method returning the number of cases, and a provide default implementation of both random() and shifted(_:) above that takes that into account (instead of the hard-coded 6 here).

CONCLUSION: I have accepted the answer by Zoff Dino. Even though the answer given by Rob Napier is exactly what I asked for, it turns out what I was asking for was not the most elegant design after all, and the other answer suggests a better approach. Still, I have upvoted both answers; thanks everyone.


Solution

  • You should extend your custom protocol instead of RawRepresentable. Try this:

    protocol MyProtocol {
        static var maxRawValue : Int { get }
    
        static func random() ->  Self
        func shifted(by offset: Int) -> Self
    }
    
    enum Color : Int, MyProtocol
    {
        case Red = 0
        case Green
        case Blue
    
        case Cyan
        case Magenta
        case Yellow
    
        // The maximum value of your Int enum
        static var maxRawValue: Int {
            return Yellow.rawValue
        }
    }
    
    extension MyProtocol where Self: RawRepresentable, Self.RawValue == Int {
        static func random() -> Self {
            let random = Int(arc4random_uniform(UInt32(Self.maxRawValue + 1)))
            return Self(rawValue: random)!
        }
    
        func shifted(by offset: Int) -> Self {
            return Self(rawValue: (self.rawValue + offset) % (Self.maxRawValue + 1))!
        }
    }
    
    let x = Color.random()
    let y = x.shifted(by: 1)