Swift – Control de flujo

Control de flujo

Uno de los temas que conforman el ABC del aprendizaje de un lenguaje de programación es sin dudas el del control de flujo.

Esto refiere a unos comandos que podemos usar para modificar el orden secuencial de un programa, permitiéndoles saltarse líneas, ejecutar varias veces una misma sentencia, hacer decisiones en base a condiciones, etcétera.

Bucles

Los bucles o loops nos permiten ejecutar un algoritmo una cierta cantidad de veces, dependiendo de la condición que se utilice o el rango de inicio y fin.

For-in

Básicamente existen dos escenarios donde podemos usar este tipo de bucles. El primero se da cuando se necesita recorrer una colección de ítems, como es el caso de un array o un diccionario.

//iteración sobre un array
let array = ["Uno", "Dos", "Tres"]

for item in array {
print("Hola \(item)")
}

//Devuelve:
//Hola Uno
//Hola Dos
//Hola Tres

//Iteración sobre un diccionario
let stock = ["Mouse": 5, "Teclado":20, "Pen drive": 10]

for (dispositivo, cantidad) in stock {
print("Se disponen de \(cantidad) unidades de \(dispositivo)")
}

//Devuelve:
//Se disponen de 5 unidades de Mouse
//Se disponen de 20 unidades de Teclado
//Se disponen de 10 unidades de Pen drive

Como se puede apreciar, al recorrer un diccionario se obtiene, en cada iteración, una tupla (dispositivo, cantidad) al que luego se puede acceder haciendo referencia a sus nombres y solo existe mientras dure el ciclo del for.

El otro escenario donde se usa este tipo de loop se da cuando se conoce el rango sobre el cual se quiere iterar. Por ejemplo, si quisiéramos contar desde 1 a 4 podríamos hacer:

for numero in 1...4 {
print(numero)
}

//Devuelve
//1
//2
//3
//4

En cada ejecución del bucle, la constante numero va tomando los valores del rango, empezando por 1 y terminando por 4. En el ejemplo, se está usando el operador de rango cerrado (…) que implica que se incluye en el rango tanto el 1 como el 4.

Operadores de rango

Como te estarás imaginando, ese no es el único tipo de operador de rango que existe, sino que podemos optar por los siguientes:

  • Operador de rango cerrado (…): es el que ya vimos. Incluye los bordes.
  • Operador de rango semi abierto (..<): incluye el rango de inicio pero excluye el final. Por ejemplo, 1..<4 toma los valores 1, 2 y 3.
  • Operador de rango a un lado (n…): se llama así ya que permite indicar el numero de posición del elemento desde el que se quiere iterar hacia delante lo más posible. Si tomamos el ejemplo del primer array y usamos un rango de (1…) se incluirían los valores “Dos” y “Tres” ya que se está indicando que se comience por el valor de la posición 1. Recordemos que los array comienzan en el índice 0. De la misma manera, se pueden hacer rangos a un lado a la inversa (…n) para indicar que se quiere iterar desde el primer valor hasta la posición n.
  • Operador de rango semi abierto a un lado (..<n): es la combinación de los dos anteriores. Se especifica desde o hasta donde queremos iterar (a un lado) pero dejando el borde abierto.

stride

Existe una manera especial de indicar rangos en la que se permite “saltearse” valores. Por ejemplo, si quisiéramos contar de 2 en 2 desde 0 a 10 podemos usar stride(from:through:by:)

for numero in stride(from: 0, through: 10, by: 2) {
print(numero)
}

//Devuelve:
//0
//2
//4
//6
//8
//10

Si quisiéramos hacer lo mismo pero sin incluir el 10, podemos usar stride(from:to:by:)

for numero in stride(from: 0, to: 10, by: 2) {
print(numero)
}

While

Este tipo de bucle realiza iteraciones hasta que una condición dada devuelva false. Existen dos tipos:

WHILE

Primero se chequea la condición. Si la misma devuelve true, se accede al bucle. La sintaxis es la siguiente:

while condición {
sentencias
}
var contador = 0

while contador < 10 {
print("Contador: \(contador)")
contador += 1
}

//Devuelve:
//Contador: 0
//Contador: 1
//Contador: 2
//Contador: 3
//Contador: 4
//Contador: 5
//Contador: 6
//Contador: 7
//Contador: 8
//Contador: 9

En el ejemplo anterior, se define un contador con un valor inicial de 0. Luego se repiten dos sentencias (se imprime por pantalla el valor del contador y se lo incrementa en 1) siempre que ese contador sea menor a 10. Por lo tanto, el bucle va a ejecutarse hasta que el contador llegue al valor 9. Una vez que valga 10, no va a cumplir la condición del while y por lo tanto no va a volver a ingresar.

Repeat-While

Primero se realiza una iteración del bucle y luego se chequea la condición. Si la misma devuelve true, vuelve a ejecutar el bucle hasta que la condición sea false. Este bucle se utiliza cuando se sabe que por lo menos una vez es necesario ejecutar el algoritmo que contiene. La sintaxis es la siguiente:

