Enumeraciones en Swift

Enumeraciones en Swift

En este capítulo veremos otro tipo de dato que podemos crear en nuestras aplicaciones y que serán muy útiles para resolver distintos tipos de problemas. A su vez, veremos la potencia que tienen las enumeraciones en Swift.

Definición de enumeraciones

Las enumeraciones son un tipo de dato que permiten definir un grupo de valores relacionados bajo un nombre. Cada uno de estos valores dentro de la enumeración son llamados miembros o casos y poseen a su vez, un nombre que los identifica. Esto es, al definir una enumeración, se establecen los posibles estados que ese enum puede llegar a valer. Todos esos estados, o miembros, están relacionados entre sí.

Por ejemplo, si quisiera crear una variable sobre los puntos cardinales sabemos que, en principio, tiene cuatro posibles valores: Norte, Sur, Este y Oeste. Esos cuatro valores podrían ser miembros de un enum llamado PuntosCardinales y la variable a crear podría tomar uno de esos estados.

Sintaxis de las enumeraciones en Swift

Para definir una enumeración se utiliza la palabra reservada enum seguida del nombre de la misma.

Luego, el cuerpo de la enumeración se encierra entre llaves, como vemos a continuación:

[code lang=”Swift”]
enum Enumeracion {
//Cuerpo de la enumeración
}
[/code]

Para definir los miembros de una enumeración, se utiliza la palabra reservada case. Siguiendo el ejemplo de los puntos cardinales, su estructura sería la siguiente:

[code lang=”Swift”]
enum PuntoCardinal {
case norte
case sur
case este
case oeste
}
[/code]

A su vez, cada caso puede definirse en una sola línea, usando una sola vez la palabra case y separando cada miembro por comas:

[code lang=”Swift”]
enum PuntoCardinal {
case norte, sur, este, oeste
}
[/code]

Convención de nombres para las enumeraciones en Swift

Al igual que las clases y las estructuras, las enumeraciones también son tipos de datos que creamos en nuestro código. Por lo tanto, el nombre que le asignemos debe comenzar con una mayúscula y, en caso de ser varias palabras, cada una de ellas debe separarse por mayúsculas sin usar guiones bajos u otro símbolo (camel case).

Otro punto a considerar es que el nombre de la enumeración debe estar en singular y nunca en plural. Esto es así ya que las enumeraciones se usan dentro de variables o constantes, por lo tanto solo pueden adoptar 1 valor a la vez. Siguiendo el caso anterior, si creamos una variable para el punto cardinal tendríamos lo siguiente:

[code lang=”Swift”]
var direccion = PuntoCardinal.norte
[/code]

La lectura de la asignación anterior es súper clara. Se está eligiendo un punto cardinal de un listado de posibles valores y guardándolo en la variable dirección.

Uso de las enumeraciones en Swift

Como vimos, tanto las variables como las constantes pueden ser del tipo de una enumeración. En su asignación inicial, debemos especificar el nombre del enum que vamos a usar y el valor que va a tomar dentro de la enumeración.

Supongamos que tenemos la siguiente enumeración:

[code lang=”Swift”]
enum DiaDeLaSemana {
case lunes, martes, miercoles, jueves, viernes, sabado, domingo
}
[/code]

Una posible asignación a una variable podría ser la siguiente:

[code lang=”Swift”]
var mañana = DiaDeLaSemana.martes
[/code]

Como la variable mañana se inicia por primera vez, debemos especificar que su valor va a ser alguno de los contenidos dentro de la enumeración DiaDeLaSemana. Sin embargo, si quisiéramos modificar su valor, ya no es necesario volver a indicar el tipo de dato:

[code lang=”Swift”]
mañana = .miercoles
[/code]

Como ya sabemos que mañana es un DiaDeLaSemana, directamente podemos asignarle otro valor dentro del enum usando un punto y el nuevo valor.

Comparar valores de las enumeraciones en Swift

Durante nuestro código, una vez que tengamos una variable con un cierto valor de los disponibles de una enumeración, vamos a necesitar preguntar por él para tomar una decisión.

Comparar con if

Podemos usar if para conocer el valor de la variable:

[code lang=”Swift”]
if mañana == DiaDeLaSemana.lunes {
print(“Se termina el fin de semana!”)
}
[/code]

Asimismo, tal como vimos antes, podemos simplificar el código anterior sin mencionar el tipo de dato:

[code lang=”Swift”]
if mañana == .lunes {
print(“Se termina el fin de semana!”)
}
[/code]

Comparar con switch

Sin embargo, si tenemos una enumeración con varios casos y deseamos codificar un comportamiento para cada uno, usar un if no va a ser la mejor opción. En esos casos, conviene usar switch:

[code lang=”Swift”]
switch mañana {
case .lunes:
print(“Mañana es lunes”)
case .martes:
print(“Mañana es martes”)
case .miercoles:
print(“Mañana es miércoles”)
case .jueves:
print(“Mañana es jueves”)
case .viernes:
print(“Mañana es viernes”)
case .sabado:
print(“Mañana es sábado”)
case .domingo:
print(“Mañana es domingo”)
}
//Devuelve
//Mañana es miércoles
[/code]

