How to require an enum be defined in Swift Protocol
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
Swift protocols can require properties, methods, and associated types, but they cannot directly declare a nested enum that every conforming type must implement. The usual workaround is to require an associated type and then constrain it so it behaves the way your design needs.
Why a Protocol Cannot Declare a Concrete Enum
A protocol describes an interface, not stored implementation details. Because of that, Swift lets a protocol say "a conforming type must provide some type here," but it does not let the protocol define a nested enum body with fixed cases for every adopter.
That distinction matters. If your goal is "every conforming type must expose a state type," a protocol can express that. If your goal is "every conforming type must define exactly these enum cases," a protocol alone cannot enforce it.
Use an Associated Type for the Enum-Like Requirement
The cleanest approach is to require an associated type with constraints that match how you want to use the enum. In many designs, CaseIterable, Hashable, and RawRepresentable are enough.
This does not prove that Step is literally an enum, but it does express the behaviors that enum-based workflows usually need: finite cases, hashability, and raw string values.
Keep the Type Nested for Readability
Even though the protocol cannot force nesting, you can still adopt a convention where the associated type is declared as a nested enum inside the conforming type. That makes call sites clearer because the name stays attached to the owner.
Using a nested enum also keeps related logic together. When another developer opens CheckoutWorkflow, they immediately see its valid states and how they are handled.
If You Need Shared Cases Across Types
Sometimes every conforming type really should use the same list of cases. In that situation, a protocol is not the right tool for defining the cases. Instead, create one shared enum and have the protocol refer to it directly.
This design is more rigid, but it is also more explicit. If the cases must be identical, using one concrete enum is simpler than trying to model the same rule through protocol constraints.
What Swift Can and Cannot Enforce
Swift can enforce that a conforming type provides an associated type named Step. Swift can also enforce that the type conforms to protocols such as CaseIterable. What Swift cannot do is say "the associated type must be an enum declaration with exactly these cases." There is no language feature for that level of structural enforcement.
When you need exact cases plus polymorphism, choose one of two designs:
- Use one shared enum for all conforming types
- Use an associated type and accept that the protocol only constrains behavior, not syntax
Common Pitfalls
- Declaring
enuminside a protocol is not supported, so the compiler will reject that design immediately. - '
CaseIterabledoes not guarantee a type is an enum, only that it providesallCases.' - If exact cases matter, an associated type is too flexible and will not fully encode the rule.
- Avoid over-constraining the associated type unless you truly need
RawRepresentableor other protocol requirements.
Summary
- A Swift protocol cannot directly require a nested enum definition.
- Use an associated type to require an enum-like type from conforming types.
- Add constraints such as
CaseIterableandHashableto express the behaviors you need. - If all adopters must share the same cases, define one concrete enum and reference it from the protocol.