repeat {
sentencias
} while condición
contador = 0

repeat {
print("Contador: \(contador)")
contador += 1
} while contador < 10

//Devuelve:
//Contador: 0
//Contador: 1
//Contador: 2
//Contador: 3
//Contador: 4
//Contador: 5
//Contador: 6
//Contador: 7
//Contador: 8
//Contador: 9

Aquí se vuelve a inicializar el contador a 0 y a continuación ya se ejecuta el primer ciclo del bucle, en donde se realizan las dos mismas operaciones que en el ejemplo anterior. La diferencia radica en que en este caso siempre se ejecuta por lo menos una vez el bucle, mientras que en el ejemplo del while solo se ingresa si cumple la condición inicial.

Sentencias condicionales

Este tipo de sentencias nos permiten tomar decisiones a lo largo del programa y ejecutar determinadas líneas de código siempre que se cumpla alguna condición. Existen dos sentencias para ello: if y switch.

If

Esta sentencia se usa para las condiciones más simples que poseen pocas opciones.

let promedio = 9

if promedio >= 7 {
print("Curso aprobado!")
}

//Devuelve:
//Curso aprobado!

En este ejemplo se valida si la constante promedio es mayor o igual a 7. En ese caso se imprime “Curso aprobado!” y en caso contrario no se está realizando ninguna acción.

Asimismo, se pueden anidar varios if en caso de necesitar preguntar por nuevas condiciones.

let alumnos = 20

if alumnos < 10 {
print("Hoy faltaron muchos alumnos")
} else if alumnos < 17 {
print("Vinieron muchos alumnos")
} else {
print("Hoy vinieron todos!")
}

//Devuelve:
//Hoy vinieron todos!

En este ejemplo, se crea una constante con la cantidad de alumnos que vinieron a clase y se le asigna un 20. Luego se pregunta si vinieron menos de 10 alumnos y en caso de ser cierto, se imprime “Hoy faltaron muchos alumnos”. En caso de que esa condición no sea cierta (o sea, si el valor es igual o mayor a 10) se pasa a la segunda condición, donde solo ingresaría si el valor es menor a 17. Como tampoco es correcto, ingresa al else el cual se ejecuta en cualquier otro caso. Como ninguno de los condicionantes anteriores devolvieron true, ya que la cantidad de alumnos es 20, se imprime por pantalla “Hoy vinieron todos!”.

Si bien se pueden anidar varios else-if, no resulta práctico si se tienen muchas condiciones que validar. En ese caso, conviene considerar la posibilidad de usar switch.

Switch

Esta sentencia resulta práctica cuando se tienen que validar una gran cantidad de condiciones. Switch compara un determinado valor con una serie de opciones y en caso de validar alguna o algunas de ellas, se ejecuta el código que la misma contiene.

El uso de esta sentencia en Swift es realmente muy poderoso ya que en cada opción se puede hacer uso de varios patrones y no solamente valores constantes como en otros lenguajes de programación.

La sintaxis de este comando es:

switch valor a considerar {
case valor 1:
sentencias para el valor 1
case valor 2,
valor 3:
sentencias para el valor 2 y 3
default:
caso contrario, hacer otra cosa
}

Como se puede observar, primero se define un valor determinado que luego se va a comparar con otros valores, dentro de cada case. La gran ventaja de Swift es que en cada case no solamente se pueden utilizar valores constantes sino que existe una gran cantidad de patrones que podemos usar, lo cual genera una gran flexibilidad en nuestro código.

Una vez que alguno de los case coincide o “hace match” con el valor comparado, se ejecuta el bloque que el mismo encierra, de la misma manera que sucede con el if.

Un punto a tener en cuenta es que la sentencia switch debe ser exhaustiva. Esto implica que dado un valor a considerar, se deben especificar dentro de los case todos los casos posibles que ese valor puede llegar a tener. En los casos en los que no es posible hacerlo o el programador no tiene intenciones de dar un comportamiento en particular para algún valor determinado, se puede usar la palabra reservada default. Al hacer uso de éste se cubren todos los demás casos no especificados en cada case.

A diferencia de otros lenguajes, no se debe usar break al final de cada case, ya que al ingresar a uno se ejecutan las líneas de código que este contiene y se sale del switch automáticamente.

let personajeAnime = "Goku"

switch personajeAnime {
case "Goku":
print("Kamehameha!!")
case "Vegeta":
print("Final Flash!!")
default:
print("nada")
}

//Devuelve:
//Kamehameha!!

En el ejemplo, se crea un personaje de Anime y se pregunta en cada casepor un personaje en particular. Como el primer case es el que valida la sentencia switch, se ejecuta el print y se devuelve “Kamehameha!!”. Es importante destacar que la sentencia default es obligatorio ponerlo ya que si la constante personajeAnime no vale ni Goku ni Vegeta no se sabría que hacer (definición de exhaustivo). Siempre es obligación cubrir todos los valores posibles.

Fallthrough

