joels
joels

Reputation: 7721

How to require an enum be defined in Swift Protocol

Is it possible to have a Protocol require that an enum be defined?

//trying to do this
protocol JSONEncodable {
    enum PropertyName // Type not allowed here
    func valueForProperty(propertyName:PropertyName) -> Any
}

//which would be implemented like this
struct Person : JSONEncodable {
    var firstName : String
    var lastName : String

    enum PropertyName {
        case FirstName
        case LastName
        func allValues() {
            return [Person.PropertyName.FirstName, Person.PropertyName.LastName]
        }
        func stringValue() {
            return "\(self)"
        }
    }
    func valueForProperty(propertyName:PropertyName) -> Any {
        switch propertyName {

        case .FirstName:
            return firstName

        case .LastName:
            return lastName
        }
    }
}

//so that I could do something like this
extension JSONEncodable {

    func JSONObject() -> [String:AnyObject] {
        var dictionary = [String:AnyObject]()
        for propertyName in PropertyName.allValues {
            let value = valueForProperty(propertyName)

            if let valueObject = value as? AnyObject {
                dictionary[propertyName.stringValue()] = valueObject

            }else if let valueObject = value as? JSONEncodable {
                dictionary[propertyName.stringValue()] = valueObject.JSONObject()
            }

        }
        return dictionary
    }
}

Upvotes: 41

Views: 34811

Answers (3)

sadel
sadel

Reputation: 77

The other answer here are great. Here's another similar approach enforcing other protocols on the enum as well as variables

protocol MyEnumProtocol: Codable, CaseIterable {
    var prettyString: String { get }
}

protocol RequiresEnum {
    associatedtype MyEnum: MyEnumProtocol
}

class SampleClassConformingTo: RequiresEnum {
    enum MyEnum: MyEnumProtocol {
        case foo
        case bar
        
        var prettyString: String {
            switch self {
            case .foo:
                return "Foo"
            case .bar:
                return "Bar"
            }
        }
    }
}

Pretty much the same as the other answers but I think it's a bit more readable defining the associatedtype protocol

Upvotes: 0

Mackarous
Mackarous

Reputation: 1517

Protocols can have associatedtypes which would just need to be adhered to in any subclass:

enum MyEnum: String {
    case foo
    case bar
}

protocol RequiresEnum {
    associatedtype SomeEnumType: RawRepresentable where SomeEnumType.RawValue: StringProtocol

    func doSomethingWithEnum(someEnumType: SomeEnumType)
}

class MyRequiresEnum: RequiresEnum {
    typealias SomeEnumType = MyEnum

    func doSomethingWithEnum(someEnumType: SomeEnumType) {
        switch someEnumType {
        case .foo:
            print("foo")
        case .bar:
            print("bar")
        }
    }
}

let mre = MyRequiresEnum()
mre.doSomethingWithEnum(someEnumType: .bar)

Edit: The associatedtype must be adhered to

Upvotes: 48

Robin Dorpe
Robin Dorpe

Reputation: 352

I think you can do it with an associatedtype that adheres to RawRepresentable

Here's an example:

protocol SomeProtocol {
    associatedtype SomeType: RawRepresentable
}

If you need to specify the type of RawRepresentable like String for example, you can do that:

protocol SomeProtocol {
    associatedtype SomeType: RawRepresentable where SomeType.RawValue: StringProtocol
}

Now you'll have a compiler error if you try to implement the protocol with anything else than an enum that has String as RawRepresentable. I hope it helps.

Upvotes: 22

Related Questions