Home » Cursos » Swift 2 Lección 6, Enumeraciones
Curso Swift Lección 6 Enumeraciones

Swift 2 Lección 6, Enumeraciones

Compra "Aprendiendo Swift 3"

La presente lección está probada y certificada en su funcionamiento con la versión 7 o superior de Xcode y corresponde a la especificación 2 y 2.1 del lenguaje Swift.
Enumerar, exponer o nombrar de manera sucesiva y uno tras otro los elementos que forman un conjunto, una serie o un todo. Esa es la definición del diccionario y nos da un idea clara de qué son las enumeraciones. Son un conjunto de datos de un mismo tipo que agrupa valores que se relacionan entre sí. Normalmente se usan para acotar posibles características de una propiedad, donde uno de los valores de la enumeración es asignado a una variable tipificada en esta.

Por ejemplo, si tenemos una propiedad de la clase vehículo que es ruedas, es fácil saber qué valores vamos a poner: 1, 2, 3 o 4. Podemos usar un entero y poner los valores. Pero, ¿y sí queremos que estos sean los únicos posibles valores de nuestro campo? Usamos las enumeraciones.

Las enumeraciones tienen dos tipos de valores: la descripción y el valor interno o valor hash. Nosotros siempre trabajaremos con la descripción, pero internamente el sistema les asigna un valor y es como reconoce cada correspondencia. Si definimos un enum como en el ejemplo, tal cual y sin tipo, los valores siempre se asociarán de manera automática y solo dispondremos de cada valor como propiedad del tipo, aunque podremos acceder a su hashValue.

Captura enum Swift 2

Si escribimos ruedas. veremos que inmediatamente se despliega un selector para elegir cualquiera de sus posibles valores.

Mientras la primera instrucción nos devolverá la descripción de la enumeración, la llamada a hashValue nos devolverá , que es el índice interno que ha autoasignado nuestra enumeración.

También podemos establecer nosotros qué valor queremos que tenga internamente para poder usarlo. Por ejemplo, si queremos que además de tener nuestra descripción, su valor interno corresponda con el número de ruedas real. Para ello usamos otro concepto que es el valor raw o de clasificación. Este solo puede usarse si tipificamos el enum a un tipo de valor, como un String, un Float, un Int o un Character. Estos son los únicos tipos soportados, pues son tipos que el sistema conoce como hashables o que pueden tener un índice interno de clasificación.

Si cambiamos la definición de ruedas por la nueva con valores fijos, cuando accedemos a hashValue o a rawValue, vemos que los datos son diferentes. El valor dos, tiene el hashValue 1 (su índice en función del lugar en que se ha enumerado) pero su rawValue es 2, con lo que si queremos usar el valor numérico de ruedas en alguna función solo tendremos que acceder al rawValue de la propiedad que tenga el número de ruedas y se asignará sin problema como entero.

De hecho, como los valores son consecutivos, podemos omitir los valores posteriores al 1 y el sistema entenderá que los demás valores son una sucesión de números donde ha de sumar uno a cada nuevo valor.

Si volvemos a buscar el rawValue de ruedas.dos, veremos que sigue siendo el entero 2 aunque no lo hayamos asignado. Esto resulta muy útil cuando tenemos muchos valores. Tenemos que notar que cualquier valor que dejemos en blanco, habiendo asignado uno anterior, siempre incrementará el siguiente. Si pusiéramos que dos fuera igual a 3, tres sería igual a 4 y cuatro a 5.

A nivel de definición podemos separar por comas con una sola instrucción case los diferentes valores o podemos poner uno en cada línea antecedido de case. Es una opción a escoger según nuestras necesidades estéticas de código o cómo queramos que se entienda.

La forma de usar estas enumeraciones es asignarlas como tipo de dato a una variable.

Como la variable numRuedas ya está tipificada de tipo ruedas, no tenemos que poner ruedas.una, si no que simplemente ponemos un punto y el valor. Asignando .una el sistema ya sabe al enum que corresponde y valida en tiempo de edición que la asignación y el valor sea correcto en todos los casos.

Trabajando con los enums

Una de las funciones más comunes para una enumeración es usar un switch que nos permita diferentes flujos en función de un valor enumerado. La ventaja de poder usar los datos descriptivos y que sus valores internos sean transparentes añade una gran versatilidad para definir tipos de estado de una app, tipos de personajes de un juego o diferentes tipos o estados de cualquier tipo de objeto y actuar en consecuencia.

La instrucción switch en este caso nos permite no incluir una opción default ya que en nuestro caso, todas las posibles opciones de valor que tiene numRuedas están contempladas, por lo que nos ahorramos esa parte. La seguridad de Swift nos obliga siempre a tener en cuenta que ningún switch se quede sin evaluar ninguno de sus casos, y en este caso, los hemos contemplados todos. Es lo que se llama modo exhaustivo. En caso de no incorporarlos todos, entonces sí deberemos usar el default, obviamente.

Una cosa muy interesante que tiene además Swift 2, es la posibilidad de crear una enumeración tipificada a tipo String y de esta forma, cada valor de case que le demos, tendrá su equivalente rawValue como cadena, conservando el mismo valor que hemos dado como enum.

Si nos fijamos, el rawValue es el mismo valor del case pero en una cadena, inicializada directamente.

Valores asociados

Otra de las prácticas que podemos hacer con las enumeraciones es establecer valores a un tipo concreto y luego supeditar su correspondencia no solo al tipo concreto de enumeración si no a uno o varios valores asociados. Cada case contemplará un único caso, pero obteniendo el valor asociado con una asociación directa, podemos trabajar con el dato que contiene cada caso.