Otro punto a tener en cuenta es que cada case debe tener un bloque de ejecución (no puede estar vacío y no contener sentencias en su interior). El siguiente switch no es correcto:

switch personajeAnime {
case "Goku":
case "Gohan":
print("Kamehameha!!")
case "Vegeta":
print("Final Flash!!")
default:
print("nada")
}

//Devuelve el error:
//error: ‘case’ label in a ‘switch’ should have at least one executable statement

En su lugar, se pueden poner dos valores distintos en el mismo case, separado por una coma o usar la palabra fallthrough en el primero de ellos:

switch personajeAnime {
case "Goku":
fallthrough
case "Gohan":
print(“Kamehameha!!”)
case "Vegeta":
print("Final Flash!!")
default:
print("nada")
}

//Devuelve:
//Kamehameha!!

switch personajeAnime {
case "Goku", "Gohan":
print("Kamehameha!!")
case "Vegeta":
print("Final Flash!!")
default:
print("nada")
}

//Devuelve:
//Kamehameha!!

Como vemos, fallthrough hace que al terminar de ejecutar ese casesiga ejecutando el case siguiente.

Comparación por intervalos

En el caso de tener que tomar una acción determinada dependiendo de un valor numérico en el switch, se pueden usar rangos para agrupar acciones dentro de un mismo case. De esta forma, suponiendo que se debe construir un algoritmo que indique si un examen se encuentra aprobado o desaprobado, se puede utilizar las siguientes líneas:

let nota = 5

switch nota {
case 0…6:
print("Examen desaprobado")
case 7…10:
print("Examen aprobado")
default:
print("Nota inexistente")
}

//Devuelve:
//Examen desaprobado

En el ejemplo anterior se utilizan rangos para agrupar las notas comprendidas entre 0 y 6 por un lado, y de 7 a 10 por otro lado. En ambos casos, los extremos del rango están incluidos en el case. Por último, dado que la sentencia switch debe ser completa, es obligatorio el uso del default para cualquier nota menor a 0 o mayor a 10, algo que en la práctica no debería ser posible. Cabe aclarar que cualquier operador de rango visto anteriormente puede ser usado dentro del case.

Vinculación de valores y where

La sentencia switch permite crear constantes o variables temporales en cada case para poder usar el valor que se está comparando dentro del cuerpo de ese case, en caso de entrar al mismo. Esta técnica es conocida como value binding (vinculación de valores). Asimismo, se puede utilizar la sentencia where para agregar condiciones adicionales y hacer más específica la comparación de ese case.

let numero = 18

switch numero {
case let x where x%2 == 0:
print(“Número par: \(x)”)
case let x where x%2 != 0:
print(“Número impar:\(x)”)
default:
print(“Nada”)
}

//Devuelve:
//Número par: 18

En el ejemplo, se crea una constante numero y se le asigna el valor 18. Luego en el primer case se pregunta en el where si ese valor es divisible por 2 para comprobar si es par y en el segundo case, si es impar. En ambos casos, se crea una constante x para almacenar ese valor y luego imprimirlo usando print. Como el numero es par, se obtiene como resultado “Número par: 18”.

break

Como dijimos anteriormente, la sentencia switch debe ser exhaustiva, por lo tanto todos los casos posibles deben ser cubiertos por los case. Sin embargo, en muchas ocasiones no hay necesidad de tomar alguna acción en algunos case y para esto es muy útil la sentencia break. En otras palabras, break nos permite terminar la ejecución del switch para que el flujo del programa continue inmediatamente luego de éste. Se puede decir que ésta es la manera en la que Swift nos permite ignorar determinados case.

De esta manera, una forma más elegante de escribir el código anterior es:

let numero = 18

switch numero {
case let x where x%2 == 0:
print("Número par: \(x)")
case let x where x%2 != 0:
print("Número impar:\(x)")
default:
break
}

//Devuelve:
//Número par: 18

Ejemplos completos

//iteración sobre un arreglo
let array = ["Mundo", "Gabriel", "Vecino"]

for item in array {
    print("Hola \(item)")
}

//Devuelve:
//Hola Mundo
//Hola Gabriel
//Hola Vecino

//Iteración sobre un diccionario
let stock = ["Mouse": 5, "Teclado":20, "Pen drive": 10]

for (dispositivo, cantidad) in stock {
    print("Se disponen de \(cantidad) unidades de \(dispositivo)")
}

//Devuelve:
//Se disponen de 5 unidades de Mouse
//Se disponen de 20 unidades de Teclado
//Se disponen de 10 unidades de Pen drive

for numero in 1...4{
    print(numero)
}

//Devuelve
//1
//2
//3
//4

for numero in stride(from: 0, through: 10, by: 2) {
    print(numero)
}

//Devuelve:
//0
//2
//4
//6
//8
//10

for numero in stride(from: 0, to: 10, by: 2) {
    print(numero)
}


var contador = 0
while contador < 10 {
    print("Contador: \(contador)")
    contador += 1
}

