Search code examples
swiftprotocols

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
    }
}

Solution

  • 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