How to Create Protocols That Enums Can Conform to with Their Cases
Learn how to create protocols for enums to conform with their cases, including handling associated values, inspired by insights from the Composable Architecture.
When working with enums in Swift, you might want to define a protocol that your enums can conform to, particularly for their cases. This idea is inspired by techniques used in The Composable Architecture (TCA). In this post, we’ll explore how to create such protocols and handle associated values effectively.
Defining a Simple Protocol for Enum Cases
Imagine you have an enum like this:
1
2
3
enum ButtonAction {
case tapped
}
You want to enforce that any enum conforming to a protocol must include a case representing a button action, such as tapped
. Here’s how you can define a protocol to achieve this:
1
2
3
public protocol ButtonTapAction {
static var tapped: Self { get }
}
Key Points:
- Static Property: The property must be
static
because when you callButtonAction.tapped
, you’re accessing the enum’s static case. - Self as Return Type: The return type is
Self
to ensure the protocol works for any conforming enum. - Immutable: The property is read-only (
get
), meaning it cannot be modified.
Now, conforming to this protocol looks like this:
1
2
3
4
5
6
7
public protocol ButtonTapAction {
static var tapped: Self { get }
}
enum MyButtonAction: ButtonTapAction {
case tapped
}
That’s it! Your enum now conforms to the protocol, and you can enforce this behavior across other enums.
Handling Associated Values
What if you want to add associated values to your enum cases? For example, you might want to track the number of taps:
1
2
3
enum MyButtonAction {
case tappedWith(count: Int)
}
Using a static var
in the protocol won’t work here because associated values require parameters. Instead, we can use a static func
in the protocol:
1
2
3
public protocol ButtonTapAction {
static func tappedWith(count: Int) -> Self
}
Key Points:
- Static Function: Just like the static property, the function must also be
static
. - Self as Return Type: The return type is still
Self
, ensuring flexibility for any conforming enum. - Matching Parameters: The function’s parameters must match the associated value type of the enum case.
Here’s how the protocol and the enum would look together:
1
2
3
4
5
6
7
8
9
public protocol ButtonTapAction {
static var tapped: Self { get }
static func tappedWith(count: Int) -> Self
}
enum MyButtonAction: ButtonTapAction {
case tapped
case tappedWith(count: Int)
}
Summary
Creating protocols that enums can conform to is a powerful way to enforce consistent behavior. Here’s what we covered:
- Use
static var
for simple cases without associated values. - Use
static func
for cases with associated values to handle parameters.
This pattern is clean, expressive, and leverages Swift’s type safety to create reusable and maintainable code.
Let me know your thoughts in the comments!
☕ Support My Work
If you found this post helpful and want to support more content like this, you can buy me a coffee!
Your support helps me continue creating useful articles and tips for fellow developers. Thank you! 🙏