//Devuelve:
//Contador: 0
//Contador: 1
//Contador: 2
//Contador: 3
//Contador: 4
//Contador: 5
//Contador: 6
//Contador: 7
//Contador: 8
//Contador: 9

contador = 0
repeat {
    print("Contador: \(contador)")
    contador += 1
} while contador < 10

//Devuelve:
//Contador: 0
//Contador: 1
//Contador: 2
//Contador: 3
//Contador: 4
//Contador: 5
//Contador: 6
//Contador: 7
//Contador: 8
//Contador: 9

let promedio = 9
if promedio >= 7 {
    print("Curso aprobado!")
}

//Devuelve:
//Curso aprobado!

let alumnos = 20
if alumnos < 10 {
    print("Hoy faltaron muchos alumnos")
} else if alumnos < 17 {
    print("Vinieron muchos alumnos")
} else {
    print("Hoy vinieron todos!")
}

//Devuelve:
//Hoy vinieron todos!


let personajeAnime = "Goku"
switch personajeAnime {
case "Goku":
    print("Kamehameha!!")
case "Vegeta":
    print("Final Flash!!")
default:
    print("nada")
}

//Devuelve:
//Kamehameha!!

switch personajeAnime {
case "Goku":
    fallthrough
case "Gohan":
    print("Kamehameha!!")
case "Vegeta":
    print("Final Flash!!")
default:
    print("nada")
}

//Devuelve:
//Kamehameha!!

switch personajeAnime {
case "Goku", "Gohan":
    print("Kamehameha!!")
case "Vegeta":
    print("Final Flash!!")
default:
    print("nada")
}

//Devuelve:
//Kamehameha!!

let nota = 5
switch nota {
case 0...6:
    print("Examen desaprobado")
case 7...10:
    print("Examen aprobado")
default:
    print("Nota inexistente")
}

//Devuelve:
//Examen desaprobado

let numero = 18
switch numero {
case let x where x%2 == 0:
    print("Número par: \(x)")
case let x where x%2 != 0:
    print("Número impar:\(x)")
default:
    break
}
//Devuelve:
//Número par: 18

Swift – Aspectos básicos

Constantes y variables

Cualquier tipo de aplicación necesita poder guardar datos temporalmente en la memoria para poder trabajar con ellos. Las constantes y variables son apartados reservados en la memoria del dispositivo para que podamos acceder a la información que contienen durante la ejecución del programa.

En este sentido, se define una variable como un espacio en memoria que puede ser modificado por un algoritmo y una constante como una zona de memoria de solo lectura, que no puede ser modificada una vez se le haya asignado un valor inicial.

Para poder utilizar tanto las variables como las constantes, debemos definir un nombre descriptivo de su contenido y debemos asociarles además un tipo de dato según la información que vayan a contener.

En Swift, para definir una variable debemos utilizar la palabra reservada var mientras que para definir una constante, debemos utilizar la palabra reservada let:

let maxAlumnosGrupo = 30
var numAlumnosGrupo = 26

En el ejemplo anterior estamos declarando una constante con la cantidad máxima de alumnos en una clase. Dado que esa cantidad no variará, es conveniente definirla como una constante. Por otro lado, estamos declarando una variable con la cantidad de alumnos que actualmente se encuentran en la clase, y cuyo valor inicial hemos establecido en 26. Este valor puede ir modificándose a medida que transcurre el tiempo, y por ese motivo debemos utilizar obligatoriamente una variable para guardar este dato.

Hemos mencionado además que las variables y las constantes se asocian a nombres, tipos de datos y valores, pero en el ejemplo anterior sin embargo, no hemos especificado el tipo de dato de la variable o la constante. Esto es posible porque Swift utiliza inferencia de tipos, es decir, que “adivina” el tipo de dato que se quiere guardar en memoria utilizando como referencia el valor que le hayamos asignando inicialmente.

Si observamos el ejemplo anterior, en ambos casos asignamos valores numéricos enteros, por lo tanto Swift infiere que ambos son del tipo Int.

En cualquier caso, si quisiéramos indicar nosotros mismos el tipo de dato de manera explícita podríamos especificarlo después del nombre de la variable utilizando los dos puntos (:) como separador, tal como se observa en el siguiente ejemplo:

let maxAlumnosGrupo: Int = 30
var numAlumnosGrupo: Int = 26
let nombreProfesorInformatica: String = "Fernando"

En el ejemplo podemos observar que escribimos el nombre de la variable o constante, seguida de dos puntos, el tipo de dato y por último el valor que queramos asignar inicialmente.

Imprimiendo valores

Para imprimir el valor de una constante o una variable en la consola, podemos utilizar la función  print(_:separator:terminator:):

let nombreProfesorInformatica: String = "Fernando"
print(nombreProfesorInformatica)

// Imprime: Fernando

La función print es global y nos permite imprimir uno o más valores. Posee dos parámetros que tienen valores por defecto, los cuales son separator y terminator. Esto implica que al momento de invocar a la función print, estos parámetros pueden ser omitidos por completo.