Cómo comentamos en el capítulo de control de flujo, la sentencia Switch necesita que los casos sean exhaustivos, es decir, que se contemplen todos los posibles valores que esa variable pueda tomar. Como la enumeración DiaDeLaSemana solo tiene 7 casos posibles, no hay riesgo de que una variable de ese tipo adopte otro valor distinto a esos 7, por lo tanto no es necesario usar un default.

Sin embargo, si no hiciéramos mención a los 7 valores en los cases, deberíamos incluir un default:

[code lang=”Swift”]
switch mañana {
case .lunes:
print(“Mañana es lunes”)
case .martes:
print(“Mañana es martes”)
case .miercoles:
print(“Mañana es miércoles”)
case .jueves:
print(“Mañana es jueves”)
case .viernes:
print(“Mañana es viernes”)
default:
print(“Mañana comienza el fin de semana”)
}
//Devuelve
//Mañana es miércoles
[/code]

Raw Values, valores crudos de las enumeraciones en Swift

Cuando creamos una enumeración y definimos sus posibles cases, cada case es en sí mismo un valor. Por el contrario, en otros lenguajes de programación como C, cada case está asociado a un valor entero. De esta manera, el primer case es igual a 0, el segundo es igual a 1 y así sucesivamente.

Si quisiéramos emular este comportamiento de C deberíamos asociar cada case a un valor, conocido como Raw Value o valor crudo por su traducción. Sin embargo, Swift es mucho más potente que C y permite asignar valores de distintos tipos, como String, Character, Enteros o números de coma flotante.

Los Raw Value son valores prepopulados, o sea, que al momento de definir la enumeración se le asigna el valor que cada case va a contener. Lo primero que debemos hacer para poder asociar estos valores es definir el tipo de dato a continuación del nombre de la enumeración (conocido como raw type) y luego asignar un raw value a cada miembro del enum, como vemos en el ejemplo:

[code lang=”Swift”]
enum Mascota: String {
case perro = “El mejor amigo del hombre”
case gato = “La mascota más suave”
case loro = “El más parlanchín”
}

let firulais = Mascota.perro
print(“\(firulais.rawValue)”)
//Devuelve
//El mejor amigo del hombre
[/code]

En el ejemplo, el Raw Type es String y cada una de las cadenas de texto asignadas a los cases conforman los Raw Values de cada uno.

Raw Values implícitos

Cuando trabajamos con enumeraciones que guardan valores crudos enteros o cadenas de texto, no es obligatorio definir un valor para cada case.

En el caso de los String, si no se define un valor, el raw value asociado será el texto del case.

[code lang=”Swift”]
enum DiaDeLaSemana:String {
case lunes, martes, miercoles, jueves, viernes, sabado, domingo
}

var mañana = DiaDeLaSemana.miercoles
print(“\(mañana.rawValue)”)

//Devuelve
//miercoles
[/code]

Por otro lado, si trabajamos con enumeraciones que almacenan enteros, si no definimos ningún valor asociado el primer case valdrá 0, el segundo valdrá 1 y asi sucesivamente (de la misma manera que funciona en C).

A su vez, si al primer miembro le asignamos un valor, por ejemplo 4, el segundo valdrá 5, el tercero 6 … es decir que cada miembro sucesivo sumará uno al anterior.

[code lang=”Swift”]
enum Saiyan: Int {
case Goku = 3, Vegeta, Gohan, Trunks
}

let principeSaiyan = Saiyan.Vegeta
print(“\(principeSaiyan.rawValue)”)

//Devuelve
//4
[/code]

HashValue en las enumeraciones en Swift

Como vimos, podemos asignar un Raw Value a cada case y corresponde al valor que este almacena. Estos Raw Value solo están disponibles si indicamos un Raw Type a la enumeración, tal cual vimos.

Sin embargo, en todo momento tenemos acceso al hashValue que corresponde al índice del case dentro de la enumeración. Esto es, la posición de cada miembro arrancando con 0 para el primero.

En este sentido, el hashValue es igual al rawValue cuando tenemos una enumeración con un Raw Type del tipo Int y no indicamos un valor para cada case (es decir, que usamos los implícitos).

[code lang=”Swift”]
print(“\(principeSaiyan.hashValue)”)

//Devuelve
//1
[/code]

El ejemplo anterior devuelve 1 ya que Goku se encuentra en la posición 0 y Vegeta en el 1.

Enumeraciones con métodos

Al igual que en las clases y structs, podemos definir métodos dentro de los enums. Esta característica muestra la flexibilidad que las enumeraciones tienen en Swift y lo diferencia con las implementaciones en otros lenguajes de programación.

Los métodos que escribamos dentro de un enum siguen las mismas reglas que vimos en el capítulo de funciones.

[code lang=”Swift”]
enum Saiyan: Int {
case Goku = 3, Vegeta, Gohan, Trunks

func superPoder() -> String {
switch self {
case .Goku, .Gohan:
return “Kamehameha”
case .Vegeta, .Trunks:
return “Garlic Ho”
}
}
}

