Enumeraciones

Tutorial Swift – Enumeraciones

Josué V. Herrera Swift Básico, Tutoriales 0 Comments

En este Tutorial Swift  aprenderemos sobre las Enumeraciones, para qué son útiles, su implementación y como siempre, todo esto lo veremos a través de varios ejemplos de uso.

Las enumeraciones usualmente se definen como un conjunto de datos de un mismo tipo que agrupa valores que se relacionan entre sí. Pero esta definición no es del todo acertada en Swift ya que en este lenguaje las enumeraciones son mucho más flexibles, y una prueba de esto sería el hecho de no tener que proporcionar un valor para cada caso de la enumeración ya que el compilador lo infiere al mismo tiempo que sus valores pueden ser presentados como distintos tipos de datos, es decir, que podemos proporcionar un valor para cada elemento de la enumeración sin necesidad de definir un tipo, al mismo tiempo que cada uno de estos valores pueden ser de cualquier tipo de dato sin que esto sea contemplado como un error.

Algo realmente único es que en Swift las enumeraciones adoptan muchas características tradicionalmente soportadas por las clases, me refiero a las propiedades computadas (computed properties) y a los métodos instancia que nos permiten añadir información extra y funcionalidad relacionado con el valor asociado a cierto elemento de la enumeración, respectivamente. Las enumeraciones también pueden definir inicializadores para proporcionar un valor inicial para cada caso, se pueden expandir para ampliar su funcionalidad más allá de su implementación original, y pueden apoyarse en los protocolos para proporcionar funcionalidades estándar.

Sintaxis

Declaramos una enumeración con la palabra clave enum y colocamos toda su definición dentro de un par de llaves:

He aquí el primer ejemplo donde definimos los cuatro puntos cardinales:

Los valores definidos en esta enumeración (como Norte, Sur, Este y Oeste) son a los que llamamos los casos de la enumeración. Para esto, tal y como podemos observar, utilizamos la palabra clave case para definir nuevos casos.

Múltiples casos pueden aparecer en una sola línea, separados por comas:

Cada definición de una enumeración define un nuevo tipo. Al igual que otros tipos en Swift, sus nombres (como CompassPoint y Planet) deben comenzar con una letra mayúscula.

Podemos trabajar con los casos de nuestra enumeración de la siguiente forma:

El tipo de directionToHead se infiere cuando se inicializa con uno de los valores posibles de CompassPoint. Una vez que directionToHead se ha declarado como de tipo CompassPoint, podemos igualuarla a otro de los casos de CompassPoint y en esta ocasión utilizando una sintaxis mucho más corta: el punto.

El tipo de directionToHead ya es conocido, por lo que podemos omitir el tipo al establecer su valor.

La sentencia switch y las enumeraciones

Podemos también desde una sentencia switch trabajar con enumeraciones:

…la salida en pantalla sería:

Este código se pudiera leer de la siguiente forma:

“Considere el valor de directionToHead. En el caso en el que sea igual a .North, imprime “Un montón de planetas tienen un norte”. En el caso en el que sea igual .South, imprimir “Ten cuidado con los pingüinos”.”

…etcétera.

La sentencia Switch debe ser exhaustiva al considerar los casos de una enumeración. Si se omite el caso de .West por ejemplo, este código no compilaría, ya que no se estaría teniendo en cuenta la lista completa de los casos de CompassPoint. Es necesario ser bien cuidadosos con esto, para así asegurar que los casos de la enumeración no se omitan accidentalmente.

Cuando no es conveniente establecer una funcionalidad por cada caso de la enumeración, se puede proporcionar un caso por defecto para cubrir los casos que no se tratan de forma explícita, ejemplo:

…la salida en pantalla:

Valores Raw implicitamente asignados

Cuando trabajamos con enumeraciones que almacenan enteros o cadenas, usted no tiene que asignar explícitamente un valor para cada caso, cuando esto no se hace, Swift asignará automáticamente los valores por nosotros.

Por ejemplo, cuando se utilizan enteros para valores raw, el valor implícito para cada caso es uno más que el caso anterior. Si el primer caso no tiene un conjunto de valores, su valor es 0.

En el siguiente ejemplo mostramos una enumeración con valores raw enteros para representar el orden de cada planeta del sistema solar:

En el ejemplo anterior, Planet.Mercury tiene un valor raw explícito de 1, Planet.Venus tiene un valor implícito de 2, y así sucesivamente.

Lo mismo sucede cuando se utilizan cadenas de valores raw, el valor implícito para cada caso es el texto del nombre de ese caso.