En caso de no especificar nada, esta función añade al final un salto de línea. Si se quiere indicar que el cursor se mantenga en la misma línea, se debe pasar un string vacío al parámetro terminator:

let nombreProfesorInformatica = "Fernando"
print(nombreProfesorInformatica, terminator:"")

// Imprime: Fernando

Una técnica muy utilizada a la hora de imprimir valores usando print, es la de interpolación de cadenas. Gracias a ella se puede especificar un espacio dentro de la cadena de texto para que sea reemplazado por el valor de una variable o constante al momento de ejecutarse. Se utiliza una barra invertida y se encierra la variable o constante entre paréntesis:

let maxAlumnosGrupo = 30
var numAlumnosGrupo = 26
print("De \(maxAlumnosGrupo) alumnos matriculados, hoy han venido \(numAlumnosGrupo)")

// Imprime: De 30 alumnos matriculados, hoy han venido 26

A diferencia de otros lenguajes, Swift no requiere el uso del punto y coma al finalizar una sentencia. Sin embargo, está permitido su uso:

let nombreProfesorInformatica = "Fernando";
print(nombreProfesorInformatica);

// Imprime: Fernando

Comentarios

Es muy común escribir texto dentro del código que no queremos que sea ejecutado pero que nos sirve como anotaciones o recordatorios. Estos comentarios son ignorados por el compilador y no son ejecutados.

Existen dos tipos de comentarios. Por un lado, tenemos a los de línea, quienes comienzan con dos barras o diagonales:

// Esto es un comentario de linea

Por otro lado, tenemos los comentarios multilineas, que se encierran entre /* y */ :

/* Esto es un comentario
de
varias
líneas */

Operadores básicos

Los operadores básicos incluidos en Swift son los siguientes:

  • Adición (+)
  • Sustracción (-)
  • Multiplicación (*)
  • División (/)
  • Resto (%) –> hace referencia al resto de una división. Ej: 10/3 es igual a 3 y sobra 1. Por lo tanto, el resto es 1.
  • Asignación (=) -> Se usa para darle un valor a una variable o constante, como vimos en los ejemplos.
  • Igual a (==)
  • Distinto a (!=)
  • Mayor a (>)
  • Menor a (<)
  • Mayor o igual (>=)
  • Menor o igual (<=)

Tipos de datos

En Swift podemos encontrar los siguientes tipos de dato:

  • Números enteros: Int y UInt
  • Números de coma flotante: Float y Double
  • String y Character
  • Bool
  • Tuplas
  • Tipos de Colección: Array, Set y Dictionary

En este post vamos a ver los usos más comunes de cada uno. Cabe mencionar que los tipos de dato se escriben con Mayúsculas la primer letra.

Números enteros

Los números enteros son aquellos que no poseen parte decimal o fracción, como el 1, 2, 3, 58 y -90. Existen dos tipos:

  • Con signo (signed): Son aquellos que pueden ser negativos, cero o positivos. Existen en sus versiones de 8, 16, 32 y 64 bits llamados de la siguiente manera:
    • Int8
    • Int16
    • Int32
    • Int64

Asimismo, Swift provee una versión adicional llamada Int que coincide en tamaño con aquel que posee la misma cantidad de bits que el sistema en donde se está ejecutando. Esto es, si el sistema es de 32 bits, entonces Int tiene el mismo tamaño que Int32 y, si el sistema es de 64, equivale al Int64.

  • Sin signo (unsigned): Son aquellos que pueden ser cero o positivos. De la misma manera que los anteriores, Swift provee el tipo UInt que coincide con UInt32 o UInt64, dependiendo de la cantidad de bits del sistema. Las otras versiones de 8 y 16 bits también están disponibles.

Si bien ambos tipos están disponibles, es recomendable siempre usar Int.

let numero1 = 5
print(numero1)

// Imprime: 5

let numero2 = numero1 + 6
print(numero2)

// Imprime: 11

var numero3:Int32 = 90
numero3 = numero3 + 40
print(numero3)

// Imprime: 130

Números de coma flotante

Son aquellos números que poseen parte decimal, como por ejemplo: 3.14 o -45.392607. Existen dos tipos:

  • Double: se utiliza para representar un número de coma flotante de 64 bits
  • Float: se utiliza para representar un número de coma flotante de 32 bits

El hecho de que Double sea de 64 bits implica que puede almacenar un mayor rango de valores, con una precisión de al menos 15 decimales, lo que lo hace preferible en cuanto a su uso en casos generales.

String y Character

Un Character es un carácter cualquiera mientras que un String es una cadena de texto o bien, una colección de Characters.

Para poder usar un String, basta con crear una constante o variable y asignarle la cadena de texto que queramos que almacene, encerrado entre comillas dobles:

let texto1 = "Esto es un String"

En algunas implementaciones, es posible necesitar crear un String vacío inicialmente para luego asignarle un valor más adelante. Para lograr ese comportamiento, se puede utilizar una cadena vacía o usar el inicializador que trae String:

var textoVacio = ""
var textoVacio2 = String()

