DEV Community

Jaume Viñas Navas
Jaume Viñas Navas

Posted on

2 1

Swift Enumerations (Part 3)

Swift enumerations are a first-class types in their own right, that means they can adopt many features traditionally supported only by classes. In this chapter we will cover how enumeration can define custom initializers to provide an initial case value, and how they can be extended and conform to protocols.

Remember to read the first chapters (Part 1 and Part 2) if you missed them or you want to refresh the basic concepts of Swift enums.

Protocols

A protocol defines an interface of methods, properties and other requirements that can be adopted by a class, structure or enumeration. Any type that satisfies the requirement of a protocol must provide an implementation of those requirements.

For demonstration purposes we use the LayerActions enumeration declared in the previous chapter, which defines a set of actions that users can execute in order to modify a layer in a design tool. To execute and describe each action we declare the protocol Taskable, which defines a method to execute a task and a description property that defines the task to be executed.

protocol Taskable {
func execute()
var description: String { get }
}
enum LayerActions: Taskable {
case scale(width: Double, height: Double)
case rotate(degrees: Double)
case fill(hex: String)
func execute() {
switch self {
case .scale(let width, let height):
print("Executes scale action")
case .rotate(let degrees):
print("Executes rotate action")
case .fill(let hexColor):
print("Executes fill action")
}
}
var description: String {
switch self {
case .scale(let width, let height):
return "Scales the layer to \(width)x\(height)"
case .rotate(let degrees):
return "Rotates the layer \(degrees) degrees"
case .fill(let hexColor):
return "Fills the layer with \(hexColor) color"
}
}
}
let scale = LayerActions.scale(width: 120, height: 60)
let fill = LayerActions.fill(hex: "#FFFFFF")
fill.description
fill.execute()

As you can see, the enumeration now conforms to the protocol requirements in the enumeration by implementing the method execute() and the computed property description.

Extensions

An extension adds new functionality to an existing class, structure, enumeration or protocol type. You can use an extension to add computed properties, define methods, define initializers, or make the existing type conform to a protocol among others.

In our case, we will extend the LayerActions enumeration to conform the protocol Taskable and make it easier to read, understand and maintain the code.

protocol Taskable {
func execute()
var description: String { get }
}
enum LayerActions {
case scale(width: Double, height: Double)
case rotate(degrees: Double)
case fill(hex: String)
}
extension LayerActions: Taskable {
func execute() {
switch self {
case .scale(let width, let height):
print("Executes scale action")
case .rotate(let degrees):
print("Executes rotate action")
case .fill(let hexColor):
print("Executes fill action")
}
}
var description: String {
switch self {
case .scale(let width, let height):
return "Scales the layer to \(width)x\(height)"
case .rotate(let degrees):
return "Rotates the layer \(degrees) degrees"
case .fill(let hexColor):
return "Fills the layer with \(hexColor) color"
}
}
}

We can add as many extensions as we want, for instance, we can extend the previous enumeration in order to implement a different protocol. Here’s an example that extends the enumeration twice to separate the implementation of protocols Taskable and Serializable.

protocol Taskable {
func execute()
var description: String { get }
}
protocol Serializable {
func serialize()
}
enum LayerActions {
case scale(width: Double, height: Double)
case rotate(degrees: Double)
case fill(hex: String)
}
extension LayerActions: Taskable {
func execute() {
switch self {
case .scale(let width, let height):
print("Executes scale action")
case .rotate(let degrees):
print("Executes rotate action")
case .fill(let hexColor):
print("Executes fill action")
}
}
var description: String {
switch self {
case .scale(let width, let height):
return "Scales the layer to \(width)x\(height)"
case .rotate(let degrees):
return "Rotates the layer \(degrees) degrees"
case .fill(let hexColor):
return "Fills the layer with \(hexColor) color"
}
}
}
extension LayerActions: Serializable {
func serialize() {
// Code that serialize the enum into a json file
}
}

Initializers

Enumerations with a raw-value type automatically receive an initializer that takes as a parameter a value of the raw value’s type. Here’s an example of an enumeration with a raw-value type that uses the initializer to create a new instance.

enum Polygon: Int {
case triangle = 3
case quadrilateral = 4
case pentagon = 5
}
let triangle = Polygon(rawValue: 3)

Initilizers return either an enumeration case or nil. As you can see in the following example, the variable q is of type Polygon? and the value returned by the raw initializer is nil.

enum Polygon: Int {
case triangle = 3
case quadrilateral = 4
case pentagon = 5
}
let edges = 2
if let q = Polygon(rawValue: edges) {
print("Polygon created with \(edges) edges")
} else {
print("There isn't a polygon with \(edges) edges")
}

You can also create custom initializers even if the enumeration is not a raw-value typed enumeration.

enum Size {
case small, medium, large, extraLarge
init?(size: Int) {
if (size <= 10) {
self = .small
} else if (size > 10 && size <= 20) {
self = .medium
} else if (size > 20 && size <= 30) {
self = .large
} else {
self = .extraLarge
}
}
}
enum GamePlatform {
case pc, playstation, xbox, nintendo
init?(name: String) {
if (name == "ps2" || name == "ps3" || name == "ps4") {
self = .playstation
} else if (name == "xbox" || name == "xbox 360" || name == "xbox one" || name == "xbox one x") {
self = .xbox
} else if (name == "nes" || name == "snes" || name == "switch") {
self = .nintendo
} else {
self = .pc
}
}
}
let nintendo = GamePlatform(name: "snes")
let extralarge = Size(size: 40)

If you find this post helpful, please recommend it for others to read.

Top comments (0)

👋 Kindness is contagious

Discover a treasure trove of wisdom within this insightful piece, highly respected in the nurturing DEV Community enviroment. Developers, whether novice or expert, are encouraged to participate and add to our shared knowledge basin.

A simple "thank you" can illuminate someone's day. Express your appreciation in the comments section!

On DEV, sharing ideas smoothens our journey and strengthens our community ties. Learn something useful? Offering a quick thanks to the author is deeply appreciated.

Okay