Vamos a suponer un juego donde queremos controlar el tipo de daño que queremos infringir a un enemigo en función de cada arma que usemos. Pero queremos que el daño del arma no sea lineal si no que podamos tener varios niveles de daño y un número de puntos que haga a los enemigos y que podrá variar en el transcurso del juego (como un arma de un juego de rol convencional que va cogiendo más poder y daño en el transcurso del juego).

Para ello creamos un enum que nos permita discernir el tipo de golpe que podemos dar con cada una de las armas: leve, medio, fuerte o mortal. Pero además, nos interesa que el valor de dicho golpe esté almacenado en el propio enum, porque habrá casos donde un golpe medio haga un daño y otros donde infrinja un poco más que otra, siendo también medio.

De esta simple forma, hemos extendido la funcionalidad del enum porque ahora la casuística no estará limitada solo al tipo, sino también al valor interno que almacenemos y que indique cuántos puntos quitamos al infligir dicho daño. Salvo en el golpe mortal donde quitamos todos los puntos que se tengan. Hemos de tener presente que el enum es un valor único de por sí de los posibles dentro de una variable, por lo que podemos asignar sola una de las opciones a cada variable de tipo golpe.

Tenemos una clase arma donde una de sus propiedades va a ser el tipo de golpe. La definimos como opcional porque aun no sabemos qué valor vamos a darle. Luego creamos una constante espada donde instanciamos la clase arma y asignamos a espada el tipo de golpe que dará, en este caso un golpe medio que infrinja un daño de 4.

Vamos a aplicar un caso práctico de daño a un personaje. Creamos nuestra clase personaje con las propiedades vida y fuerza a 100 y creamos un guerrero.

Ahora vamos a golpearlo con el arma. Vamos a suponer que el daño leve quita a la vida los puntos que tenga asignada el arma, el daño medio quita el doble de los puntos, el fuerte el doble de los puntos pero también resta dichos puntos a la fuerza y el mortal, obviamente, deja la vida a 0 y mata al personaje.

Si ahora vemos el valor de vida del personaje, veremos que la vida de este es 92, es decir el doble de los puntos porque hemos establecido que la espada da un golpe medio de daño 4.

¿Qué pasa en un buen juego de rol cuando matamos muchos enemigos con un arma? Que esta sube su poder y el daño que infringe. Vamos a suponer que queremos aumentar el daño de la espada. Solo tenemos que cambiar el tipo de golpe que dará y el número de puntos. Vamos a cambiar entonces para que espada de un golpe fuerte de 8 puntos.

Si volvemos a preguntar por los valores del personaje tras el cálculo de su daño, veremos que ahora ha infringido daño en vida y además en fuerza. De esta simple manera, una misma arma puede infringir diferentes daños y tipos de daño.

En el switch también podemos supeditar valores de la variable interna usando la sentencia where, de forma que por ejemplo, para un mismo caso, si el valor interno cumple una condición hará una cosa y si cumple otra se irá a otro flujo.

En este caso, el golpe medio tiene dos casos: si estamos dando un golpe medio y el valor de puntos que lleva el enum es menor o igual a 5, entonces hacemos un daño que sea el doble de los puntos. Pero si es mayor de 5, entonces hacemos que el daño sea el triple. Como vemos, así podemos obtener flujos extra y conseguir que para un mismo enum podamos tener diferentes casos.

Resumiendo

La enumeración, como hemos visto, permite controlar diferentes tipos de valores acotados y es muy práctico cuando queremos controlar algún tipo de algo de una forma que sepamos, más semánticamente, en qué se distinguen los diferentes tipos.

Como siempre, os invitamos a que probéis, inventéis vuestros propios casos y juguéis con ellos. Además, repasad si es necesario todas las lecciones del curso de Swift. Hasta la próxima lección, y como siempre, Good Apple Coding.

Compra "Aprendiendo Swift 3"

Acerca de Julio César Fernández

Analista, consultor y periodista tecnológico, desarrollador, empresario, productor audiovisual, actor de doblaje e ingeniero de vídeo y audio.

Otras recomendaciones

Lecciones Prototipos (I): UITableView

Lecciones por prototipos (I): Vistas de tabla (UITableView)

Primera lección por prototipos, un nuevo e innovador contenido. A veces, lo normal es que nos perdamos sin terminar de entender qué es o cómo funcionan los componentes que forman parte de una app o un juego. Para este caso hemos creado las lecciones por prototipos. Una exploración básica de conceptos esenciales a través de prototipos en Playground que podemos probar con Swift Playgrounds en el iPad o con Xcode 8. En esta primera lección abordamos las UITableView (vistas de tabla). Un elemento esencial en la mayoría de apps de iOS que muchas veces no es entendido desde su base y por lo tanto, provoca un mal uso de las mismas.

  • Moises

    Excelente explicación, espero siga dando enseñanzas de este tipo. Un saludo.

  • .[Roberto Hevens]

    Buen día Julio:

    ¿Es necesario crear un juego App para aprender a hacer Apps con swift?, a mi personalmente no me entusiasma pensar juegos o hacerlos o jugarlos.

    • Evidentemente no, pero los ejemplos de juegos suelen ser más cercanos a la mayoría de la gente que se acerca a la programación y no tanto un app de gestión de algún tipo o el manido ejemplo de la calculadora. Es una elección basada en la experiencia, pero es fácilmente extrapolable a cualquier otro contexto sin problema.
      Un saludo.