textoVacio = "Hola Mundo!"
textoVacio2 = "Hola Mundo!"

Asimismo, se puede modificar una cadena de texto utilizando el operador +=, siempre que la misma esté declarada como una variable:

var textoVacio = ""
textoVacio += "Hola Mundo!"
textoVacio += " cómo estás?"
print(textoVacio)

// Imprime: Hola Mundo! cómo estás?

Como dijimos anteriormente, un String puede verse también como un conjunto de Characters. Por lo tanto, si queremos concatenar un Character a un String la operatoria es un poco distinta. Si usamos la técnica anterior la misma no va a funcionar ya que un String solo puede concatenarse con otro String. Sin embargo, podemos hacer uso del método append de String para agregar el Character en cuestión. Un punto a tener en cuenta es que para usar una variable de este tipo, es necesario especificar el tipo de dato, ya que de lo contrario Swift inferirá que se trata de un String:

var textoVacio = ""
textoVacio += "Hola Mundo! cómo estás?"
let exclamacion:Character = "!"
textoVacio.append(exclamacion)
print(textoVacio)

// Imprime: Hola Mundo! cómo estás?!

En el ejemplo, se declara una constante exclamación del tipo Character y se le asigna un valor. Como se aclaró previamente, es necesario indicarle al compilador que esta constante es del tipo Character, de lo contrario inferirá que se trata de un String de un solo carácter.

Por último, Swift incorpora una técnica muy utilizada llamada Interpolación de Cadenas (String interpolation) que permite armar una cadena de texto combinando texto, números, funciones, variables y otras expresiones. Cada ítem a incluir en la cadena debe ser encerrada entre paréntesis y precedida por una barra invertida:

let cadena = "El número \(numero3) es más grande que el número \(numero2)"
print(cadena)

// Imprime: El número 130 es más grande que el número 11

Como se puede ver en el ejemplo, estamos usando las variables numero2 y numero3 creados anteriormente para armar un texto que los incorpora en un mismo String resultante. Recordemos que estas variables son del tipo Int, pero al estar dentro de los paréntesis y la barra, se convierte su tipo o salida a String y se concatena al resto de la cadena.

Por lo tanto, para concatenar string en Swift tenemos la opción de usar el operador +=, en el cual a una variable String se le puede adicionar otro a continuación, el operador +, para construir una cadena a partir de otras que se concatenan mediante ese operador, o la interpolación de cadenas, que a su vez nos permite convertir a String valores de otro tipo de dato e insertarlo dentro de una cadena resultante.

Bool

El tipo de dato booleano permite que una constante o variable pueda valer true o false. Son utilizados para tomar decisiones en base a comparaciones lógicas.

let heladeraLlena = false
if heladeraLlena {
    print("A preparar algo para comer!")
} else {
    print("Tenemos que comprar comida")
}

// Imprime: Tenemos que comprar comida

En el ejemplo anterior, se declara una constante indicando que la heladera no se encuentra llena. Luego se pregunta si la misma está llena, pero al no estarlo se ejecuta solo la sentencia encerrada en el else.

Tuplas

Las tuplas permiten agrupar varios valores de cualquier tipo en uno solo.

let ubicacion = (-34.599722, -58.381944)
print("Buenos Aires se encuentra en la longitud \(ubicacion.0)")

// Imprime: Buenos Aires se encuentra en la longitud -34.599722

En el ejemplo anterior, declaramos una tupla del tipo (Double, Double)con las coordenadas de longitud y latitud de la provincia de Buenos Aires. Luego, usamos la longitud haciendo referencia a ella por su posición en la Tupla (el cual empieza por el índice 0) y se imprime su valor dentro de un String más grande usando la técnica de String Interpolation.

Un punto a tener en cuenta es que las tuplas pueden contener cualquier cantidad de elementos y los mismos pueden ser a su vez de distintos tipos, como por ejemplo:

  • (Int, Int)
  • (Int, String)
  • (Bool, String, Int)
  • (Double, Float, Bool, String, Int)

Otra forma de acceder a los valores que contiene la tupla es mediante el nomenclado de sus elementos. De esta manera, al ponerle un nombre a cada uno, simplemente hacemos referencia a ellos por este atributo:

let registro = (nombre: "Fernando", esMayorDeEdad: true)
if registro.esMayorDeEdad {
    print("\(registro.nombre) está autorizado a ingresar al club")
}

// Imrpime: Fernando está autorizado a ingresar al club

En este caso, para poder hacer uso de los datos de la tupla registro, simplemente hacemos referencia a los nombres de sus elementos.

Las tuplas son especialmente útiles cuando se utilizan como valores de retorno en funciones, ya que les da la posibilidad de devolver más de un valor al mismo tiempo.

Tipos de colección

Existen tres tipos de colecciones en Swift: los arrays son colecciones ordenadas de elementos, los Sets son colecciones sin ordenar y con valores únicos y por ultimo, los Diccionarios son listas sin ordenar de elementos clave-valor.

