Multiple Enum Implementing Protocols Questions

Multiple enum implementing protocols questions

For your second question, you just need to use Self:

protocol Eventable {
var name: String { get }
static var all: [Self] { get }
}

Self, similar to self, just means "the current type".

The first question is a little bit harder because you can't really get all the values of an enum safely. See here for more info. The closest I got was:

extension Eventable where Self: RawRepresentable, Self.RawValue == String {
var name: String {
return self.rawValue
}
}

This means that you can omit the declaration of name in MyEnum, but not all.

Multiple protocol conformace in enum doesn't work

Changing the enum Definition from enum Theme: Int, CaseIterable, CustomStringConvertible { to enum Theme:ThemeProvider { does not make Theme automatically have a T, which is required by ThemeProvider.

And if you add typealias T = Theme then the Theme enum does still not have Int, CaseIterable, CustomStringConvertible, cause typealias T = Theme does not make any type(Theme here) conform any protocol.

That's why enum Theme: Int, CaseIterable, CustomStringConvertible, ThemeProvider { typealias T = Theme ... is required to make Theme conform to ThemeProvider.

What you probably want is enum Theme: Int, ThemeProvider { where ThemeProvider is public protocol ThemeProvider: RawRepresentable, CaseIterable, CustomStringConvertible {}.

More can't be said without any example.

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

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

Swift enum multiple generic types involving depending protocols

The error is a little misleading. Let's simplify the problem to explore it.

protocol ProtocolA {}

protocol ProtocolB: ProtocolA {}

enum MyEnum<T: ProtocolA, U: ProtocolB> {
case A(T)
case B(U)
}

struct AThing: ProtocolA {}

struct BThing: ProtocolB {}

let bThing = BThing()
let thing = MyEnum.B(bThing) // error: cannot invoke 'B' with an argument list of type '(BThing)'

So now we have the same problem without needing DataObject. Why does this fail?

MyEnum is generic. That means that in order to create a concrete type, it must know the types of T and U. You provided constraints on those types (one conforms to ProtocolA and the other to ProtocolB), but you didn't say specifically what types they are.

When we get to this line:

let thing = MyEnum.B(bThing)

What type is thing? With type inference, we can work out what U is, but what is T?

let thing: MyEnum<?, BThing> = MyEnum.B(bThing)

There's just not enough context here to work it out, so we have to tell the compiler explicitly:

let thing = MyEnum<AThing, BThing>.B(bThing)

So the full type of thing is MyEnum<AThing, BThing>, which is a different type than MyEnum<OtherAThing, BThing>. (Exactly like [Int] is different type than [String], which is why let xs = [] won't compile without an explicit type definition.)

Your second example that works is not generic, so there's no problem. It simplifies to:

enum MyEnum {
case A(ProtocolA)
case B(ProtocolB)
}

Bound typealiases just rename types, they don't create new types (like an unbound typealias would, or as Swift 2.2 calls it, associatedtype). So we know that A takes any ProtocolA and B takes any ProtocolB, and all MyEnum instances have the same type.

The error is unfortunate because the compiler is confused. You'd get a clearer error if you simplified this down to the most basic case. This fails for exactly the same reason as your example.

enum MyEnum<T> {
case A
}

let x = MyEnum.A // error: generic parameter 'T' could not be inferred

How to implement Identifiable using two enum variables

Since this type is exactly defined by the combination of its values, it is its own Identifier. So as long as Card is Hashable, it can identify itself:

extension Card: Hashable, Identifiable {
var id: Self { self }
}

Passing enum with multiple generic types as argument

The problem is that the compiler cannot infer the type of T since you only specify the type of U. Therefore you have to explicitly define the type:

printState(State<Struct1, Struct2>.Two(secondStruct:Struct2()))

How do I create protocol extensions that work with enums that have been initialized?

You can create static property requirements with type Self for all common cases between the 2 enums on your protocol to be able to switch over them in the implementation of description.

protocol ModeName: CustomStringConvertible, Equatable {
static var off: Self { get }
static var exit: Self { get }
static var auto: Self { get }
static var partial: Self { get }
static var open: Self { get }
}

extension ModeName {
var description: String {
switch self {
case .off: return "MODE_OFF".localized()
case .exit: return "MODE_EXIT".localized()
case .auto: return "MODE_AUTO".localized()
case .partial: return "MODE_PARTIAL".localized()
case .open: return "MODE_OPEN".localized()
default:
return ""
}
}
}

enum DoorModeName: String, ModeName {
case off = "MODE_OFF"
case exit = "MODE_EXIT"
case auto = "MODE_AUTO"
case partial = "MODE_PARTIAL"
case open = "MODE_OPEN"
}

enum OperatingModeName: Int, ModeName {
case off = 0
case exit = 1
case auto = 2
case partial = 3
case open = 4
}

How to add and use an Enum Case as a Protocol Type Requirement?

You can't require protocol conformances to be enum's

protocol Component {
static var `default`: Self { get }
static func custom(value: Int) -> Self

var customValue: Int? { get }
}

struct C: Component {
static var `default`: C {
return C()
}

static func custom(value: Int) -> Self {
return C()
}

var customValue: Int? { 42 }
}

let c = C()
c is Component // true

And because you can't require conforming types to be enums you can't switch/case on self in a protocol extension

Protocol buffers and enums combinations?

In Protobufs, an enum-typed field is only allowed to have one of the exact numeric values specified in the enum. That is to say, you cannot use an enum-typed field as a bitfield. If you want a bitfield, you need to use an integer type like int32. This rule actually applies even in languages that have numeric enum types, like C++ -- if an enum-typed protobuf field read from the wire has an invalid value, it will be treated like an unknown field and thus hidden.

If you switch to integers, you of course now have the problem of how to declare flag values. Unfortunately Protobufs provides no good way to define constants. As you suggested in your self-answer, you can use a dummy enum definition as a hack, but note that the numeric value won't necessarily be available in all languages. It works in C++ and Python since they use numeric enums (and apparently C# too?). In Java, Protobuf enums have a .getNumber() method which you can use to get the numeric value; otherwise, normal Java enums are not numeric.

(Aside: I'm the author of most of Google's open source Protobuf code. I'm also the author of Cap'n Proto, a newer non-Google project aimed at replacing Protobufs. Among other advantages, Cap'n Proto supports defining constants in schema files. But, as of this writing C# support is not ready yet (though being worked on!).)



Related Topics



Leave a reply



Submit