Sobrevivir en Swift: Parte 1
Tags: swift, spanish, ios
Published: 2021-01-01 16:02:28

Esta es la parte 1 de un post en dos partes.
La parte 2 [aquí](https://dev.to/fmo91/sobrevivir-en-swift-parte-2-2gab)

# Características de Swift
 
Swift es un lenguaje de programación desarrollado por Apple para ser utilizado en el desarrollo de aplicaciones para sus plataformas, en reemplazo de Objective-C, su anterior lenguaje de preferencia. De este modo, y usando Xcode, se puede desarrollar aplicaciones para iOS, iPad OS, MacOS, WatchOS y TVOS. A partir del año 2015, Swift es también Open Source, mantenido y desarrollado no solo por Apple, sino por una comunidad abierta de desarrolladores.
 
Swift es multiparadigma, lo cual significa que podemos utilizarlo para desarrollar código orientado a objetos (OOP), programación funcional, procedural, scripting, etc. Swift se adapta a nuestro estilo de preferencia, si bien la comunidad ha acordado buenas prácticas y una forma de desarrollar software siguiendo sobre todo, la orientación a objetos.
 
Swift es moderno, lo que significa que incorpora funcionalidades de los lenguajes de programación más modernos, como veremos en el curso. Además, sigue desarrollándose e incorporando nuevas funcionalidades.
 
Swift es sencillo de aprender, pero puede volverse tan complejo como necesitemos.
 
Durante este curso veremos la sintaxis de Swift y ejemplos de uso. Lo necesario como para que podamos comenzar a desarrollar aplicaciones de iOS, pero es importante notar que el lenguaje puede llegar a ser mucho más complejo que lo que veremos aquí. Este curso no pretende ser completo y exhaustivo en Swift, sino brindar las herramientas necesarias para iniciar.


# Variables
 
Las variables están presentes en la mayoría de los lenguajes de programación, y son nombres (llamados identificadores), que contienen un valor.
En Swift, las variables se declaran con la palabra clave `var`, seguido de un `=` y el valor que contiene. Por ejemplo:

```swift
var nombre = "Fernando"
var calle = "Avenida Rivadavia"
var patasDeUnaSilla = 4
```

Algo que puede resultar muy curioso en esas declaraciones es que no estamos especificando el tipo. Esto es porque Swift incorpora **Inferencia de tipos**. La inferencia de tipos nos permite escribir las variables sin ser explícitos en su tipo, porque Swift es lo suficientemente "inteligente" como para darse cuenta que un `4` es un `Int`, y que `"Fernando"` es un `String`.
 
## Podemos ser explícitos
Claro, hay momentos en los que nos puede ser útil ser explícitos en el tipo de dato. Por ejemplo, si hablamos de un precio

```swift
var precio1 = 12
```

Quizás queramos especificar que ser trata de un número decimal, y no de un `Int`. En Swift podemos especificar el tipo con `: <TipoDeDato>`. Por ejemplo:

```swift
var precio2: Double = 2.50
```

## Constantes
Supongamos que tengamos una variable con un valor que **sabemos** que no cambiará. Por ejemplo, un gato tiene cuatro patas. Si queremos definir la cantidad de patas de un gato, podremos definir una constante. Para hacerlo, cambiaremos `var` por `let`, convirtiendo esa variable en una constante.
Una constante es una variable a la que **solo se podrá asignar un valor una vez**. Si queremos modificar su valor luego de esto, el mismo compilador nos arrojará un error.

```swift
var hojasDeUnArbol = 10
hojasDeUnArbol = 22
hojasDeUnArbol = 25 // No recibiremos errores al querer modificar una variable declarada con var.
let patasDeUnGato = 4
// Descomenten la siguiente línea para verificar que realmente esto dará error.
// patasDeUnGato = 5
```

Al nombrar una variable, es importante ser muy explícitos en su significado. Esto quiere decir, **no abreviemos**.
 
`nom` no es un nombre de variable aconsejable. `nombre` sí lo es.
`cantDigitos` no es un nombre de variable aconsejable. `cantidadDeDigitos` sí lo es.
`i` no es un nombre de variable aconsejable. `index` sí lo es.
 
Si bien en el curso usaremos nombres en español, en la práctica todos serán en inglés.

Cada lenguaje de programación tiene su forma "correcta" de escribirse, lo que podemos considerar **Buenas prácticas**. En Swift, la buena práctica es definir las variables como constantes, usando `let`, y dejar `var` solo para los casos en los que sea estrictamente necesario. Esto nos puede ahorrar dolores de cabeza, porque sabemos que la variable que estamos utilizando, si fue definida con `let`, no ha sufrido modfiicaciones. Para funciones complejas, esto puede ser muy importante.

# Tipos de datos
Como vimos en la sección anterior, si bien no es necesario que seamos explícitos en el tipo de dato de una variable, porque el compilador **infiere** el tipo de dato, podemos elegir ser explícitos y definirlo de todos modos. En esta sección elegiremos ese camino.
Swift viene con un conjunto de tipos de dato predefinidos, y nos permite definir los nuestros, como veremos en las siguientes secciones.

## Tipos de datos numéricos
Swift incorpora los siguientes tipos de datos numéricos principales:
* `Int`: Número entero.
* `Double`: Número decimal.
* `Float`: Número decimal, pero de menor capacidad que `Double`.

```swift
let patas: Int = 4
let precio: Double = 30.50
let temperatura: Float = 20.2
```

## Bool
`Bool` es un valor booleano. Es `true` o `false`. Solo admite esas dos posibilidades.

```swift
let encendido: Bool = true
let alumnoPresente: Bool = false
```

## String
`String` es una cadena de caracteres. Se utiliza para definir textos y palabras. Por ejemplo, un nombre o una calle podrían definirse como `String`.

```swift
let nombre: String = "Fernando"
```

Un dato de color sobre los `String` en Swift es que permiten emojis!
Por qué? Por qué no? 🤷

```swift
let fancyHelloWorld: String = "👋 🌎!"
```

# Print
A veces necesitamos imprimir por consola algún valor. En Swift, esto lo hacemos con la función `print()`. Por ejemplo, un "Hola, Mundo!", el programa típico para aprender a programar, se puede escribir de esta manera:

```swift
print("Hola, Mundo!")
```

Si se ejecuta el programa, con el botón de la barra izquierda, se mostrará ese mensaje en la consola de la parte inferior de la pantalla.

# Comentarios
Los comentarios pueden ser de una línea, o multilínea.
## Comentarios de una línea
Se escriben con `//`, y solo abarcan una línea.

```swift
let mensaje = "Hello, world!" // Este es un mensaje
```

## Comentarios multilínea
Comienzan con `/*`, y terminan con `*/`.

```swift
/* Esta línea
 imprime el
 mensaje de hola mundo */
print(mensaje)
```

# Interpolation
¿Cómo formamos `String` a partir de otras variables? Una forma sencilla es sumando.

```swift
let nombre = "Fernando"
let apellido = "Ortiz"
let nombreCompleto = nombre + apellido // Fernando Ortiz
```

La alternativa, y lo más utilizado en Swift, es la interpolación. Una interpolación de `String` consiste en "incrustar" variables dentro de un `String`. Esto se hace introduciendo `\(<variable>)` dentro del `String`. Por ejemplo:

```swift
let segundoNombre = "Martín"
let experiencia = 6
let descripcion = "Mi nombre es \(nombre) \(segundoNombre) \(apellido) y tengo \(experiencia) años de experiencia en desarrollo de iOS."
print(descripcion) // "Mi nombre es Fernando Martín Ortiz y tengo 6 años de experiencia en desarrollo de iOS."
```

Noten también que estamos introduciendo un `Int` en el medio del `String` y no nos ha dado problemas. Esta es otra ventaja de la interpolación.

# Contenedores
Llamaremos Contenedores en Swift a los tipos de datos que contienen otros tipos de datos en su interior y los podemos recorrer de alguna manera.

## Array
También llamados arreglos, listas o vectores, son tipos de datos que almacenan valores en forma secuencial. La particularidad que tienen en Swift es que no se pueden mezclar valores con diferentes tipos dentro de un mismo `Array`.
La forma más simple de definir un Array es esta:

```swift
let alumnos = ["Federico", "Micaela", "Aldana", "Oscar"]
```

Nótese que solo hemos utilizado valores de `String`. Un array con valores ["Fernando", 28, true] no sería válido, a menos que el array sea de tipo `[Any]`. El tipo `Any` es un comodín que cuenta por todos los tipos, pero nos da ciertas desventajas de las cuales no hablaremos en este momento. Mi consejo aquí es **NO LO HAGAN**.

Si queremos definir el tipo y ser explícitos, podemos hacerlo:

```swift
let numeros1: [Int] = [1, 21, 129]
let numeros2: Array<Int> = [3, 122]
```

Hay varias maneras de definir un `Array` vacío.

```swift
let numerosVacio1: [Int] = [] // Aconsejable
let numerosVacio2: Array<Int> = []
let numerosVacio3 = [Int]()
let numerosVacio4 = Array<Int>()
```

### Métodos útiles en Array
Si queremos contar la cantidad de elementos en un `Array`, usaremos la propiedad `count`.

```swift
let numeros3 = [12, 31, 231, 23]
print("\(numeros3) tiene \(numeros3.count) elementos")
```

Dato curioso, los `String` en Swift son en realidad secuencias de caracteres, y también tienen la propiedad `count`. De hecho, la mayoría de lo que explicaremos aquí, aplica para `String`.

```swift
let nombre = "Fernando"
let cantidadDeCaracteres = nombre.count
print("\(nombre) tiene \(cantidadDeCaracteres) letras.")
```

Si queremos obtener el primer elemento de un `Array`, usaremos `.first!`, la razón del `!` lo explicaremos más adelante. Solo digamos por el momento, que usaremos `!` para afirmar que sabemos con **total seguridad** que hay un primer elemento en el `Array`, que no está vacío. Noten que si hacemos `.first!` en un `Array` vacío, el programa se detendrá y lanzará un error.

```swift
let nombres = ["Tamara", "Nicolás", "Francisco"]
let primerNombre = nombres.first! // "Tamara"
```

Si queremos saber si un `Array` está vacío, usaremos la propiedad `isEmpty`, que devuelve un `Bool`.

```swift
if numerosVacio1.isEmpty {
    print("Está vacío!")
}
```

Si queremos agregar un elemento a un `Array`, usaremos la función `append`

```swift
var nombresALlenar: [String] = []
nombresALlenar.append("Fernando")
nombresALlenar.append("Martín")
print("Nombres a llenar ahora tiene \(nombresALlenar.count) elementos") // Nombres a llenar ahora tiene 2 elementos
```

Si queremos acceder a una posición exacta de un `Array`, usamos lo que se conoce como `subscript`, y es básicamente un número (el índice) entre corchetes. Los índices en un `Array` de Swift comienzan desde 0. Noten que si queremos obtener un elemento en una posición que no está definida, el programa se detendrá y lanzará un error.

```swift
let nombres2 = ["Aldana", "Iván", "Marcos", "Cristian"]
let tercerNombre = nombres2[2] // "Marcos"
// Esto fallará al ejecutarse.
// let quintoNombre = nombres2[4]
```

# Diccionarios
Un `Dictionary` en Swift es un contenedor que asocia nombres a valores. También se llaman **key-value**, o **clave-valor**. El diccionario estará compuesto por pares clave-valor, donde la clave es por lo general un `String`, y el valor puede ser cualquier cosa. Aquí no rige tan fuertemente la restricción de que los valores sean todos del mismo tipo, y el tipo `Any` es más utilizado.
Los `Dictionary` son análogos al tipo `Map` en otros lenguajes de programación.

```swift
let persona: [String: Any] = [ // Noten que al definirlo con `Any`, el valor puede ser de cualquier tipo.
    "nombre": "Ricardo",
    "apellido": "Gomez",
    "edad": 27,
    "cargo": "Project Manager"
]

let familia: [String: String] = [ // Al definirlo como [String:String], el valor solo puede ser String
    "madre": "Marge",
    "padre": "Homero",
    "hijo": "Bart",
    "hija": "Lisa",
    "bebé": "Maggie",
    "perro": "Huesos" // no es Ayudante de Santa, lo lamento. 😁
]

let diccionarioVacio: [String: Any] = [:] // El `:` debe estar, para que no se confunda con un Array vacío
```

La ventaja de poder definir clave-valor es que podemos acceder a cualquier valor utilizando su clave. Siguiendo el ejemplo de la familia:

```swift
let madre = familia["madre"]! // "Marge"
```

El `!` es necesario porque debemos asegurarle al compilador que realmente existe un valor para esa clave

# For
El `for` es una estructura muy conocida en los lenguajes de programación en general. En Swift es algo diferente. Un `for` nos permite ejecutar una porción de código un número predefinido de veces. En la práctica, el caso más común es recorrer un contenedor, como un `Array` o un `Dictionary`.

```swift
// Usaremos estos valores para las demostraciones
let nombres = ["Ayelén", "Lautaro", "Natalia", "Sergio", "Gerardo"]
let persona: [String: Any] = [
    "nombre": "Ricardo",
    "apellido": "Gomez",
    "edad": 27,
    "cargo": "Project Manager"
]
```

## for-in - Array
Para recorrer un `Array`, podemos recorrerlo elemento por elemento usando `for-in`.
En este caso imprimirá:

```swift
 Nombre: Ayelén
 Nombre: Lautaro
 Nombre: Natalia
 Nombre: Sergio
 Nombre: Gerardo
```

```swift
for nombre in nombres {
    print("Nombre: \(nombre)")
}
```

## for-in - Dictionary
También podremos recorrer diccionarios con `for-in`. En este caso la sintaxis es algo diferente, porque por cada elemento del `Dictionary` obtendremos un par (`tuple` realmente, como veremos en una sección posterior), con la clave y el valor correspondiente. Veamos un ejemplo. Imprimirá:
 
```swift
 apellido : Gomez
 cargo : Project Manager
 nombre : Ricardo
 edad : 27
```
 
Noten también que un `Dictionary` no preserva el orden de los campos. En este ejemplo imprimió `nombre` en tercer lugar, sin importar que lo haya definido en primer lugar.

```swift
for (atributo, valor) in persona {
    print("\(atributo) : \(valor)")
}
```

## Ranges
Una forma alternativa de utilizar los `for` es definiendo un `Range` o rango. Un `Range` define un intervalo de valores.
- `(0 ...< 10)` por ejemplo, define un rango desde 0 a 10, sin incluir el 10.
- `(0 ... 10)` define un rango desde 0 a 10 inclusive.

Imprimirá
 
```
 Número actual: 0
 Número actual: 1
 Número actual: 2
 Número actual: 3
 Número actual: 4
 Número actual: 5
 Número actual: 6
 Número actual: 7
 Número actual: 8
 Número actual: 9
 Número actual: 10
```

```swift
for numero in 0 ... 10 {
    print("Número actual: \(numero)")
}
```

Podemos utilizar la propiedad `count` de `Array` para recorrerlo de una manera más tradicional. Noten que `for index in 0 ..< nombres.count` es muy similar a algo como `for var index = 0 ; index < nombres.count ; index++`, sintaxis que no existe en Swift. Y si se preguntan, no, no existe `index++` en Swift. Lo que sí existe es `index += 1`, pero de todos modos la forma tradicional de `for` no está presente en Swift.

```swift
for index in 0 ..< nombres.count {
    print("Nombre actual: \(nombres[index])")
}
```

# While
La cláusula `while` es muy utilizada en lenguajes de programación. Sin embargo, si debo ser honesto, en mis seis años desarrollando aplicaciones de iOS, no recuerdo haber tenido que utilizarla alguna vez. La mayoría de las veces, una variación de `for` nos permite el mismo resultado, y es preferible por lo general, porque al utilizar `while` siempre tenemos la posibilidad de caer en un ciclo infinito de modo accidental.
En este apartado, de todos modos mostraré cómo utilizar `while`, pero repito, practicamente no es necesario utilizarlo.
`while` es una sentencia que nos permite definir una condición y un bloque de código. La condición se evaluará. Si es `true`, entonces se ejecutará el bloque de código. Este proceso se repetirá indefinidamente hasta que la condición se evalúe como `false`.

Este ejemplo imprimirá:
 
```
 Nombre con while: Ayelén
 Nombre con while: Lautaro
 Nombre con while: Natalia
 Nombre con while: Sergio
 Nombre con while: Gerardo
```

```swift
var index = 0
while index < nombres.count {
    print("Nombre con while: \(nombres[index])")
    index += 1
}
```

# Funciones
Las funciones son bloques de código que reciben opcionalmente un conjunto de datos de entrada, llamado **input** (o parámetros, o argumentos), y devuelve, opcionalmente, un valor como resultado, llamado **output**.
En Swift, las funciones se declaran con la palabra clave `func`. La estructura para declarar una función es la siguiente:

`func <nombreDeLaFuncion> (<input1>: <TipoInput1>, <input2>: <TipoInput2>, ..., <inputN>: <TipoInputN>) -> <TipoOutput> { <cuerpo> }`
 
Las funciones pueden o no tener un Output, como se ha dicho anteriormente. Sin embargo, si lo declaran, deben devolverlo utilizando la palabra clave `return`.
 
Todo esto puede resultar confuso pero es en realidad muy intuitivo, veamos algunos ejemplos:

```swift
func sumar(x: Int, y: Int) -> Int {
    return x + y
}

func holaMundo() {
    print("Hola, Mundo!")
}

func saludar(nombre: String) {
    print("Hola, \(nombre)!")
}
```

La función `sumar` recibe dos parámetros, `x` e `y`, y devuelve la suma de ambos (tiene Output).
La función `holaMundo`, no tiene input, y tampoco Output. Solo imprime `"Hola, Mundo!"` por consola.
La función `saludar` recibe un input `nombre`, y no devuelve nada.

## Invocación
Las funciones pueden ser llamadas (o "invocadas") por su nombre y el nombre de sus argumentos en forma explícita. Esto es muy importante y es diferente a lo que sucede con otros lenguajes de programación.

```swift
let resultadoSumar = sumar(x: 10, y: 14) // 24
holaMundo()
saludar(nombre: "Fernando")
```

## Legibilidad
Ahora bien, si son detallistas, habrán notado que `saludar(nombre: "Fernando")` no se lee muy bien que digamos. Podemos corregir esto. ¿No les resultaría más sencillo leer `saludar(a: "Fernando")`? Sin embargo, esto nos pondría en un problema aún peor, porque dentro de la función tendríamos una variable `a`, con el valor `"Fernando"`.
En realidad no estamos obligados a elegir una forma u otra. Swift distingue **nombres internos** y **nombres externos** para los parámetros de las funciones. Hagamos una modificación, con una función nueva `decirHola`.

```swift
func decirHola(a nombre: String) {
    print("Hola, \(nombre)!")
}

decirHola(a: "Fernando")
```

¡Mucho mejor! Esta función `decirHola` recibe un solo argumento, con un nombre interno `nombre`, visible dentro del cuerpo de la función y un nombre externo `a`, visible desde el punto de invocación de la función (cuando la llamamos). Si no especificamos nombres diferentes para el nombre interno y el nombre externo del argumento, ambos serán el mismo, como en los primeros ejemplos.

Resolvamos otro problema. La función `sumar` se ve rara. En vez de `sumar(x: 10, y: 14)`, sería más natural decir `sumar(10, 14)` o `sumar(10, a: 14)`, o similar. Swift nos permite definir nombres externos vacíos con el nombre reservado `_` (guión bajo). El guión bajo nos permite especificar que no queremos que tenga un nombre externo. Intentemos realizar una multiplicación para que se vea como `multiplicar(2, por: 8)`

```swift
func multiplicar(_ x: Int, por y: Int) -> Int {
    return x * y
}
multiplicar(2, por: 8) // 16
```

# Precondiciones
Una precondición es una condición que debe complirse para que una función sea ejecutada. Si la precondición no se cumple, entonces la función devuelve un valor adecuado o informa del error. En Swift expresamos las precondiciones con la cláusula `guard`. Podemos leerlo como "Asegurate de que esto se cumpla. Si no, devolvé este valor."

```swift
func describirDivision(de dividendo: Int, por divisor: Int) -> String {
    guard divisor != 0 else {
        return "No es posible dividir por cero."
    }
    let resultado = dividendo / divisor
    return "El resultado de dividir \(dividendo) por \(divisor) es \(resultado)."
}

let descripcion1 = describirDivision(de: 20, por: 5)
print(descripcion1) // El resultado de dividir 20 por 5 es 4.

let descripcion2 = describirDivision(de: 10, por: 0)
print(descripcion2) // No es posible dividir por cero.
```

# Buenas prácticas
Hasta aquí la teoría sobre la sintaxis de las funciones. **El fragmento que sigue es completamente opcional e incluso pueden leerlo al finalizar este curso**, pero me parece importante destacarlo.
¿Por qué escribimos funciones? Hay varias razones, pero lo importante es entender que al función es la herramienta fundamental que tenemos a disposición para escribir abstracciones en nuestro código. Una abstracción es en cierta forma adaptar un concepto a términos que nos permitan razonar sobre él en forma más intuitiva. Considerando que escribimos código para expresar una solución a un problema de forma que no solo una computadora lo entienda (que es la parte más sencilla), sino para que otras personas lo lean, lo entiendan y puedan sentirse seguros modificándolo y extendiéndolo, entonces las abstracciones nos permiten sumar claridad a nuestro código.
Una buena función entonces es entendible, clara, legible y modificable. Voy a enumerar a continuación buenas prácticas al momento de escribir una función.

## Funciones cortas
Una función no debería ocupar más de diez o quince líneas de código por lo general. Si una función se extiende más que esa cantidad de líneas, es conveniente considerar separarla en funciones más pequeñas y componerlas. Una función de una sola línea no es algo malo si suma claridad.

## Funciones legibles
Esto aplica tanto para el cuerpo de la función como para la invocación de la misma. El código que va dentro de una función tiene que ser una secuencia de pasos bien definida. La invocación de una función debe leerse naturalmente.

## Funciones cohesivas
Una función debe hacer una sola cosa y hacerla bien. Esto es incluso más prioritario que escribir funciones cortas. Si una función tiene 25 líneas de código pero todas contribuyen al objetivo sin sumar carga cognitiva a la persona que lo está leyendo

# Tuplas
Una tupla es el primer tipo de dato personalizado que veremos en el curso. Las tuplas son conjuntos de datos bajo un nombre común, y sin ninguna funcionalidad adicional (como sí dan las clases, por ejemplo). Una tupla se puede definir con la palabra clave `typealias`, o bien al momento de generarse.

```swift
typealias NombreCompleto = (primerNombre: String, apellido: String)
let nombre: NombreCompleto = (primerNombre: "Fernando", apellido: "Ortiz")
print("El nombre es \(nombre.primerNombre) y el apellido es \(nombre.apellido)") // El nombre es Fernando y el apellido es Ortiz
```

El caso más común para utilizar una tupla es para permitir que una función devuelva más de un valor.

```swift
func dividir(_ dividendo: Int, por divisor: Int) -> (cociente: Int, resto: Int) {
    // Asumimos que no estamos dividiendo por cero
    return (
        cociente: Int(dividendo / divisor), // Le quitamos lo que va después de la coma al convertirlo a entero
        resto: dividendo % divisor // El operador módulo nos devuelve el resto de la división
    )
}

let resultado = dividir(13, por: 4)
print("Cociente: \(resultado.cociente) ;; Resto: \(resultado.resto)") // Cociente: 3 ;; Resto: 1
```

## Destructuring
Destructuring es una forma de separar una tupla en sus componentes. Se realiza igualando una tupla con un conjunto de variables entre paréntesis y separados por coma:

```swift
let (miCociente, miResto) = dividir(17, por: 3)
print("Cociente: \(miCociente) ;; Resto: \(miResto)") // Cociente: 5 ;; Resto: 2

// También podemos ignorar alguno de esos valores con un guión bajo
let (cociente, _) = dividir(50, por: 4) // Ignoramos el resto aquí
print("El resultado de dividir 50 por 4 es \(cociente)") // El resultado de dividir 50 por 4 es 12
```

## Componentes anónimos
Una tupla puede tener sus componentes anónimos. Si bien no es recomendable, es posible. En este caso podemos obtener sus componentes según el orden de los mismos, usando `tupla.0` para el primer elemento, `tupla.1` para el segundo, etc. O bien aplicando destructuring `let (primero, segundo) = tupla` lo cual es preferible.
Veamos un último ejemplo generando intervalos usando una función que devolverá el valor menor y el mayor en una tupla.

```swift
func generarIntervalo(valor: Int, amplitud: Int) -> (Int, Int) {
    let cotaInferior = valor - amplitud
    let cotaSuperior = valor + amplitud
    return (cotaInferior, cotaSuperior)
}

// Con destructuring - Preferible.
let (inferior, superior) = generarIntervalo(valor: 10, amplitud: 2)
print("[\(inferior);\(superior)]") // [8;12]

// Accediendo por orden a los componentes anónimos
let intervalo = generarIntervalo(valor: 10, amplitud: 2)
print("[\(intervalo.0);\(intervalo.1)]") // [8;12]
```

# Tipos de datos opcionales
Imaginemos que queremos describir una persona. ¿Qué atributos tiene una persona? Podemos decir `nombre`, `apellido`, `dni`, `edad`, etc. Todos ellos existen. Una persona no puede no tener `nombre`. Una persona no puede no tener `apellido`. Estos seguramente son de tipo `String`. Lo que se conoce como un tipo **no opcional**.
Sin embargo, esto no aplica para todos los atributos de una persona. ¿Qué sucede con el `segundoNombre`? Una persona puede no tener `segundoNombre`, es **OPCIONAL**.
Los tipos de datos opcionales en Swift permiten modelar justamente eso, valores que pueden estar ausentes, ser nulos. Posiblemente habrán notado que hasta el momento, en todos los ejemplos se usaron valores no opcionales, y que solo en pequeñas partes se usaron los `!` para aclarar que un valor "estábamos seguros de que existía", como en `first!` para los `Array`.
Todos los tipos de datos tienen su contraparte opcional, que se define con el sufijo `?`. Así por ejemplo
- `String?` es un `String` que puede tener valor o bien ser `nil` (nulo en Swift).
- `Int?` es un `Int` que puede ser `nil`.
- `Bool?` es un `Bool` que puede ser `nil`.
- `[String]?` es un `Array` de `String` que puede ser `nil`.
- `(cociente: Int, resto: Int)?` es una tupla de dos `Int`. La tupla puede ser `nil`. Los componentes de la tupla, en caso de existir ésta, no pueden ser nulos.
- `(primerNombre: String, segundoNombre: String?)` es una tupla de dos `String`. El primero no puede ser nulo. El segundo sí puede serlo. La tupla no puede ser nula.

```swift
typealias DatosDePersona = (nombre: String, segundoNombre: String?, apellido: String, edad: Int)
let fernando: DatosDePersona = (
    nombre: "Fernando",
    segundoNombre: "Martín",
    apellido: "Ortiz",
    edad: 29
)
let nicolas: DatosDePersona = (
    nombre: "Nicolás",
    segundoNombre: nil, // Es nulo, no existe.
    apellido: "Duarte",
    edad: 29
)
```

## Forzar un opcional
Si sabemos que un valor opcional realmente tiene valor, podemos forzarlo. Para ello se añade el sufijo `!` al valor, y pasará de ser opcional, a ser no opcional.
 A continuación algunos ejemplo. Sin embargo, en la práctica esto es una **MUY MALA PRÁCTICA**. Repito, **MUY MALA PRÁCTICA**. Si añadimos `!` para obtener un valor no opcional, que en realidad era `nil`, la aplicación se cerrará completamente por el error.

```swift
print("El segundo nombre de Fernando es \(fernando.segundoNombre)")
// El segundo nombre de Fernando es Optional("Martín")
// Debemos obtener el valor no opcional de Fernando

print("El segundo nombre de Fernando es \(fernando.segundoNombre!)")
// El segundo nombre de Fernando es Martín
```

## if-let
 Esta es una de las formas "correctas" de manejar opcionales. Dentro de un `if` se puede incluir una asignación de un opcional que solo se ejecutará si el valor no es `nil`. Por ejemplo:

```swift
if let segundoNombre = fernando.segundoNombre {
    // Aquí adentro, segundoNombre no es opcional
    print("El segundo nombre de Fernando es \(segundoNombre)") // El segundo nombre de Fernando es Martín
}

if let segundoNombre = nicolas.segundoNombre {
    // No se ejecutará.
    print("El segundo nombre de Nicolás es \(segundoNombre)")
} else {
    print("Nicolás no tiene segundo nombre")
}
```

## guard-let
Un `guard` es lo contrario de un if. Entra en el cuerpo de la estructura solo en caso de que la condición no se cumpla, y se usa al escribir funciones para expresar una precondición.
`guard` también puede utilizarse para eliminar opcionales de una manera muy similar a `if-let`.

```swift
func obtenerSegundoNombre(de persona: DatosDePersona) -> String {
    guard let segundoNombre = persona.segundoNombre else {
        return "\(persona.nombre) no tiene segundo nombre"
    }
    return "El segundo nombre de \(persona.nombre) es \(segundoNombre)"
}

print(obtenerSegundoNombre(de: fernando)) // El segundo nombre de Fernando es Martín
print(obtenerSegundoNombre(de: nicolas)) // Nicolás no tiene segundo nombre
```

## Implicitly unwrapped optionals
Perdón por el inglés repentino, pero así lo encontrarán en internet si lo buscan. Lo que venimos realizando, de "eliminar la opcionalidad" se llama en realidad **unwrap**. Un implicitly unwrapped optional es un tipo de dato opcional, al que podemos asignarle `nil`, obviamente, pero que al momento de utilizarse, se considerará no opcional pase lo que pase. Por supuesto, la aplicación se cerrará por un error en caso de que lo queramos utilizar siendo nulo. Y por supuesto es una mala práctica. Sin embargo, en IOS se utiliza mucho, sobre todo en los frameworks que están escritos en Objective-C (el predecesor de Swift en las plataformas de Apple).

```swift
let segundoNombreNoNulo: String! = "Marcos"
let segundoNombreNulo: String! = nil

func imprimirSegundoNombre(_ segundoNombre: String) {
    print(segundoNombre)
}

imprimirSegundoNombre(segundoNombreNoNulo) // Marcos
//imprimirSegundoNombre(segundoNombreNulo) // CRASH!
```

## Optional chaining
Imaginemos el caso en el que tengamos un objeto opcional.

```swift
var personaOpcional: DatosDePersona?
personaOpcional = (
    nombre: "Nicolás",
    segundoNombre: nil,
    apellido: "Duarte",
    edad: 29
)
```

Si en este caso, en el que `personaOpcional` es opcional, queremos acceder a alguno de sus miembros, sean atributos o métodos, podemos utilizar el mecanismo llamado Optional chaining.
 
Optional chaining nos permite acceder a los miembros del objeto opcional, usando `?.` en lugar de `.`.

```swift
let nombreDePersonaOpcional = personaOpcional?.nombre // es de tipo String?, aunque `nombre` sea String, ya que no tenemos la certeza de que personaOpcional exista.
```

# Ejercicios Primera Parte
Resolver los siguientes ejercicios:
 
1. Definir una tupla que describa una dirección, con campos como `ciudad`, `partido`, `provincia`, `calle`, `pais`, `codigoPostal`, etc. Siéntanse libres de colocar todos los campos que consideren relevantes. Usar un diccionario para la calle con los campos `nombreDeCalle`, `numero`, `entrecalle1` y `entrecalle2`.
2. Dentro de la dirección, definir algunos tipos de datos opcionales, entre ellos `piso` y `departamento`.
3. Definir tres direcciones en constantes.
4. Escribir una función que reciba una dirección y la imprima como un `String` bien formateado. Hacer uso de la interpolación.
5. Escribir una función que reciba un `Array` de direcciones y devuelva un `String` que contenga `"piso: \(piso) ; depto: \(departamento)"`, SOLO para las direcciones que tengan definidos tanto un piso como un departamento.