La enumeración del ejemplo siguiente cuenta con valores raw de cadena para representar el nombre de cada dirección:

En el ejemplo anterior, CompassPoint.South tiene un valor raw implícito de “Sur”, y así sucesivamente. Podemos acceder al valor raw de un caso de la enumeración con su propiedad rawValue:

El valor de EarthsOrder es 3 y el de SunsetDirection es “Occidente”

Valores asociados

A veces es útil poder almacenar valores en distintos tipos de datos y tenerlos asociados a cierto caso de nuestra enumeración. Esto nos permitiría almacenar información personalizada adicional junto con el valor del caso. Por ejemplo cuando trabajábamos con CompassPoint veíamos 4 casos que hacían referencia a los puntos cardinales, bueno, pues quizás nos resulte útil, almacenar junto al valor que representa en sí, también las coordenadas de nuestra posición.

Podemos definir enumeraciones y almacenar valores asociados de cualquier tipo, y de ser necesario, los tipos de datos de estos casos pueden ser diferente para cada caso de la enumeración, es decir un caso puede recibir dos enteros, el siguiente una cadena de caracteres, y el tercero cuatro números de coma flotante, etcétera.

En el siguiente ejemplo podemos ver todo lo antes explicado:

De la línea 5 a la 41 tenemos la declaración de un enum llamado HeatLevel. Este enum representa la temperatura de cada hornilla de nuestra cocina de inducción (sí, así mismo, este será el objeto con el que trabajaremos, fue lo que se me ocurrió) en varios estados, estos últimos son los casos declarados de la línea 7 a la 11. En estas línea es donde aplicamos la asociación de valores, como podemos observar a diferencia del primero, todos los casos restantes terminan con el segmento (stove: Int) y en esto consiste la asociación de valores, estamos relacionando a cada uno de estos casos un valor entero al que nombramos como stove. El objetivo que perseguimos al asociar este valor a cada caso de la enumeración es que cuando se establezca un nivel de temperatura también se especifique la hornilla a la que le aplicaremos este valor.

De la línea 15 a la 17 dentro de la función levelMessage() declaramos una sentencia switch, en la cual tal y como explicamos anteriormente en este artículo, tenemos que incluir todos los casos de la enumeración, de lo contrario el compilador nos mostrará un mensaje de error que nos impedirá compilar el código, la única forma de solventar esto sin especificar todos los casos es declarando default: dentro del bloque switch. Esta función determina el estado de la hornilla y lo devuelve en una cadena de texto.

Luego de las línea 43 a la 130 tenemos la declaración de la clase que representa una cocina de inducción. En esta tenemos varios códigos que se van del tema de este artículo excepto el método getStoveNumber() el cual dado una enumeración de tipo HeatLevel nos devuelve el valor asociado a esta, es decir nos devuelve el entero que representa la hornilla a la que está aplicada el nivel de temperatura que representa ese caso. Como podemos observar es bastante rudimentaria en esta versión de Swift 2 la manera que tenemos para determinar el valor asociado a un caso de una enumeración, esto no me gusta para nada y personalmente jamás implementaría algo así, no por el hecho de que tengamos que iterar por cada caso ya que en Swift los bloquees switch no funcionan de esa manera (es decir no son Fallthrough) por lo que cuando encuentran un resultado positivo automáticamente terminan su ejecución, no es necesario una sentencia break) es que sencillamente no es la solución más óptima, lo muestro solamente para que puedan ver la manera con la que actualmente contamos para obtener estos valores asociados, demasiado trabajo para algo que podemos lograr de otras formas más sencillas.

Como ya comenté, el resto de código no tiene nada que ver con las enumeración por lo que de comentarlo solamente haría este artículo mucho más extenso añadiendo información no relacionada con el tema que estamos discutiendo. Quizás hayan fragmentos de código que no entiendan, ya que en este ejemplo hay varias técnicas o funcionalidades de Swift de las que no he escrito aun en este sitio, los invito a que si no entienden algo, o sencillamente tiene alguna duda, no duden en dejar un comentario formulando su inquietud. Yo con mucho gusto las contestaré y luego profundizaré sobre el tema en un futuro Tutorial Swift.

La salida del código anterior es la siguiente:

Espero que todo cuanto se ha dicho aquí, de una forma u otra le haya servido de aprendizaje, de referencia, que haya valido su preciado tiempo.

Este artículo, al igual que el resto, será revisado con cierta frecuencia en pos de mantener un contenido de calidad y actualizado.

Cualquier sugerencia, ya sea errores a corregir, información o ejemplos a añadir será, más que bienvenida, necesaria!