Todos los tipos de colección son claros en cuanto al tipo de dato que almacenan. Esto significa que una vez declaradas las colecciones, solo se podrán agregar elementos del mismo tipo de dato de los que fueron indicados al principio. Por lo tanto, si se dispone de un array de Strings, solo se podrá agregarle Strings y no números enteros o booleanos.

Arrays

Se trata de colecciones ordenadas de elementos, los cuales pueden aparecer más de una vez dentro del array. Como es una lista ordenada, cada elemento ocupa una posición indicado por un índice que comienza con 0. Por lo tanto, el primer elemento ocupa la posición 0, el segundo la posición 1, etcétera.

Para crear un array, se puede inferir su tipo de acuerdo a los datos iniciales que se le asigna (encerrados entre corchetes) o bien, se puede crear un array vacío usando un inicializador al cual se le debe indicar el tipo de dato:

let array1 = ["uno", "dos", "tres"]
print("El primer elemento del array es \(array1.first!)")

// Imprime: El primer elemento del array es uno

var array2 = [Int]()
array2.append(1)
array2.append(4)
array2.append(9)
print("El segundo elemento del array2 es \(array2[1])")

// Imprime: El segundo elemento del array2 es 4

En el primer ejemplo, se infiere que el arreglo será un array de Strings. En cambio en el segundo se crea inicialmente un array de enteros vacío y luego se le agregan 3 datos. Un punto a tener en cuenta es que si queremos ver el primer elemento podemos usar el subíndice 0 o el método first (al cual le agregamos un signo de exclamación por tratarse de un opcional, lo que veremos más tarde). Si queremos ver el segundo elemento, debemos usar el subíndice 1, como se ve en el segundo ejemplo.

Para eliminar un elemento del array, se utiliza el método remove(at:) y se indica el subíndice:

array2.remove(at: 1)
print("El segundo elemento del array2 es \(array2[1])")

// Imprime: El segundo elemento del array2 es 9

Al eliminar el segundo elemento del array, luego su lugar es ocupado por el numero 9 que anteriormente ocupaba el tercer lugar.

Sets

Se trata de un tipo de colección de elementos que no siguen un orden y que solo aparecen una vez dentro de la colección. Es útil cuando justamente el orden no es importante o bien, cuando se requiere asegurar que el listado contenga valores únicos.

De la misma manera que los arrays, podemos crear Sets asignándoles valores al momento de declararlos por primera vez o bien, usar inicializadores para crearlos vacíos:

let coleccion = Set<Int>()
print("Tenemos \(coleccion.count) elementos")

// Imprime: Tenemos 0 elementos

var nombres:Set<String> = ["Juan", "Luis"]

Un punto a tener en cuenta es que si se quiere usar un Set inicializándolo pasándole los datos que va a contener (segundo ejemplo) es necesario indicar el tipo de dato del mismo ya que Swift no puede inferir si se trata de un Set o de un Array. En el ejemplo, se indicó el tipo de dato Set.

Para insertar un valor en la colección se utiliza el método insert(_:) y para eliminar remove(_:):

var alumnos:Set<String> = ["Juan", "Luis"]
nombres.insert("Pedro")
nombres.insert("Javier")

for dato in nombres {
    print("\(dato)")
}

// Imprime:
// Juan
// Luis
// Pedro
// Javier

nombres.remove("Pedro")

for dato in nombres {
    print("\(dato)")
}

// Imprime:
// Juan
// Luis
// Javier

En el ejemplo anterior, usamos el Set nombres creado previamente y le agregamos dos valores adicionales. Para mostrar el contenido de la colección, usamos un for para recorrerlo, en donde se crea una constante temporal llamada dato que contiene el valor de cada elemento según la iteración de la que se trate. Es decir, el for se ejecuta 4 veces y la primera vez que ingresa, la constante dato es igual a “Lionel”. La segunda equivale a “Messi”, y así sucesivamente. Finalmente eliminamos un elemento y volvemos a recorrer la colección para validar.

Diccionarios

Un diccionario permite almacenar asociaciones de pares clave – valor, en donde todas las claves de la colección son del mismo tipo y todos los valores son del mismo tipo pero puede ser distinto que el tipo de la clave. Los elementos aquí guardados no siguen un orden determinado y no pueden repetirse dos elementos con la misma clave.

Se denomina diccionario porque al igual que un diccionario convencional, los valores aquí almacenados son buscados mediante su clave. Es por esto que las claves no pueden repetirse ya que solo se admite devolver un valor al momento de realizar una búsqueda, siempre que la clave exista.

Para poder usar un Diccionario, se debe determinar entre corchetes el par clave – valor, los cuales se separan por dos puntos (:). En el caso de querer indicar el tipo de dato de cada uno de ellos, puede hacerse usando la misma convención.

var monedas: [String:String] = ["ARS":"Peso argentino", "USD": "Dolar americano", "MXN":"Peso Mexicano"]

En este caso, se indicó el tipo de dato [String:String] de manera explícita pero, al asignarle los valores al momento de declarar la variable monedas, es perfectamente válido omitir el tipo de dato ya que el mismo se puede inferir. Es decir, también pudo haberse escrito así:

var monedas = ["ARS":"Peso argentino", "USD": "Dolar americano", "MXN":"Peso Mexicano"]

Para poder acceder al valor de una determinada clave basta con indicar la misma como si se tratase de un subíndice:

let peso = monedas["ARS"]
print("\(peso!)")

//Devuelve:
//Peso argentino

Si se quiere agregar un nuevo valor, se debe usar la misma técnica vista en el ejemplo anterior pero asignándole un valor:

monedas["CRC"] = "Colones"

var colones = monedas["CRC"]
print("\(colones!)")

//Devuelve:
//Colones

Para modificar un valor cuya clave ya existe, se utiliza la misma técnica:

monedas["CRC"] = "Colones Costa Rica"

colones = monedas["CRC"]
print("\(colones!)")

//Devuelve:
//Colones Costa Rica

Ejemplo completo

let cantidadMaximaDeJugadoresPorEquipo = 11
var cantidadDeDelanteros = 2

var nombreDeUsuario: String = "Gabriel"
let cantidadDeMesesEnUnAño: Int = 12

print(nombreDeUsuario);
//Devuelve:
//Gabriel

print(nombreDeUsuario, terminator:"")
//Devuelve:
//Gabriel

print("De los \(cantidadMaximaDeJugadoresPorEquipo), \(cantidadDeDelanteros) están jugando como delanteros")
//Devuelve:
//De los 11, 2 están jugando como delanteros

//Esto es un comentario de linea

/* Esto es un comentario
 de
 varias
 lineas*/

//Números enteros
let numero1 = 5
print(numero1)
//Devuelve:
//5

let numero2 = numero1 + 6
print(numero2)
//Devuelve:
//11

var numero3:Int32 = 90
numero3 =  numero3 + 40
print(numero3)
//Devuelve:
//130

//Números de coma flotante
var numero4:Float = 3.45

//String y Characters
let texto1 = "Esto es un String"

var textoVacio = ""
var textoVacio2 = String()
textoVacio = "Hola Mundo!"
textoVacio2 = "Hola Mundo!"

textoVacio += " cómo están?"
print(textoVacio)
//Devuelve:
//Hola Mundo! cómo están?

let exclamacion:Character = "!"
textoVacio.append(exclamacion)
print(textoVacio)
//Devuelve:
//Hola Mundo! cómo están?!

let cadena = "El número \(numero3) es más grande que el número \(numero2)"
print(cadena)
//Devuelve:
//El número 130 es más grande que el número 11

//Bool
let heladeraLlena = false
if heladeraLlena {
    print("A preparar algo para comer!")
} else {
    print("Tenemos que comprar comida :(")
}
//Devuelve:
//Tenemos que comprar comida

//Tuples
let ubicacion = (-34.599722, -58.381944)
print("Buenos Aires se encuentra en la longitud \(ubicacion.0)")
//Devuelve:
//Buenos Aires se encuentra en la longitud -34.599722

let registro = (nombre: "Gabriel", esMayorDeEdad: true)
if registro.esMayorDeEdad {
    print("\(registro.nombre) está autorizado a ingresar al club")
}
//Devuelve:
//Gabriel está autorizado a ingresar al club


//Array
let arreglo1 = ["uno", "dos" ,"tres"]
print("El primer elemento del arreglo es \(arreglo1.first!)")
//Devuelve:
//El primer elemento del arreglo es uno

var arreglo2 = [Int]()
arreglo2.append(1)
arreglo2.append(4)
arreglo2.append(9)
print("El segundo elemento del arreglo2 es \(arreglo2[1])")
//Devuelve:
//El segundo elemento del arreglo2 es 4

arreglo2.remove(at: 1)
print("El segundo elemento del arreglo2 es \(arreglo2[1])")
//Devuelve:
//El segundo elemento del arreglo2 es 9

//SET
let coleccion = Set<Int>()
print("Tenemos \(coleccion.count) elementos")
//Devuelve:
// Tenemos 0 elementos

var nombres:Set<String> = ["Lionel","Messi"]

nombres.insert("Neymar")
nombres.insert("Iniesta")

for dato in nombres {
    print("\(dato)")
}
//Devuelve:
//Lionel
//Messi
//Neymar
//Iniesta

nombres.remove("Neymar")

for dato in nombres {
    print("\(dato)")
}
//Devuelve:
//Lionel
//Messi
//Iniesta

var monedas: [String:String] = ["ARS":"Peso argentino", "USD": "Dolar americano", "MXN":"Peso Mexicano"]

let peso = monedas["ARS"]
print("\(peso!)")
//Devuelve:
//Peso argentino

monedas["CRC"] = "Colones"
var colones = monedas["CRC"]
print("\(colones!)")
//Devuelve:
//Colones

monedas["CRC"] = "Colones Costa Rica"

colones = monedas["CRC"]
print("\(colones!)")
//Devuelve:
//Colones Costa Rica