Search code examples
swiftenumsdry

Create a DRY function for multiple enums (enum subclassing?)


I have multiple enums which will all share an identical function. Is there a way to write this function once and have it present in all specified enums? Here is an example:

enum CustomerTypes: Int, CaseIterable {
    case NewCustomer = 0
    case ExistingCustomer
    case Myself

    private var title: String {
        switch self {
        case .NewCustomer : return StandardStrings.NewDrive.NewCustomer.string
        case .ExistingCustomer : return StandardStrings.NewDrive.ExistingCustomer.string
        case .Myself : return StandardStrings.NewDrive.Myself.string
        }
    }

    static var titles: [String] {
        get {
            var toReturn: [String] = []
            for value in allCases {
                toReturn.append(value.title)
            }

            return toReturn
        }
    }
}

enum EnquiryTypes: Int, CaseIterable {
    case Phone = 0
    case FaceToFace

    private var title: String {
        switch self {
        case .Phone : return StandardStrings.Misc.Phone.string
        case .FaceToFace : return StandardStrings.Misc.FaceToFace.string
        }
    }

    static var titles: [String] {
        get {
            var toReturn: [String] = []
            for value in allCases {
                toReturn.append(value.title)
            }

            return toReturn
        }
    }
}

As you can see here, both enums share identical "titles" variables. Is there a way I can possibly create an enum/class in which they can inherit this function from?

Here are some things I have tried:
Can you extend an enum?
I considered this, but I don't want to extend ALL functions that have Int rawValues

I considered creating a protocol, then creating a function which took the protocol like this, but I still would need to implement the _allCases on every enum (as you can't inherit from CaseIterable):

protocol RBEnum {
    var title: String { get }
    var _allCases: [RBEnum] { get }
}

So any ideas how I can avoid violating DRY principles on these enums?


Solution

  • A protocol is the right approach. Not sure why do you think protocols cannot inherit from each other, but they can, so you can make your protocol inherit from CaseIterable.

    You can also significantly simplify titled by using map instead of a for..in loop and getting rid of the useless get specifier. A getter is the default accessor for a computed property, you don't need to wrap your closure in get { ... } unless you are creating both a getter and a setter.

    protocol Titled: CaseIterable {
        var title: String { get }
        static var titles: [String] { get }
    }
    
    extension Titled {
        static var titles: [String] { allCases.map(\.title) }
    }
    

    Then you just need to keep the title property on your enums and make them conform to Titled and due to the default implementation you get titles for free.

    Unrelated to your question, but enum cases should be lowerCamelCase like all variables.

    enum CustomerTypes: Int, Titled {
        case newCustomer = 0
        case existingCustomer
        case myself
    
        var title: String {
            switch self {
            case .newCustomer : return "new"
            case .existingCustomer : return "existing"
            case .myself : return "my"
            }
        }
    }
    
    enum EnquiryTypes: Int, Titled {
        case phone = 0
        case faceToFace
    
        var title: String {
            switch self {
            case .phone : return "phone"
            case .faceToFace : return "face"
            }
        }
    }