let principeSaiyan = Saiyan.Vegeta
print(“\(principeSaiyan.superPoder())”)

//Devuelve
//Garlic Ho
[/code]

Valores asociados en las enumeraciones en Swift

En algunos casos es conveniente asociar determinada información relevante para cada miembro de una enumeración. Swift permite que en cada case se incluya información adicional al momento de crear una variable o constante del tipo de la enumeración en cuestión. Se dice que esa información adicional es un valor asociado al case que lo contiene.

Por ejemplo, supongamos que tenemos que realizar una aplicación para una escuela y necesitamos hacer un inventario del personal que trabaja en ella. Si se trata de un profesor queremos que se guarde la materia que dicta pero si se trata de un preceptor queremos el curso que administra, referenciándolo con un número entero (es decir, el preceptor puede ser de 5to año). En este sentido, podríamos usar el siguiente enum:

[code lang=”Swift”]
enum EmpleadoEscuela {
case profesor(String)
case preceptor(Int)
}

let Gabriel = EmpleadoEscuela.profesor(“Programación”)
let Lionel = EmpleadoEscuela.preceptor(5)

switch Gabriel {
case .profesor(let materia):
print(“El profesor dicta la materia \(materia)”)
case .preceptor(let curso):
print(“El preceptor administra el curso \(curso)”)
}

//Devuelve:
//El profesor dicta la materia Programación
[/code]

Como vemos en el ejemplo, cada case define un tipo de dato asociado para recibir un valor al momento de la creación de la variable. Dentro de un enum, cada miembro puede definir los valores asociados que requiera, incluso de distinto tipo entre ellos. A su vez, cada case puede tener 0, 1 o n valores asociados. Por ejemplo, si quisiéramos tener más datos del profesor, como nombre, apellido y materia podríamos solicitar tres String, como vemos a continuación:

[code lang=”Swift”]
enum EmpleadoEscuela {
case profesor(String,String,String)
case preceptor(Int)
}

let Lionel = EmpleadoEscuela.profesor(“Lionel”,”Messi”,”Magia”)

switch Lionel {
case .profesor(let nombre, let apellido, let materia):
print(“El profesor \(nombre) \(apellido) dicta la materia \(materia)”)
case .preceptor(let curso):
print(“El preceptor administra el curso \(curso)”)
}

//Devuelve:
//El profesor Lionel Messi dicta la materia Magia
[/code]

En el ejemplo anterior estamos usando let para tener una copia del valor asociado que contiene la variable. Como no estamos modificando su valor dentro del case, no necesitamos crear una variable, pero puede hacerse perfectamente en caso de requerirlo.

Por otro lado, si todos los valores asociados son extraídos de la misma forma, es decir, todos como constantes (let) o todos como variables (var) es posible escribir let o var una vez sola al principio. Por lo tanto, el siguiente Switch es equivalente al anterior:

[code lang=”Swift”]
switch Lionel {
case let .profesor(nombre, apellido, materia):
print(“El profesor \(nombre) \(apellido) dicta la materia \(materia)”)
case .preceptor(let curso):
print(“El preceptor administra el curso \(curso)”)
}
[/code]

Computed properties en enumeraciones en Swift

Las enumeraciones pueden definir también computed properties o propiedades computadas (calculadas), por su traducción. Estos son propiedades que devuelven un valor que ha sido calculado previamente.

Esta característica muestra otra diferencia entre la implementación de las enumeraciones en Swift versus otros lenguajes como C.

A continuación, veremos un ejemplo de un enum que incorpora una computed property para informar la profesión de cada case posible:

[code lang=”Swift”]
enum EmpleadoEscuela {
case profesor(String,String,String)
case preceptor(Int)

var profesion:String {
switch self {
case .profesor(_,_,let materia):
return “Profesor de \(materia)”
case .preceptor(_):
return “Preceptor”
}
}
}

print(“\(Lionel.profesion)”)

//Devuelve:
//Profesor de Magia
[/code]

En el ejemplo, definimos una propiedad calculada para obtener un String que informa de la profesión. En el primer case, como solo necesitamos el dato de la materia, omitimos los dos primeros valores usando un guión bajo. De esta manera, le indicamos al compilador que no vamos a hacer uso de ellos y por lo tanto no se reserva espacios en memoria de manera innecesaria.

Descarga el archivo playground.
Siguiente capítulo: Manejo de errores.

Si quieres aprender más sobre que es iOS sigue este enlace con una guía definitiva sobre el mejor Sistema Operativo para móviles.
Name
Email
Review Title
Rating
Review Content
Enumeraciones en Swift
5,0 rating based on 12.345 ratings
Overall rating: 5 out of 5 based on 6 reviews.

3 opiniones en “Enumeraciones en Swift”

    1. Hola Marco, gracias por tu comentario. Realmente es un incentivo ver que los tutoriales que publicamos son de ayuda para la comunidad.
      Tenemos en mente seguir con el curso, aunque no sabemos realmente cuando publicaremos el próximo post. Espero que pronto … muy pronto!.
      Un abrazo. Gabriel

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *