intro duccion swift

98

Upload: areien-iuarti

Post on 11-Jul-2016

20 views

Category:

Documents


5 download

TRANSCRIPT

Page 1: Intro Duccion Swift
Page 2: Intro Duccion Swift

Introducción a Swift

Pedro Piñera Buendia and David Ortega

This book is for sale at http://leanpub.com/introduccionswift

This version was published on 2016-02-24

This is a Leanpub book. Leanpub empowers authors and publishers with the Lean Publishingprocess. Lean Publishing is the act of publishing an in-progress ebook using lightweight tools andmany iterations to get reader feedback, pivot until you have the right book and build traction onceyou do.

© 2016 Pedro Piñera Buendia and David Ortega

Page 3: Intro Duccion Swift

Contents

1 - Variables y tipos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1Ejercicio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8

2 - Colecciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9

3 - Control de flujo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14

4 - Closures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20

5 - Clases y estructuras . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24Clases y estructuras . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24Propiedades y métodos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26

6 - Subscripts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31Definición de subscripts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31Opciones de Subscripts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32Dónde son útiles los subscripts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33Resumen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33Ejercicio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33

7 - Herencia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34Definiendo la clase base . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34Heredando de la clase base . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34Sobreescritura (Overriding) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35Resumen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38

8 - Inicialización y deinicialización . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39Inicialización . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39Deinicialización . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47Resumen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48Ejercicio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48

9 - Automatic Reference Counting (ARC) . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50Cómo funciona . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50Ciclos de strong reference entre clases de instancia . . . . . . . . . . . . . . . . . . . . . 51Resolviendo strong reference cycles entre instancias . . . . . . . . . . . . . . . . . . . . . 53

Page 4: Intro Duccion Swift

CONTENTS

Ciclos strong reference en closures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55Resumen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57

10 - Encadenado de opcionales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58Encadenando múltiples niveles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60Resumen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60

11 - Gestión de errores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61Lanzando errores: ErrorType . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61Llamando métodos con try . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62Resumen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64

12 - Casting de tipos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66Comprobación del tipo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66Casting a un tipo determinado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67Casteado de tipos: Any y AnyObject . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68Resumen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69

13 - Tipos encadenados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70Sintáxis del punto y la inferencia de tipos . . . . . . . . . . . . . . . . . . . . . . . . . . 70Resumen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71

14 - Extensiones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72Atributos computados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72Constructores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73Métodos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73Métodos de instancia mutables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74Subscripts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74Tipos encadenados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74Protocolos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75Resumen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76

15 - Protocolos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77Sintáxis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77Requerimientos de métodos y properties . . . . . . . . . . . . . . . . . . . . . . . . . . . 77Requerimientos de mutabilidad en los métodos de un protocolo . . . . . . . . . . . . . . 78Requerimientos en constructores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78Conformar protocolos mediante extensiones . . . . . . . . . . . . . . . . . . . . . . . . . 79Composición de protocolos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80Comprobar la conformación de un protocolo . . . . . . . . . . . . . . . . . . . . . . . . 80Resumen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82

16 - Genéricos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83Funciones genéricas: Parametros tipados . . . . . . . . . . . . . . . . . . . . . . . . . . . 83Tipos genéricos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83

Page 5: Intro Duccion Swift

CONTENTS

Extendiendo un tipo genérico . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84Constraints para tipos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84Tipos asociados (Protocolos Genéricos) . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84Sentencias Where . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85Resumen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86

17 - Control de acceso . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87Control de acceso en funciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87Control de acceso en Enums . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88Control de acceso en subclases . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88Control de acceso en constantes, variables, properties y subscripts . . . . . . . . . . . . . 89Getters y Setters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89Control de acceso en constructores y constructores por defecto . . . . . . . . . . . . . . . 90Control de acceso en protocolos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90Control de acceso en extensiones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92Resumen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92Ejercicio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92

Page 6: Intro Duccion Swift

1 - Variables y tiposSi has llegado hasta aquí es que te interesa aprender un poco más sobre Swift Swift es un lenguajede programación creado por Apple y enfocado en el desarrollo de aplicaciones para toda su familiade productos (iOS, Mac OS X, watchOS y tvOS). Fue presentado en el WWDC 2014 y está diseñadode tal manera que puede usar cualquier biblioteca programada en Objective-C, así como llamar afunciones de C. Swift usa el compilador LLVM, incluido en XCode a partir de la versión 6 y en 2015Apple liberó la especificación del lenguaje, que pasó a ser de código abierto.

Swift provee de una versión propia de los tipos básicos de C y Objective-C, incluyendo Int paraenteros, Doubley Float para valores en coma flotante, Bool para Booleanos y String para cadenasde texto.

Swift es un lenguaje fuertemente tipado con inferencia de tipos, esto quiere decir que cuandocreamos una variable esa variable será del mismo tipo durante toda su vida y que, además, nosiempre es necesario poner el tipo de las variables ya que el compilador lo inferirá por el contexto.Además de las variables, en Swift es posible usar constantes, que permiten realizar un código muchomás seguro y claro.

En Swift también se introducen los tipos opcionales, que gestionan la ausencia de valor. Se usan deforma similar a los nil en Objective-C, pero son mucho más seguros que estos.

Las constantes y variables asocian un nombre como puede ser contadoro nombreEmpleadocon unvalor de un tipo en concreto (1y Juan, por ejemplo). El valor de una constante no se puede cambiaren el ciclo de vida de ésta, pero las variables sí que pueden ser modificadas con un nuevo valor enel futuro.

Declarando Constantes y variables

Las constantes y variables han de ser declaradas antes de poder ser usadas. Para declarar unaconstante utilizamos la palabra reservada lety para declarar una variable usamos la palabrareservada var.

1 let contador = 1

2 var nombreEmpleado = "Juan"

Se pueden declarar multiples constantes o variables en una sola linea:

1 var latitude=39.47913556, longitude=-0.35497427

1

Page 7: Intro Duccion Swift

1 - Variables y tipos 2

Anotaciones de tipo

Como hemos dicho, Swift es capaz de inferir el tipo de las variables y constantes en casi todas lasocasiones. En cualquier caso, siempre tenemos la posiblidad de especificar el tipo que debe ser lavariable.

1 var nombreEmpleado: String

Los dos puntos se podrían traducir como ‘de tipo’, con lo que la linea anterior se leería más o menosasí:

“Declara la variable nombreEmpleado de tipo String

Igual que antes, podemos declarar multiples variables del mismo tipo en una sola linea:

1 var x,y,z: Double

Nombrando Constantes y Variables

Los nombres de constantes y variables pueden contener casi cualquier carácter excepto espacios enblanco, simbolos matemáticos, flechas, carácteres unicode inválidos o carácteres de dibujo de cajaso líneas. Tampoco pueden empezar por un número, aunque sí que pueden contener números encualquier otra posición.

Es posible cambiarle el valor a una variable en cualquier momento, siempre que se lo cambiemospor otro tipo compatible:

1 var bienvenida="Hola Pepito!"

2 bienvenida="Hola Juanito!"

En cambio, para las constantes, no es posible cambiar el valor una vez que se ha puesto. Si lointentaramos recibiríamos un error de compilación:

1 let bienvenida="Hola Pepito!"

2 bienvenida="Hola Juanito!"

3 //Error de compilación: bienvenida no puede ser cambiado.

Tuplas

Además de los tipos conocidos como Int, Float, Double, Bool o String en Swift se incluye otro tipoque nos permitirá trabajar con agrupaciones de estos tipos básicos de una forma muy eficaz, son lastuplas.

Por ejemplo, para definir una variable con los datos de una coordenada geográfica podríamos hacerlode la siguiente manera

Page 8: Intro Duccion Swift

1 - Variables y tipos 3

1 let coordenada=(39.47913556, -0.35497427)

2

3 //la coordenada podríamos extraerla en constantes

4 let (latitud, longitud) = coordenada

5

6 print ("La latitud es \(latitud)")

7 print ("La longitud es \(longitud)")

8

9 //Si solo necesitaramos la longitud podríamos usar el guión bajo (_) al descompo\

10 ner una tupla

11

12 let (_,soloLaLongitud) = coordenada

13 print ("La longitud es \(soloLaLongitud)")

14

15 //También se puede acceder a los valores de las tuplas mediante su índice

16 print ("La latitud es \(coordenada.0)")

17

18 //Además, se pueden poner nombres a los valores de la tupla cuando se crean y ac\

19 ceder mediante estos

20 let coordenadaNombres=(latitud:39.47913556, longitud:-0.35497427)

21 print ("La latitud es \(coordenadaNombres.latitud)")

22 print ("La longitud es \(coordenadaNombres.longitud)")

Opcionales

En Swift nos encontramos la opción de definir que una variable o constante tiene o no tiene valor. Esun concepto parecido a nil en Objective-C, pero no es exactamente lo mismo, ya que nil significala ausencia de un valor válido y los opcionales de Swift significan que no existe ningún tipo devalor. Además, los opcionales de Swift funcionan para cualquier tipo de valor, mientras que nil enObjective-C sólo funciona para objetos.

Aquí dejamos un ejemplo sencillo de cómo se llegaría a tener una variable opcional

1 let numeroPosible = "123"

2 let numero = Int(numeroPosible)

3 //En este caso numero se inferiría como tipo "Int?" o "Int opcional"

Esto pasaría porque el inicializador de Int podría fallar si, por ejemplo, numeroPosible hubieratenido un valor “Pepito”.

Para poner una variable opcional como sin valor utilizamos la palabra reservada nil. >Hay quetener en cuenta que nil solo se puede utilizar en variables opcionales. Es decir, cuando se definauna variable que debería poder no tener valor en ciertas condiciones la deberemos inicializar comoopcional.

Para definir una variable opcional utilizamos el carácter ? después del tipo

Page 9: Intro Duccion Swift

1 - Variables y tipos 4

1 var respuesta: String?

Para poder trabajar con un opcional debemos de desenvolverlo primero para comprobar si tienevalor. Una forma sería mediante una comparación en una sentencia ‘if’.

Además, para poder acceder al valor de ese entero debemos utilizar el caracter exclamación ‘!’ paraextraerlo. swift if numero != nil { print("numero contiene un entero: \(numero!)") }

Una forma más directa de hacer todo este engranaje sería utilizar el enlazado opcional, que puedeser usado en sentencias if y while.

1 if let numeroNoOpcional=numeroOpcional {

2 print("numero contiene un entero: \(numeroNoOpcional)")

3 }

Se podría hacer una estructura mucho más compleja en la cual hubieran diversos enlazadosopcionales seguidos de una sentencia where comprobar una condicion booleana:

1 if let primero=Int("5"), segundo=Int("2") where primero>segundo {

2 print("\(primero) > \(segundo)")

3 }

Existe la opción de definir opcionales implicitamente desenvueltos usando para definir-los el simbolo ! en lugar de ?. Estos opcionales se comportarán en todo caso igual que unopcional definido con el interrogante, con la excepción de que no necesitaremos utilizarla exclamación para extraerlos y usarlos.

Operadores

En Swift los operadores básicos se utilizan de forma similar a la mayoría de lenguajes, de maneraque no nos detendremos mucho en esto y simplemente mostraremos un pequeño código de ejemplo:

1 //El primero y más claro es la asignación, con el operador =

2 var a = 5

3

4 //Se puede usar con tuplas como ya hemos visto

5 let (x,y)=(1,2)

6

7 //No devuelve valor

8 if x=y {

9 //No sería valido, ya que = no devuelve un valor

10 }

Page 10: Intro Duccion Swift

1 - Variables y tipos 5

11

12 //Los operadores aritméticos (+, - , / y *)

13 var two = 1+1

14 var one = 2-1

15 var three = 9/3

16 var four = 2*2

17

18 //La suma se puede usar en cadenas

19 var holaMundo = "hola " + "mundo"

20

21 //Operador resto %

22 var resto = 11 % 2 //igual a 1

23

24 //Operaciones de incremento y decremento

25 var contador = 0

26 contador++ //1

27 contador-- //0

28

29 var nuevoContador = contador++

30 //nuevoContador = 0 y contador = 1 porque nuevoContador se ha asignado antes del\

31 incremento

32

33 var ultimoContador = ++contador

34 //ultimoContador = 2 y contador = 2 porque ultimoContador se ha asignado después\

35 del incremento

36

37

38 //Se puede realizar una asignación compuesta con los operadores

39 var compuesto = 1

40 compuesto+=3 //resultado 4

Existen unos operadores especiales a los que sí que les vamos a dar un poco más de visibilidad, estosson los operadores de rango.

Básicamente existen dos operadores de rango en Swift, el de rango cerrado (…) y el de rango semiabierto (..<), veamos un ejemplo de uso.

Page 11: Intro Duccion Swift

1 - Variables y tipos 6

1 //El operador de rango cerrado es util cuando iteramos sobre el mismo rango

2 for i in 0...5 {

3 print ("\(i) por 5 \(i*5)")

4 }

5

6 //El operador de rango semi abierto es util cuando iteramos sobre elementos como\

7 arrays

8 let sueldos = [100, 200, 300, 400]

9 let count = sueldos.count

10

11 for j in 0..<count {

12 print ("Sueldo \(j+1) es \(sueldos[j])")

13 }

Cadenas y carácteres

En Swift las cadenas se representan con el tipo String. Los contenidos de este tipo pueden seraccedidos de varias maneras, incluido como colección de valores de tipo Character.

1 //Para inicializar una cadena lo hacemos de la forma que ya hemos visto anterior\

2 mente

3 let unaCadena = "Esto es una cadena"

4

5 //Podemos inicializar una cadena vacia de diferentes maneras

6 var cadenaVacia = ""

7 var otraCadenaVacia = String()

8

9 //Las cadenas tienen la propiedad isEmpty para comprobar si es una cadena vacía

10

11 if (cadenaVacia.isEmpty) {

12 print ("No hay nada")

13 }

14

15 //Una cadena será mutable o inmutable (se podrá modificar) simplemente mediante \

16 la definición como variable o constante

17 var cadenaMutable = "cadena mutable"

18 let cadenaInmutable = "cadena inmutable"

Las cadenas en Swift siempre se pasan por valor. Es decir, cuando enviamos una cadena a unmétodo,el valor de ésta se copia al nuevo método para que pueda trabajar con ella sin modificar la variableexterna.

Page 12: Intro Duccion Swift

1 - Variables y tipos 7

En Swift podemos trabajar con los carácteres de una cadena mediante su propiedad characters enun bucle for-in.

Para concatenar cadenas podemos usar el operador + o +=, pero si queremos concatenar unCharacter a un String debemos usar el método append().

1 let cadena1 = "hola "

2 let cadena2 = "mundo"

3 var bienvenida = cadena1+cadena2

4

5 let exclamacion: Character = "!"

6 bienvenida.append(exclamacion)

Otras operaciones que podemos hacer con cadenas serían:

Insertar un caracter en un indice determinado con insert(_:atIndex:) Insertar el contenido de otrostring en un índice con insertContentsOf(_:at:) Borrar un carácter de un indice determinado conremoveAtIndex(_:) Borrar un substring de un rango determinado mediante removeRange(_:)

Trabajando con Variables y Constantes

Veamos un pequeño ejemplo de cómo funcionaría el trabajo con las constantes y variables segúnhemos visto hasta ahora:

1 //Definimos la variable de tipo String

2 var nombreEmpleado="Pepito"

3 //Definimos otra variable de tipo String, pero sin asignarle valor

4 var nombreJefe:String

5

6 //Como son variables podemos reasignar el contenido de ambas, vamos a darle un a\

7 umento a Pepito y a contratar a un nuevo empleado

8 nombreJefe=nombreEmpleado

9 nombreEmpleado="Juanito"

10

11 //Ahora veamos los sueldos que tienen para poder trabajar con numeros

12 //El sueldo del jefe es fijo, así que lo definiremos con una constante

13 let sueldoJefe = 10

14 //El sueldo del empleado puede cambiar, así que se define con una variable

15 var sueldoEmpleado = 6

16

17 //Si el jefe intenta subirse el sueldo le dará un error

18 sueldoJefe = 11 //Error de compilación

19

Page 13: Intro Duccion Swift

1 - Variables y tipos 8

20 //En cambio si que podemos subir el sueldo del empleado

21 sueldoEmpleado = 7

22

23 //Si queremos saber el coste total de los sueldos podemos utilizar el operador +

24 var sumaSueldos = sueldoJefe + sueldoEmpleado

25

26 //Y si queremos saber el sueldo medio podemos dividirlo por 2

27 var mediaSueldos = sumaSueldos/2

28

29 //Cuidado! mediaSueldos se ha inferido como entero y debería ser un decimal, así\

30 que le forzaremos a que lo sea

31 var mediaSueldosFloat: Float = Float(sumaSueldos)/2.0

32

33 //Si queremos subir un poco más el sueldo del empleado podemos hacerlo mediante \

34 un operador unario (++)

35 ++sueldoEmpleado

Ejercicio

Crea un nuevo playground en el cual inicialices dos variables con tuplas. Estas variables tendránun nombre y una edad. Realiza operaciones con las edades para obtener información sobre la edadmedia y la suma de las edades.Además, imprime un mensaje dando la bienvenida a ambos.

Page 14: Intro Duccion Swift

2 - ColeccionesEn Swift podemos encontrar tres tipos de colecciones básicas: arrays, sets y diccionarios.

• Los arrays son colecciones ordenadas de datos• Los sets son colecciones desordenadas de valores únicos• Los diccionarios son colecciones desordenadas de asociaciones clave-valor, donde las clavesson únicas pero los valores pueden repetirse.

Las colecciones únicamente pueden contener objetos del mismo tipo de manera que sólo podamosinsertar y obtener objetos de ese tipo.

Los arrays, sets y diccionarios de swift están implementados como colecciones genéri-cas. Veremos más sobre esto en el capitulo de genéricos.

Al igual que pasaba con las cadenas, la mutabilidad o inmutabilidad de las colecciones dependeráde si son creadas como constante o variable.

Por regla general crearemos com inmutable cualquier colección donde los datos novayan a cambiar, de esta manera el compilador podrá optimizar su trabajo.

Arrays

Para crear un array vacío de un tipo determinado utilizaremos la forma corta que nos provee Swift[Element]. Existe la posibilidad de la forma larga, que sería Array<Element>, pero usaremos la cortapor seguir una guia de estilo similar a la de Apple.

1 var enteros = [Int]()

2 print ("enteros es de tipo [Int] con \(enteros.count) elementos")

3 enteros.append(3)

4

5 //Cuando el compilador ya conoce el tipo podemos reinicializar el array mediante\

6 []

7 enteros = []

8

9 //Podemos inicializar un array con un valor por defecto

10 var tresEnteros = [Int](count: 3, repeatedValue: 0)

9

Page 15: Intro Duccion Swift

2 - Colecciones 10

11

12 //Es posible enlazar dos arrays mediante el operador +

13 var tresEnterosMas = [Int](count: 3, repeatedValue:1)

14

15 var seisEnteros = tresEnteros+tresEnterosMas

16

17 //Si conocemos los valores del array, podemos inicializarlo directamente

18 var nombres = ["Rodrigo", "Lola"]

19 //No hemos puesto el tipo del array ya que este va a contener únicamente Strings\

20 y eso el compilador lo puede obtener por inferencia de tipos

21

22 //Con el operador += podemos añadir otro array

23 nombres += ["Sofia", "Anibal"]

Además de las operaciones básicas los Arrays disponen de métodos para añadir, reemplazar yeliminar elementos

Por ejemplo, para insertar un elemento en un indice determinado de un array podemos utilizarinsert(_:atIndex:) Para borrar un elemento podemos usar removeAtIndex(_:), aunque si lo quequeremos es borrar el primer o último elementos tenemos removeFirst() y removeLast()

Sets

Las dos diferencias clave de los Sets y los Arrays es que los Sets no tienen orden y además sólopermiten que cada elemento entre una vez. Es decir, si necesitamos una colección en la cual loselementos no se repitan y además no nos importa el orden en el que se guardan, usaremos Set.

Para crear un Set debemos poner la forma larga, es decir Set<Element>.

1 var letras = Set<Character>()

2

3 letras.insert("a")

4 //Además, igual que en los arrays, podemos volver a reinicializar el Set con la \

5 instrucción []

6 letras = []

Se puede crear e inicializar un set de la misma forma que un array, con los elementos entre []swift var numeros: Set<String> = ["uno", "dos", "tres"] En el ejemplo anterior podemosver que estamos definiendo una variable numeros de tipo Set<String>. Es importante notar que alinicializar la variable directamente con los datos debemos de poner el tipo, ya que el compilador nosabría distinguir si se trata de un Array o un Set. En cambio, el <String>si que sería opcional, yaque eso lo puede inferir el compilador de los datos que le pasamos. Es decir, el ejemplo anterior seríaequivalente a:

Page 16: Intro Duccion Swift

2 - Colecciones 11

1 var numeros: Set = ["uno", "dos", "tres"]

Podemos obtener el número de elementos de un Set con la propiedad count Asimismo podemostambién saber si un Set es vacío con la propiedas isEmpty

1 print("En el set habían \(numeros.count) elementos");

2

3 if numeros.isEmpty {

4 print ("Está vacío")

5 } else {

6 print ("No está vacío")

7 }

8

9 //Podemos añadir nuevos elementos con insert

10 numeros.insert("cuatro")

Para borrar un elemento de un set podemos usar el método remove(_:), el cual borrará el elementoy lo devolverá si existiera, sino devolverá nil. Para comprobar si un elemento existe en el set tenemosel método contains(_:). “‘swift if let numero = numeros.remove(“dos”) { print (“(numero) existía”)} else { print (“No existía”) }

if numeros.contains(“cinco”) { print (“Contiene”) } else { print (“No contiene”) } “‘

Existen una serie de operaciones que los sets nos permiten realizar, estas serían:

• intersect(_:) para crear un nuevo set con los valores comunes de ambos sets• exclusiveOr(_:) para crear un nuevo set con los valores diferentes de cada set• union(_:) para crear un nuevo set con todos los valores de ambos sets• substract(_:) para crear un nuevo set con los valores del primer set que no están en elsegundo set.

Además, los sets también nos permiten realizar comparaciones entre ellos para comprobar si unocontiene a otro, si son iguales, etc…

• Usaremos el operador == para comprobar si dos sets contienen todos los mismos valores.• El método isSubsetOf(_:) nos permite comprobar que todos los valores del set estáncontenidos en el set especificado.

• El método isSuperSetOf(_:) realizaría la operación contraria a la anterior, es decir, el set dereferencia sería el que contiene al otro.

• Los métodos isStrictSubsetOf(_:) e isStrictSuperSetOf(_:) determinan si un set essubset o superset de otro, pero no igual a éste.

• Finalmente, el método isDisjointWith(_:) determinará si dos sets no tienen valores encomún.

Page 17: Intro Duccion Swift

2 - Colecciones 12

1 var numerosPares : Set = ["dos", "cuatro", "seis"]

2 var numerosImpares : Set = ["uno", "tres", "cinco"]

3

4 var numerosPrimos : Set = ["uno", "dos", "tres", "cinco"]

5

6 var paresPrimos: Set = numerosPares.intersect(numerosPrimos)

7 //paresPrimos = ["dos"]

8

9 var imparesNoPrimos: Set = numerosImpares.subtract(numerosPrimos)

10 //imparesNoPrimos = []

11

12 var paresImpares: Set = numerosPares.union(numerosImpares)

13 //paresImpares = ["dos", "cuatro", "seis", "uno", "tres", "cinco"]

14

15 paresImpares.isSupersetOf(paresPrimos)

16 //true

17 paresPrimos.isSubsetOf(paresImpares)

18 //true

19 paresPrimos.isSupersetOf(numerosImpares)

20 //false

21 numerosPares.isDisjointWith(numerosImpares)

22 //true

Diccionarios

Los diccionarios nos permiten almacenar asociaciones clave-valor. Las claves han de ser del mismotipo y los valores del mismo tipo entre ellos también. Los valores no tienen un orden específicodentro del diccionario, sino que haremos referencia a ellos a través de sus claves, que funcionarán amodo de identificador.

Para inicializar un diccionario utilizaremos bien la forma larga: Dictionary<Key, Value> o bien laforma corta: [Key: Value].

1 var enteros = [Int, String]()

En este caso hemos creado un diccionario en el cual la clave será un Int el valor un String.

Al igual que en los casos anteriores, una vez que está inicializado el diccionario y el compilador tienedatos sobre su tipo ya podemos re inicializarlo mediante [:].

Page 18: Intro Duccion Swift

2 - Colecciones 13

1 enteros[16] = "dieciseis"

2 enteros = [:]

La forma más directa de crear un diccionario es a través de un diccionario de literales

1 var aeropuertos = ["VLC": "Valencia", "MAD": "Madrid"]

Como podemos comprobar, no ha hecho falta definirle el tipo que tienen la clave y el valor, ya queen este caso sí que es posible inferirlo directamente al tener todas las claves el mismo tipo, así comotodos los valores.

Igual que en los Arrays y los Sets podemos comprobar el número de elementos de un diccionario,así como si está vacío a través de la propiedad count y el método isEmpty

Podemos usar la sintaxis de subscript para añadir o acceder a los elementos de un diccionario Ademásde obtener el valor, con subscripts podemos también eliminar un valor concreto asignando nil a laclave correspondiente.

1 aeropuertos["NYC"] = "New York"

2 aeropuertos["MAD"] = nil

3

4 print ("\(aeropuertos["NYC"]!)")

Como se puede comprobar, en el print utilizamos la ! para mostrar el String del valordel diccionario. Esto es porque los Diccionarios pueden devolver valores vacíos, con loque todas las operaciones que nos devuelvan algún valor siempre lo van a encapsularen un opcional del mismo tipo del valor que definimos. Es decir, si, como en este caso,nuestro valor era un String, al obtener el dato nos devolverá un String?

Page 19: Intro Duccion Swift

3 - Control de flujoSwift nos provee con diversos mecanismos para controlar el flujo de nuestra aplicación. Disponemosde elementos básicos, incluidos en casi todos los lenguajes, como pueden ser los bucles for y while

o las instrucciones if, guard y switch para ejecutar diversas opciones de entre las que se elegirá unabasandonos en ciertas condiciones.

Además del bucle for tradicional, Swift también dispone del bucle for-in, que nos facilita el trabajode recorrer arrays, sets, diccionarios y otras secuencias.

Bucles For

For

No hay mucho que explicar de los buenos bucles for. Básicamente disponen de una condición y unincrementador, que harán que la variable que creemos vaya modificandose según el incrementadormientras se cumpla la condición.

1 for var indice=0; indice<3; ++indice {

2 print("indice es \(indice)")

3 }

Las constantes y variables creadas en la inicialización del bucle for sólo serán válidas en el contextode este. Si quisieramos obtener el valor final de indice después de acabar el bucle deberíamosinicializarlo antes.

1 var indice: Int

2 for indice=0; indice<3; ++indice {

3 print("indice es \(indice)")

4 }

5 print("indice al final es \(indice)")

For-In

Usaremos el bucle for-in para iterar sobre una secuencia de datos. Esta secuencia puede ser un rango,un String o los items de un array, un set o un diccionario, por ejemplo.

14

Page 20: Intro Duccion Swift

3 - Control de flujo 15

1 for indice in 1...5 {

2 print ("\(indice) por 5 es \(indice*5))

3 }

El funcionamiento es sencillo, el bucle cogerá el valor de cada uno de los elementos en la secuenciaseleccionada y los hará disponibles para utilizar en la variable que definamos; en este caso indice.

Para poder recorrer un array, diccionario o set el uso es similar al ejemplo anterior. Veamos cómosería en un array y un set:

1 //un array

2 var array = ["uno", "dos", "tres"]

3

4 for numero in array {

5 print ("El numero es: \(numero)")

6 }

7

8

9 //un set

10 var set: Set = ["uno", "dos", "tres"]

11

12 for numero in set {

13 print ("El numero es: \(numero)")

14 }

Como hemos podido comprobar, en ambos casos el uso sería el mismo, con la diferencia de que elarray nos devolvería los items ordenados y el set no.

En el caso de los diccionarios la cosa cambia ligeramente “‘swift //un diccionario var diccionario =[“uno”:1, “dos”:2, “tres”:3]

for (clave,valor) in diccionario { print (“El numero es: (valor)”) } “‘

Básicamente aquí hemos de usar una tupla para poder obtener la clave y el valor. En este casoconcreto, que sólo necesitabamos el valor podíamos haber puesto la tupla como (_,valor).

Bucles While

Los bucles while tienen la base en la repetición de las acciones definidas mientras se cumpla unacondición concreta.

En este caso tenemos dos tipos de bucles while, el bucle while básico, que evaluará la condición antesde ejecutar las acciones y el bucle repeat-while, que evaluará la condición después de ejecutar lasacciones.

Veamos el ejemplo más sencillo de estos bucles y donde está la diferencia:

Page 21: Intro Duccion Swift

3 - Control de flujo 16

1 var contador = 0

2

3 while (contador < 10) {

4 ++contador

5 print("Contador es: \(contador)")

6 }

7

8

9 contador = 0

10

11 repeat {

12 ++contador

13 print("Contador es: \(contador)")

14 } while (contador < 10)

Como hemos comprobado, en ambos casos el resultado es el mismo pero ¿Qué pasaría que el valordel contador empezara en 10?

Instrucciones condicionales

If-else

Una sentencia if nos permite controlar la entrada o no del flujo de la aplicación a una determinadazona de instrucciones. Para eso comprobaremos la validez de una sentencia booleana.

1 var nombre = "David"

2

3 if (nombre == "David") {

4 print ("Hola David")

5 }

Se pueden encadenar multiples sentencias ifmediante la instrucción else if y podemos cerrar conuna sentencia else.

Page 22: Intro Duccion Swift

3 - Control de flujo 17

1 var opcion="tres"

2

3 if opcion=="uno" {

4 print ("La opción es uno")

5 } else if opcion=="dos" {

6 print ("La opción es dos")

7 } else {

8 print ("La opción no es ni uno ni dos")

9 }

Básicamente se podría traducir if por “si” y else por sino. Con lo que nos encontraríamos con unflujo que diría algo así como “Si la opción es uno entonces imprime “La opción es uno”, sino pasaentonces, si la opción es dos, imprime “La opción es dos”, sino pasa entonces imprime “La opción noes ni uno ni dos”.

Switch

La sentencia switch en general es una forma abreviada de escribir una serie de sentencias if-elsesobre la misma variable. Para ver un caso sencillo, pongamos el ejemplo de las opciones anteriores:

1 var opcion="tres"

2

3 switch opcion {

4 case "uno":

5 print ("La opción es uno")

6 case "dos":

7 print ("La opción es dos")

8 default:

9 print ("La opción no es ni uno ni dos")

10 }

Este es un ejemplo muy sencillo, pero ya empezamos a ver la potencia de la sentencia switch enSwift, ya que la hemos usado directamente con cadenas en lugar de con enteros como permitenotros lenguajes como PHP o Java.

Otra opción muy potente que tienen las sentencias switch en Swift es que permiten poner diversasopciones en el mismo bloque, por ejemplo

Page 23: Intro Duccion Swift

3 - Control de flujo 18

1 let caracter: Character = "e"

2

3 switch caracter {

4 case "d", "a", "v", "i":

5 print ("Contenido en david")

6 default:

7 print ("No contenido en david")

8 }

Hay que tener en cuenta que en Swift es obligatorio que todos los bloques en una sentencia switchtengan datos, ya que no existe el concepto de ‘fallthrough’ en el cual si un bloque no acaba en breakse salta al siguiente.

No solo podemos usar la sentencia switch con Strings o enteros, sino que podemos usar por ejemplotuplas o rangos:

1 let contador = 10

2

3 switch contador {

4 case 1...5:

5 print ("De 1 a 5")

6 case 6...15:

7 print ("De 6 a 15")

8 default:

9 print ("Mayor de 15 o menor de 1")

10 }

11

12

13 let posicion = (2,2)

14

15 switch posicion {

16 case (0,0):

17 print ("La posición está en el origen")

18 case (0,_):

19 print ("La posición está en el eje X")

20 case (_,0):

21 print ("La posición está en el eje Y")

22 default:

23 print ("La posición está fuera de los ejes")

24

25 }

Page 24: Intro Duccion Swift

3 - Control de flujo 19

Instrucciones de transferencia de control

Existen algunas instrucciones en Swift que nos permitiran cambiar el flujo del control de laaplicación, estas son:

• continue: básicamente permite decirle a cualquier bucle que continue la ejecución desde elsiguiente elemento, saltandose lo que le quede del actual.

• break: permite romper la ejecución de un bucle. Es util si estamos buscando un elementodentro de un array con un bucle y queremos cortar la ejecución cuando ya lo tenemos.

• fallthrough: sirve para permitir, en una sentencia switch, que el control caiga de un bloquede ejecución al siguiente.

Page 25: Intro Duccion Swift

4 - ClosuresLos closures son bloques de funcionalidad autocontenidos que pueden ser pasados de un lado a otroy usados en el código. Son un concepto similar a los bloques de Objective-C o lambdas en otroslenguajes de programaicón.

Closure expressions

Las closure expressions son una manera de escribir closures incluidas en una forma breve y sencilla.Con las closure expressions podemos realizar algunas optimizaciones sintácticas de manera quepodemos escribir closures de una forma más abreviada sin perder claridad o intención. En lassiguientes lineas veremos un ejemplo de estas optimizaciones realizadas para ir refinando un ejemplodel método sort(_:).

La librería estandar de swift provee un metodo llamado sort(_:), que ordena un array de valores deun tipo determinado basandose en la salida de un closure de ordenación que se le provee. Pongamosun ejemplo del array a ordenar:

1 let nombres = ["David", "Pedro", "Chaume", "Pepito", "Juanito"]

El método sort(_:) recibe un closure que admite dos argumentos del mismo tipo que el contenidodel array y devuelve un Boolpara decir si el primer valor debería aparecer antes o después delsegundo valor.

En este ejemplo estamos ordenando un array de String, con lo que el closure debe de ser una funcióndel tipo (String, String)->Bool.

Una manera de proveer del closure es escribir una función del tipo correspondiente y pasarla comoargumento al método sort(_:).

1 func haciaAtras(s1: String, _ s2: String) -> Bool {

2 return s1>s2

3 }

4 var ordenadosHaciaAtras = nombres.sort(haciaAtras)

Usando closure expressions

Esta es una forma relativamente larga para escribir una expresión tan sencilla como (a>b), de maneraque vamos a empezar escribiendo el closure en linea usando la sintaxis de las expresiones de closure.

20

Page 26: Intro Duccion Swift

4 - Closures 21

1 var ordenadosHaciaAtras = nombres.sort({ (s1: String, s2: String)->Bool in retur\

2 n s1 > s2 })

Básicamente, la estructura es sencilla, comenzamos con una {, seguida de la declaración de ladefinición de parámetros y tipo de retorno. Después ponemos la palabra reservada in, que separará ladefinición de parámetros del cuerpo del closure. Finalmente ponemos el cuerpo del closure cerrandotodo con una llave de cierre }.

Como podemos ver, simplemente cambiando la función por un closure inline hemos acortado deforma significativa la cantidad de código escrito sin cambiar el resultado.

Infiriendo el tipo por el contexto

El closure que estamos utilizando se está pasando a un método. Por esto, el compilador puede saberperfectamente cuales son los parámetros y el tipo de retorno que necesitará tener el closure. Es decir,gracias a la inferencia de tipos podemos ahorrarnos todo ese código, dejando algo como lo que sigue:

1 var ordenadosHaciaAtras = nombres.sort({ s1, s2 in return s1 > s2 })

Siempre que usemos un closure como parámetro en linea para una función podemos omitir ladefinición de tipos, ya que se inferirá de los tipos que recibe la función en cuestión.

Retornos implicitos en closures de una sola expresión.

Si nuestra closure tiene una sola instrucción podemos omitir también la palabra return.

1 var ordenadosHaciaAtras = nombres.sort({ s1, s2 in s1 > s2})

Nombres de argumento abreviados.

Swift nos provee de abreviaturas para los nombres de argumento de las closures inline, de maneraque podamos omitir también la definición explícita de éstos si fuera necesario. Los nombres que dason $0, $1, $2, etc… siguiendo el orden de la definición.

En nuestro caso podemos poner:

1 var ordenadosHaciaAtras = nombres.sort( { $0 > $1 } )

Funciones de operador

No se dará siempre, pero en el caso concreto de nuestro ejemplo la función mayor-que (>) de Stringestá definida como una función que recibe dos String y devuelve un Bool, exactamente como elclosure que necesitamos para la función sort(_:). Es decir, en última instancia podríamos quedarnoscon:

Page 27: Intro Duccion Swift

4 - Closures 22

1 var ordenadosHaciaAtras = nombres.sort(>)

Closures a posteriori

Swift permite escribir después del paréntesis los closures en linea siempre que éste sea el últimoparámetro que reciba una función. Por tanto, el ejemplo anterior se podría haber escrito como:

1 var ordenadosHaciaAtras = nombres.sort() { $0 > $1 }

E incluso, si el método sólo admite un parámetro, podemos no escribir los ().

Captura de valores

Los closures permiten capturar constantes y variables del contexto en el que son definidos. Además,si asignamos un closure a una constante o una variable, este closure será capaz de modificar losdatos de las variables internas que tenga. Veamos un ejemplo:

1 func suma(sumaBase cantidad: Int) -> () -> Int {

2 var total = 0

3 func sumador() -> Int {

4 total += cantidad

5 return total

6 }

7 return sumador

8 }

9

10 let sumaDiez = suma(sumaBase: 10)

11 sumaDiez()

12 //10

13 sumaDiez()

14 //20

15 sumaDiez()

16 //30

17

18

19 let sumaTres = suma(sumaBase: 3)

20 sumaTres()

21 //3

22 sumaTres()

23 //6

24 sumaDiez()

25 //40

Page 28: Intro Duccion Swift

4 - Closures 23

Hemos creado una función suma, que devuelve una función de tipo ()->Int. Dentro de esta funciónsuma hemos definido una variable total y una función anidada (la forma más sencilla de closure)sumador. La función anidada es capaz de acceder al valor de total ya que los closures pueden accedera los valores del contexto que les envuelve.

A partir de ahí hemos creado dos constantes sumaDiez y sumaTres que a pesar de llamar a la mismafunción suma devuelven valores independientes. Esto ocurre porque cada una de las constantes tienesu propio contexto y por tanto los valores avanzan de forma independiente.

Además, hay que recordar que los closures son tipos por referencia, esto quiere decir que siasignamos el mismo closure a otra variable o constante, este será exactamente el mismo y tendráacceso al mismo contexto.

1 let sumaDiez2 = sumaDiez

2 sumaDiez2()

3 //50

Page 29: Intro Duccion Swift

5 - Clases y estructurasClases y estructuras

Las clases y estructuras son la base de las aplicaciones en Swift. Permiten definir propiedades ymétodos para añadirles funcionalidad.

Swift no requiere interface e implementación separados para clases y estructuras.

Comparando clases y estructuras

Las clases y estructuras en Swift tienen muchas cosas en común, ambas pueden:

• Definir propiedades para guardar valores• Definir métodos para proveer funcionalidad• Definir subscripts para proveer acceso a sus valores• Definir inicializadores para definir su estado inicial• Ser extendidas• Conformar protocolos

Además de esto, las clases permiten (y las estructuras no):

• Herencia• Casting de tipos• Desinicializadores para liberar los recursos• El conteo de referencias permite más de una referencia a las instancias de clase.

Definición

Las clases y las estructuras se definen de forma similar. Las clases con la palabra reservada class ylas estructuras con struct.

24

Page 30: Intro Duccion Swift

5 - Clases y estructuras 25

1 struct Localizacion {

2 var latitud = 0

3 var longitud = 0

4 }

5

6 class Direccion {

7 var localizacion = Localizacion()

8 var calle: String?

9 var ciudad: String?

10 var numero: Int?

11 }

12

13 let localizacion = Localizacion()

14 let direccion = Direccion()

En el ejemplo anterior hemos creado una estructura Localizacion y una clase Direccion. Estaúltima tiene una variable de tipo Localizacion. Además, hemos inicializado una instancia de cadauna de ellas para poder usarlas.

Inicializador por defecto

Todas las estructuras tienen un inicializador por defecto con sus variables, de manera que sepuede instanciar de la siguiente manera swift let localizacion = Localizacion(latitud: 0

longitud: 0)

Paso de estructuras y clases

Las estructuras se pasan por valor

Esto quiere decir que cuando asignamos una instancia de una estructura a otra variable o la usamospara llamar a un método, esta instancia será copiada y la instancia que estaremos modificando seráotra diferente, con lo que la instancia inicial no cambiará aunque cambiemos la interna.

Las clases se pasan por referencia

Básicamente lo contrario que con las estructuras. Si pasamos una instancia de una clase a unmétodo,esta clase no se copia, sino que se envía una refrencia a la instancia. De esta manera, si se cambia laclase dentro del método se cambiará también fuera.

Operadores de identidad

Como las clases son tipos por referencia es posible que multiples constantes y variables haganreferencia a la misma instancia de una clase. En ocasiones puede ser util comprobar si dos constantes

Page 31: Intro Duccion Swift

5 - Clases y estructuras 26

o variables hacen referencia a la misma instancia de una clase, para eso usamos los operadores deidentidad:

• Identico a (===)• No identico a (!==)

Propiedades y métodos

Las propiedades y los métodos son lo que proveen a las clases y estructuras de funcionalidad real.Las propiedades nos ofrecen potencial para guardar valores y los métodos nos dan la posibilidad derealizar cálculos y procesos.

Propiedades almacenadas

Las propiedades almacenadas son la forma más sencilla de propiedades. Son las constantes ovariables que se guardan como parte de una instancia de una clase o estructura.

Aquí podemos ver el ejemplo de una estructura que define tres propiedades, dos de ellas variables yuna constante que no podrá ser modificada:

1 struct Fuente {

2 let nombre: String

3 var tamaño: Int

4 var peso: Int

5 }

Propiedades calculadas

Las propiedades calculadas no guardan un valor, sino que lo calculan en base a otros datos y lo sirvenen el momento que se consultan. Proveen de un getter y un setter opcional para obtener y guardarotras propiedades indirectamente.

Page 32: Intro Duccion Swift

5 - Clases y estructuras 27

1 struct Punto {

2 var x = 0,

3 var y = 0

4 }

5

6 struct Rectangulo {

7 var xMin = 0.0, yMin = 0.0, xMax = 0.0, yMax = 0.0

8

9 var centro : Punto {

10 get {

11 let centroX = xMin+(xMax-xMin)/2

12 let centroY = yMin+(yMax-yMin)/2

13 return Punto(centroX, centroY)

14 }

15 }

16 }

17

18 var cuadrado = Rectangulo()

19 cuadrado.xMin = 0

20 cuadrado.yMin = 0

21 cuadrado.xMax = 10

22 cuadrado.yMax = 10

23

24 let centro = cuadrado.centro

25 //Punto(5,5)

Si una propiedad calculada sólo tiene getter es posible eliminar el bloque get, con lo que quedaríaalgo como lo que sigue:

1 struct Rectangulo {

2 var xMin = 0.0, yMin = 0.0, xMax = 0.0, yMax = 0.0

3

4 var centro : Punto {

5 let centroX = xMin+(xMax-xMin)/2

6 let centroY = yMin+(yMax-yMin)/2

7 return Punto(centroX, centroY)

8 }

9 }

Métodos

Todos los métodos tienen un nombre por el cual se les hace referencia. Además del nombre se lespuede definir uno o más valores con sus tipos, que los métodos recogerán como entrada. También

Page 33: Intro Duccion Swift

5 - Clases y estructuras 28

es posible definir el tipo del valor de retorno que la función devolverá al acabar.

Por ejemplo, un método que recibiría como parámetro dos String y devolvería otro String

1 func diHola(nombre1: String, nombre2: String) -> String {

2 return nombre1 + " dice hola a " + nombre2

3 }

Si no hubieran parámetros o tipo de retorno simplemente no se pondrían (en el caso del tipo deretorno no se pondría ni la ->).

En los parámetros se puede especificar el nombre externo de éstos si se pone antes del nombreinterno, también se puede omitir si se pone _

1 func diHola(diHola nombre1: String, aQuien nombre2: String) -> String {

2 return nombre1 + " dice hola a " + nombre2

3 }

4 diHola(quien: "Johnny", aQuien: "Pepito")

5

6 //Esta es otra opción

7 func diHola(nombre1: String, _ nombre2: String) -> String {

8 return nombre1 + " dice hola a " + nombre2

9 }

10 diHola("David", "Pepito")

Es posible definir valores por defecto para los parámetros

1 func diHola(quien nombre1: String = "Pedro", aQuien nombre2: String) -> String {

2 return nombre1 + " dice hola a " + nombre2

3 }

4

5 diHola(aQuien: "Pepito")

6 diHola(quien: "Juanito")

En los métodos es Swift también es posible especificar que un método recibirá uno o más valores deun determinado tipo, así como permitir la mutabilidad de los parámetros que enviamos mediante sudefinición como constantes o variables.

Page 34: Intro Duccion Swift

5 - Clases y estructuras 29

1 func contador (var sumatorio: Int = 0, step: Int) -> Int{

2 sumatorio += step

3

4 return sumatorio

5 }

6 //En este caso podemos cambiar sumatorio (en el contexto del método), pero no po\

7 driamos modificar step

8

9 contador(step: 1)

10 contador(step: 3)

11 contador(5, step: 3)

12

13 //Comprobamos que sólo se cambia en el contexto del método

14 var sumatorio = 10

15

16 contador(sumatorio, step: 4)

17 sumatorio //=10

Si quisieramos que un método pudiera cambiar una variable externa deberíamos pasar esa variablecomo inout, además, tendremos que llamar a la función enviando el parámetro por referenciamediante el operador ‘&

1 func contador (inout sumatorio: Int, step: Int){

2 sumatorio += step

3 }

4 var sumatorio = 10

5

6 contador(&sumatorio, step: 4)

7 sumatorio //=14

Tipos

Las funciones tienen un tipo específico que está formado por los parámetros que se le pasan y porel tipo de retorno. Por ejemplo, la función del ejemplo anterior tendría un tipo (Int, Int) -> Int

Otro ejemplo sería la función: swift func holaMundo() { print ("hola mundo") } que tendríaun tipo ()->Void, es decir, una función sin parámetros y devuelve Void.

Los tipos de las funciones se pueden usar como parámetro para otras funciones, así como para definirvariables

1 var funcionContador: (Int, Int)->Int = contador

Veamos un ejemplo de paso de funciones como parámetro usando la función contador que definimosanteriormente

Page 35: Intro Duccion Swift

5 - Clases y estructuras 30

1 func bucleContador(starting: Int=0, step: Int=1, maximo: Int, funcion: (inout In\

2 t, Int)->Void)->Int {

3 var sumatorio = starting

4 while sumatorio<maximo {

5 funcion(&sumatorio, step)

6 }

7 return sumatorio

8 }

9

10 bucleContador(maximo: 10, funcion: contador)

Page 36: Intro Duccion Swift

6 - SubscriptsEn Swift, clases, estructuras y enumeraciones pueden definir subscripts, que son accesos directos amiembros dentro de colecciones.

Usamos subscripts por ejemplo para acceder a los elementos de un Array

1 let myArray = ["one", "two", "three"]

2 myArray[2] // [2] Is a subscript

Un tipo puede tener múltiples subscripts y además estos no están limitads a una única dimensión.

Definición de subscripts

La forma de definir un subscript es similar a como se definen los atributos, solo que en este casoes necesario especificar el gettery setterde dicho subscript. En el ejemplo inferior definimos unsubscript que toma un parámetro Inty define como acceder al index del elemento.

1 subscrit(index: Int) -> Int {

2 get {

3 // Defino como obtengo el elemento en el index pasado al subscript

4 }

5 set(newValue) {

6 // Defino como se setea el elemento en un index determinado.

7 }

8 }

El tipo de newValue es el mismo tipo del elemento en el subscript determinado, en elcaso anterior, tipo Int

En el caso de tener subscripts que son sólo de lectura no es necesario definir las sentencias de gety set y simplemente definir cómo se retorna el parámetro en ese index:

1 subscript(index: Int) -> Int {

2 // Retornar el elemento en ese index

3 }

31

Page 37: Intro Duccion Swift

6 - Subscripts 32

Opciones de Subscripts

Aunque en los ejemplos anteriores hemos visto como podemos usar subscripts con un únicoparámetro, se pueden pasar también múltiples parámetros. A la hora de usarlos es el propiocompilador el que inferirá el tipo de subscript a usar. Swift define el uso de múltiples scripts comosubscript overloading.

1 struct Matrix {

2 let filas: Int, columnas: Int

3 var print: [Double]

4 init(filas: Int, columnas: Int) {

5 self.filas = filas

6 self.columnas = columnas

7 print = Array(count: filas * columnas, repeatedValue: 0.0)

8 }

9

10 // Definición de subscript

11 subscript(celda: Int, columna: Int) -> Double {

12 get {

13 return print[(celda * columnas) + columna]

14 }

15 set {

16 print[(celda * columnas) + columna] = newValue

17 }

18 }

19 }

20

21 // Inicializamos la matriz

22 var mat = Matrix(filas: 3, columnas: 3)

23

24 // Accedemos a los setters de los subscript

25 mat[0,0] = 1.0

26 mat[0,1] = 2.0

27 mat[1,0] = 3.0

28 mat[1,1] = 5.0

29

30 // Obtenemos los valores usando subscript

31 println("\(mat[0,0])")

32 println("\(mat[0,1])")

33 println("\(mat[1,0])")

34 println("\(mat[1,1])")

Page 38: Intro Duccion Swift

6 - Subscripts 33

Dónde son útiles los subscripts

Los subscripts son útiles en aquellas clases o estructuras que representen colecciones de datos(siempre que no uses Dictionary o Array que por defecto ya tienen definido el operador). Si creastu propia colección, por ejemplo, para encapsular información extra aparte de la colección, entoncespudes definir como sería el subscript de tu nuevo tipo.

Recuerda, si tu clase/estructura modela una colección, los subscripts son útiles paraacceder a los elementos de la colección de forma más cómoda.

Resumen

• Podemos crear subscripts en clases, estructuras y enumeraciones que representen coleccionesen varias dimensiones.

• Los subscripts son accedidos mediante corchetes: [2, 3]

• Los subscripts pueden tener varias dimensiones.• También puede ser de cualquier tipo, y no unicamente Int.

Ejercicio

Vamos a poner en práctica los subscripts y lo vamos a hacer con un ejemplo real con el cualencontramos los desarrolladores de apps a diario, el acceso a APIs paginadas. Cuando las APIsHTTP tienen que retornar grandes colecciones de datos, estas lo hacen devolviendo los resultados enmúltiples peticiones. El resultado desde el punto de vista del programador que consume esos datosson varias colecciones que tenemos que presentar como una única colección en la interfaz de nuestraaplicación. ¿Cómo?, modelaremos el resultado de todas las páginas en un nuevo tipo.

Tendremos un nuevo tipo, PaginatedResponse con las siguientes condiciones:

• Permitirá añadir una nueva página• En cualquier momento podremos acceder a la última página• Mediante subscript podremos acceder a una página en concreto. Si no existe devolveremos unOpcional

• También podremos acceder a la colección completa concatenando el resultado de cada página.• Implementa el tipo de acuerdo a las especificaciones anteriores.

Page 39: Intro Duccion Swift

7 - HerenciaUna clase puede heredar métodos, propiedades y otras funcionalidades de una clase. En progra-mación orientada a objetos esto se conoce como herencia de clases.

Las subclases pueden acceder a los métodos, propiedades y subscripts de la super clase siempre ycucando estas sean internal o public . El compilador comprobará si a la hora de sobreescribir loselementos de la clase padre estos se corresponden con los mismos tipos de su padre.

Definiendo la clase base

Cualquier clase que no hereda de ninguna otra clase es una clase base.

Por ejemplo, la clase del ejemplo inferior no hereda de ninguna otra clase, se trata de una clase base,Animal:

1 class Animal {

2 var nombre: String = ""

3 var patas: Int = 0

4 var feliz: Bool = true

5 func camina() {

6 print("Yo camino")

7 }

8 }

Recuerda, la forma de crear una instancia de esa clase es usando su constructor. En este caso elconstructor por defecto no toma ningún parámetro:

1 let miMascota = Animal()

Una vez tenemos la instancia de animal creada podemos acceder a cualquier propiedad de dichoanimal:

1 print("El nombre de mi mascota es: \(miMascota.nombre)")

Heredando de la clase base

La forma de heredar de la clase base es creando la nueva clase y especificando junto con el nombrede la nueva clase la clase de la que esta hereda con el siguiente formato

34

Page 40: Intro Duccion Swift

7 - Herencia 35

1 class MiSubclase: MiClasePadre

En herencia de objetos a la clase de la cual hereda se le denomina “padre”, o clase “padre”.

Así pues, si quisiéramos hacer la clase Animal más concreta y representar un animal en concreto,por ejemplo Perro podríamos hacer una subclase de Animal:

1 class Perro: Animal {

2 var raza: String = ""

3 func guau() {

4 print("\(self.nombre): Guau Guau")

5 }

6 }

Nuestra nueva clase ha heredado tanto los métodos como las propiedades de Animal, nombre, patas,y camina() y podemos usarlas desde la clase Perro.

De forma similar a como hicimos con Animal, podemos crear una nueva instancia de Perro:

1 let miPerro = Perro()

2 miPerro.raza = "Yorkshire"

Sobreescritura (Overriding)

Cualquier subclase puede sobreescribir una propiedad, método o subscript de la clase padre de laque hereda. Este concepto se conoce como overriding.

Para indicar que una variable, método o subscript va a ser sobreescrito en lugar de utilizar ladefinición de la clase padre se utiliza el keyword override. En el caso de no usarlo, el compiladorde Swift alertará de que la clase padre ya tiene una definición y se está intentando sobreescribir sinhacerlo de forma explícita.

Swift es un lenguaje de programación que fuerza al desarrollador a ser bastante explicitoa la hora de desarrollar. En este caso en lugar de permitir sobreescribir comportamientossin ser consciente de ello, prefiere avisar y forzarte a que lo especifiques. De lo contrario,el código no compilará.

Page 41: Intro Duccion Swift

7 - Herencia 36

Acceder a la definición de la clase padre

Cuando sobreescribimos el comportamiento de un subscript, método o variable nos puede interesaracceder a la definición original en la clase padre. Esto se puede hacer de la siguiente forma:

• Si hemos sobreescrito un método en la definición de nuestro propio método podemos llamara super.miMetodo() y ejecutará la implementación de dicho método en la clase padre.

• Si hemos sobreescrito un atributo en la nueva definición de esta, podemos llamar a su-

per.miPropiedad para obtener el valor de la clase padre.• En el caso de subscripts de forma similar podemos llamar a super[someIndex] para usar elsubscript de la clase padre.

Sobreescribiendo métodos

Retomando los ejemplos anteriores, podemos sobreescribir el método de caminar para Perro siendomás explícitos en la acción del mismo:

1 class Perro: Animal {

2

3 override func camina() {

4 print("Soy un perro que camina con \(self.patas) patas")

5 }

6

7 }

Si creas una instancia de dog, y llamas al método camina() ejecutará el método sobreescrito.

1 let miPerro = Perro()

2 miPerro.camina() // Imprime "Soy un perro que camina con 4 patas"

Sobreescribiendo atributos

Puedes sobreescribir una atributo de una clase para redefinir el getter/setter de la misma o inclusoañadir observers para permitir al atributo sobrescrito observar los cambios en dicho atributo.

Sobreescribir Getters y Setters

Puedes sobreescribir el getter y setter de una atributo independientemente de si la variable esalmacenada o computada. El carácter computado o almacenado de la variable no es conocido por lasubclase y lo único que hereda esta es el nombre y el tipo.

Puedes incluso una clase de tipo readonly convertirla en una readandwrite en la redefinición enla subclase. Lo que no puedes hacer, sin embargo, es hacer readonly un atributo que está definidocomo read-write en la clase padre.

Page 42: Intro Duccion Swift

7 - Herencia 37

1 class Yorkshire: Perro {

2

3 override var nombre: String {

4 return "Yorkshire"

5 }

6

7 }

En el ejemplo anterior estamos sobreescribiendo la atributo nombre definida en la clase Animal. Enconcreto estamos definiendo el getter de esta variable. Si quisiéramos acceder a nombre de Animal

simplemente tendríamos que hacer:

1 super.nombre

Sobreescribiendo los observers de los atributos

También podemos sobreescribir los observers de variables willSet, didSet para escuchar cualquiercambio que se pueda producir en las mismas.

No podemos sobreescribir los observers para tipo de variables constante, let, ya queestas variables no pueden cambiar y por lo tanto no tiene sentido escuchar cambios enlas mismas.

Siguiendo con el ejemplo anterior, si quisieramos sobreescibir el observer de la variable happy,podríamos hacerlo de la siguiente forma:

1 class Perro {

2 override var feliz: Bool {

3 didSet {

4 if !feliz {

5 self.jugar()

6 }

7 }

8

9 func jugar() {

10 print("PJugando con \(self.nombre)")

11 }

12 }

Prevenir overrides

Podemos prevenir cualquier tipo de override usando el keyword final aplicado sobre classes,métodos, variables, y subscripts:

Page 43: Intro Duccion Swift

7 - Herencia 38

1 final class MiClase {}

2 final func miMetodoFinal() {}

3 final var miVariableFinal: Bool = false

4 final subscript(index: Int) {}

Si intentara sobreescribir cualquiera de esos elementos marcados con final el compilador lanzaríaun error y el código no se compilaría.

Resumen

• Una clase base es aquella que no hereda de ninguna otra.• Cuando heredamos de una clase existente, heredamos de esta sus atributos, métodos, con-structores y subscripts.

• A la hora de heredar, podemos sobreescribir elementos de la clase padre tales como métodos,subscripts, atributos, y observers de los atributos.

• La sobreescritura puede ser evitada si lo especificamos con final.

Page 44: Intro Duccion Swift

8 - Inicialización y deinicializaciónInicialización

El proceso de inicialización consiste en preparar una instancia de una clase, estructura, o enumespecificando el valor de aquellas variables que lo necesiten. También se puede aprovechar estemétodo para especificar el estado de la instancia.

La forma de especifica la forma de inicializar la clase es mediante la función init() que toma tantosargumentos como sean necesarios para mover la instancia a un estado inicial. Por ejemplo, en el casodel struct inferior, mediante el init podemos pasar el nombre del perro cuando se crea una instanciade perro:

1 struct Perro {

2

3 let nombre: String

4

5 init(nombre: String) {

6 self.nombre = nombre

7 }

8

9 }

10

11 let bobby: Perro = Perro(nombre: "Bobby")

En el constructor de la clase (forma de definir a los inititializers) pasamos el parámetro nombre, yen la definición del init, damos valor al atributo nombre de la clase al valor de nombre (parámetropasado).

A la hora de pasar parámetros en el constructor, al igual que en los métodos, podemos definir valorespor defectos. El usuario a la hora de crear una instancia de la clase puede no pasar esos parámetrosy el constructor tomará el valor por defecto:

39

Page 45: Intro Duccion Swift

8 - Inicialización y deinicialización 40

1 struct Hijo {

2

3 let edad: Int

4

5 init(edad: Int = 0) {

6 self.edad = edad

7 }

8

9 }

10 let nuevoHijo: Hijo = Hijo()

En el ejemplo anterior, al no pasar el valor de edad, por defecto nuevoHijo tendrá la edad de 0.

Parámetros externos e internos

A la hora de definir el constructor, Swift permite definir un nombre, externo y otro interno paralos parámetros del constructor. La razón es debido a que a la hora de inicializar una instancia otronombre puede hacer el código de inicialización más fácil de leer. Por ejemplo:

1 struct Ordenador {

2 let compania: String

3 init(de compania: String) {

4 self.compania = compania

5 }

6 }

7

8 let iMac: Ordenador = Ordenador(de: "Apple")

El en fragmento anterior a la hora de inicializar uns instancia de Ordenador se utiliza el nombreexterno del parámetro compania, de. De esa forma es fácil reconocer que el ordenador ha sidofabricado por Apple. Si hubieramos utilizado el mismo nombre para la variable tanto de formainterna como externa:

1 let ordenador: Ordenador = Ordenador(de: "Apple")

Es más difícil saber si la compañía que estamos pasando es la que hizo el envío del ordenador a tucasa, la compañía que se encargó de fabricar el hardware, …

Swift promueve la escritura de código bastante expresivo, de ahí que existan funcional-idades como la de utilizar nombres externos e internos para los parámetros. Haz uso deestas funcionalidades y evita ambigüedades en código.

Page 46: Intro Duccion Swift

8 - Inicialización y deinicialización 41

Puedes encontrar el caso en el que a la hora de crear una instancia no necesites pasar ningún nombrea los parámetros ya que el nombre de la clase/estructura/enum es suficientemente explícito comopara saber a qué haces referencia con el parámetro. En esos casos podemos decir a Swift que ignorelos nombres externos y que en el constructor no necesites especificarlos. Por ejemplo:

1 class Altura {

2 let total: Float

3

4 init(_ total: Float) {

5 self.total = total

6 }

7 }

8

9 let miAltura: Altura = Altura(180)

Atributos opcionales

Cuando nuestra instancia a inicializar tiene atributos que son opcionales estos atributos puedenno tener valor. Esto implica que a la hora de crear definir la estructura de nuestro constructor, no esnecesario inicializar esas variables ya que de no hacerlo, el compilador entendería que los valorespara esos atributos, inicialmente son nil:

1 struct Perfil {

2

3 var trabajo: Trabajo?

4

5 }

6 let pedro: Perfil = Perfil()

En el ejemplo anterior trabajo es opcional porque un perfil puede tener o no trabajo, y no esnecesario especificar un valor para ese atributo.

Atributos constantes

Nuetra clase/estructura puede tener atributos que son constantes. Swift te forzará a inicializarlosen los constructores definidos. De lo contrario el código no compilará. Una vez estos atributos soninicializados, por el hecho de ser constantes, no podrán ser modificados más adelante:

Page 47: Intro Duccion Swift

8 - Inicialización y deinicialización 42

1 struct Cancion {

2 let nombre: String

3 let artista: Artista

4

5 init(nombre: String, artista: Artista) {

6 self.nombre = nombre

7 self.artista = artista

8 }

9

10 init() {

11 // El compilador se quejará ya que no inicializamos nombre & artista

12 }

13 }

Constructor por defecto

En el caso de no definir ningún constructor, Swift tomará un constructor por defecto e inicializarálas variables a sus valores por defecto. En el caso de no tener estas un valor por defecto el compiladorretornará un error y te forzará a definir como estas son inicializadas.

1 class Lista {

2 var canciones: [Cancion] = []

3

4 func anyadir(cancion cancion: Cancion) {

5 self.canciones.append(cancion)

6 }

7 }

8 let salsa: Lista = Lista()

En el caso de estructuras el propio Swift define un constructor por defecto tomando como parámetrostodas las variables de la estructura:

1 struct Cancion {

2 let nombre: String

3 let anyo: Int

4 }

5 let miCancionFavorita: Cancion = Cancion(nombre: "Bailando", anyo: 2015)

A estos constructors se les denomina Memberwise.

Page 48: Intro Duccion Swift

8 - Inicialización y deinicialización 43

Delegación al inicializar (value types)

Las estructuras en Swift pueden tener multiples constructores definidos. Estos pueden delegarresponsabilidades de inicialización entre ellos en un proceso conocido como tal. Para entenderlomejor analicemos el ejemplo inferior:

1 struct Rectangulo {

2

3 let ancho: Float = 0.0

4 let alto: Float = 0.0

5 let color: UIColor

6

7 init(ancho: Float, alto: Float, color: UIColor) {

8 self.ancho = ancho

9 self.alto = alto

10 self.color = color

11 }

12

13 init(ancho: Float, alto: Float) {

14 self.init(ancho: ancho, alto: alto, color: UIColor.whiteColor())

15 }

16

17 }

La estructura anterior tendría 3 constructores.

1. El constructor por defecto cuyo único argumento es el color puesto que este no tiene valor pordefecto: Rectangulo(color: miColor)

2. El segundo constructor sería el primero que se aprecia en el ejemplo donde pasamos todas lasvariables a inicializar: Rectangulo(ancho: 20, alto: 20, color: miColor)

3. El último de ellos sería el constructor que internamente usa un color por defecto y sólo esperawidth y height: Rectangulo(ancho: 20, alto: 20)

Herencia e inicialización

Todos los atributos de una clase incluyendo todas ellas que una clase hereda de la super clase, debenser inicializados durante el proceso de inicialización. Swift ofrece dos tipos de constructors paraclases conocidos como:

• Designed initializers• Convenience initizers.

Page 49: Intro Duccion Swift

8 - Inicialización y deinicialización 44

Designed Initializers Inicializa todos los atributos introducidos por una clase y llama al init de superpara continuar con la inicialización. Conceptualmente estos inicializers propagan la inicializaciónhacia las clases padres, siendo responsables únicamente de sus atributos únicamente.

Convenience Initializers Estos initializers llaman a un designed initializer de la propia clase parainicializarse. Suelen ser utilizados para crear instancias con una configuración determinada (comouna factory demodelos haría). Podríamos decir que se trata del equivalente a delegar la inicialización(para estructuras) pero en el caso de clases.

1 init(parametro1: String, parametro2: String) {

2 // Inicializa la clase

3 }

4

5 convenience init() {

6 self.init(parametro1: "1", parametro2: "2")

7 }

Reglas de inicialización para clases

Para simplificar la forma en la que interactuan los initializers en Swift, el lenguaje aplica lassiguientes tres reglas entre los initializers:

• Regla 1: Un designed initializer debe llamar al designed initializer de la clase padre.• Regla 2: Un convenience initializer debe llamar a un designed initializer de la misma clase.• Regla 3: Un convenience initializer debe como último recurso llamar a un designed initializer.

Recuerda:

• Los designed initializers mueven la responsabilidad hacia arriba.• Los convenience initializers mueven la responsabilidad al mismo nivel

Herencia de constructores

En comparación con Objective-C, las subclases de Swift no heredan los constructores de sus clasespadres. Si necesitas que una subclase provea de uno o más constructores de la clase padre, puedesofrecer una implementación personalizada de esos constructores en la subclase.

Cuando escribes un constructor que coincide con la sintáxis del constructor de la padre se trata de undesignated initializer, o constructor designado. Debes indicarlo con el modificador override antesde la definición del constructor.

De la misma forma en la que sobreescribes atributos, métodos y subscripts, la presencia delmodificador override hace que Swift compruebe la clase padre y su constructor para er si coinciden,validandno cada uno de los parametros que has especificado en el constructor de la subclase.

Page 50: Intro Duccion Swift

8 - Inicialización y deinicialización 45

Si en lugar de sobreescribir un constructor designated lo hacemos de uno de conve-niencia no es necesario especificar que estamos sobreescribiendo un constructor conoverride.

Herencia automática del constructor

Aunque en el punto anterior se mencione que los constructores no son automáticamente heredadosde la clase padre, si se cumplen algunas condiciones, si que se pueden heredar. En práctica, estosignifica que no necesitas sobreescribiri construcores en algunos escenarios, y puedes heredarlos dela super clase.

Las reglas que aplican si los constructores serán heredados son

• Regla 1: Si la subclase no define ningún constructor designado, automáticamente hereda todoslos constructores designados de la super clase.

• Regla 2: Si la subclase ofrece una implementación de todos los constructores designadosde la super clase –ya sea por la regla 1, o mediante una implementación como parte de sudefinición– entonces automáticamente hereda todos los constructores de conveniencia de lasuper clase.

Las reglas también aplican incluso si hay más constructores de conveniencia.

Constructores Failable

En determinadas ocasiones puede interesar que el constructor falle en la inicialización. Esto puedesuceder por ejemplo, cuando los parámetros de inicialización son inválidos, o alguna otra condiciónque impide que la inicialización sea satisfactoria.

Para hacer frente a eso, Swift ofrece un tipo de constructores conocidos como “failable”. Estos puedenser usados en clases, strucs, y enums. La forma de definir un constructor de tipo failable es mediante:

1 init?()

Nota: No puedes tener un constructor de tipo failable y no failable con el mismo tipode parámetros y nombres.

Para notificar sobre una inicialización fallida, simplemente retorna nil durante la inicialización:

Page 51: Intro Duccion Swift

8 - Inicialización y deinicialización 46

1 struct Animal {

2 let especie: String

3 init?(especie: String) {

4 if especie.isEmpty { return nil }

5 self.especies = especies

6 }

7 }

También puede utilizarse con enums, por ejemplo si quisiéramos inicializar el enum con otro tipodistinto al propio enum:

1 enum UnidadTemperatura {

2 case Kelvin, Celsius, Fahrenheit

3 init?(simbolo: Character) {

4 switch simbolo {

5 case "K":

6 self = .Kelvin

7 case "C":

8 self = .Celsius

9 case "F":

10 self = .Farenheit

11 default:

12 return nil

13 }

14 }

15 }

Constructor Failable init!

Los constructores de tipo failable, init?() retornan nil si no se ha podido inicializar. Si quisiéramosque en lugar de retornar un opcional unwrappeado de forma implicita podemos hacerlo con laversión del constructor init!.

Constructores requeridos

A la hora de definir una clase podemos especificar si un constructor es required. Cuando unconstructor es marcado como required cualquier subclase de estas clase debe implementar dichoconstructor.

Page 52: Intro Duccion Swift

8 - Inicialización y deinicialización 47

1 class MiClase {

2 required init() {

3 // Implementación del constructor

4 }

5 }

Cuando un init se define como required la subclase que lo sobreescriba no necesitautilizar override.

1 class MiSubclase {

2 required init() {

3 // Implementación de la subclase

4 }

5 }

Deinicialización

Swift permite definir deinitializer que son llamados automáticamente cuando la instancia de unaclase va a ser deallocada. La forma de escribir un deinitializer es mediante:

1 class MiClase {

2 deinit {

3 // Ejecutamos lo necesario

4 }

5 }

¿Cómo funciona la deinicialización? Por defecto cuando un objeto se libera de memoria, Swiftlibera todos los recursos asociados a este si no hay otro referenciándolos. En algunas situacionespodemos necesitar ejecutar una limpieza de recursos cuando nuestra instancia es liberada dememoria, por ejemplo cancelar peticioens activas, o elimiar una entidad que se está encargaa deabrir un fichero.

Los deinitializers son llamados automáticamente, justo antes de que la instancia sea liberad dememoria. No puedes llamarlos de forma manual. Los deinitializers de la clase padre son heredadospor las clases hijas y la llamada de estos comienza por el último en la jerarquía finalizando por eldeinit de la clase padre.

Debido a que el deinitializer se llama antes de que la clase sea liberada de memoria, todas losatributos son accesibles.

Page 53: Intro Duccion Swift

8 - Inicialización y deinicialización 48

Resumen

• Inicializar una instancia de una clase consiste en especificar un estado inicial para dichainstancia (dando valor a sus atributos).

• Los constructores pueden tener tanto parámetros internos como externos.• Si la clase o la estructura contiene atributos opcionales estos no requieren valor a la hora deser inicializados ya que su valor por defecto será nil.

• Si los atributos son constantes, Swift forzará a que se les de un valor en el constructor.• De no definir un constructor, Swift automáticamente definirá uno seteando los atributos a susvalores por defecto. En el caso de no tener valor por defecto, forzará a definir un constructor.

• Las estructuras permiten delegar la inicialización• Las clases tienen dos tipos de constructores:

– Designed initializers que delegan la inicialización hacia las clases padres.– Convenience initializers que delegan la inicialización a un designed initializer.

• Los constructores de las clases padres son heredados sólo si se cumplen dos condiciones:– La subclase no define un constructor designado.– Si implementa todos los designados de la subpadre, además hereda los de conveniencia.

• Existen constructores de tipo failable (init?) que pueden fallar en la inicialización y retornannil.

• Existe la versión implícita del constructor failable, init!.• Los constructores pueden ser requeridos en las subclases con el keyword required.• Swift notifica de la liberación de memoria de las instancias de clase llamando a un bloquedeinit{} definido en la clase.

Ejercicio

Sin usar Xcode analiza donde encuentra el error en el ejemplo inferior:

1 class Coche {

2 let color: String

3 let marca: String

4

5 }

6

7 class Mercedes: Coche {

8

9 init(color: String) {

10 self.color = color

11 self.marca = "Mercedes"

12 }

Page 54: Intro Duccion Swift

8 - Inicialización y deinicialización 49

13

14 }

Page 55: Intro Duccion Swift

9 - Automatic Reference Counting(ARC)Swift usa ARC para gestionar la memoria. En la mayoría de casos, esta gestión de menoria funcionade forma automática de forma que no necesita de explicitamente indicar cómo y cuando liberar loselementos en memoria. ARC automáticamente libera de memoria aquellas instancias que ya no sonnecesarias.

En algunos casos particulares, sin embargo, ARC requiere más información para la relación entrelas partes de tu código para hacer dicha gestión.

Cómo funciona

Cada vez que creamos una instancia de una clase, ARC reserva una porción de memoria para dichainformación. Esta porción mantiene toda la información de la instancia, junto con la informacióndel tipo de instancia, y todos los valores sociaados a esta.

Cuando estas instancias ya no son necesarias, ARC las libera de memoria para que pueda ser usadacon otros fines. Esto asegura que las instancias de clase no ocupan espacio en memoria cuando yano son necesarias nunca más.

Si ARC deallocara (así es como se define la liberación de memoria) una instancia e intentáramosa acceder a sus atributos, o llamar a sus métodos de instancia, nuestra aplicación probablementeabortaría la ejecución lanzando un error de runtime.

Para saber si una instancia está todavía siendo usada, ARC lleva un control sobre cuantos atributos,constantes, y variables están referenciando a la instancia de la clase. ARC no deallocará una istanciasi esta tiene una referencia activa que todavía sigue en memoria.

Para hacer esto posibe, siempre que asignes una clase de instancia a un atributo, constante, ovariable, ese atributo, constante o variable tiene una referencia strong a la instancia. La referenciase denomina referencia “strong” porque mantiene una relación fuerte con dicha instancia, y estoimpide que sea liberada.

50

Page 56: Intro Duccion Swift

9 - Automatic Reference Counting (ARC) 51

1 class Cancion {

2 let nombre: String

3 init(nombre: String) {

4 self.nombre = nombre

5 print("\(self.nombre) se está inicializando")

6 }

7 deinit {

8 print("\(self.nombre) se va a deinicializar")

9 }

10 }

11

12 var referencia1: Cancion?

13 var referencia2: Cancion?

14 var referencia3: Cancion?

15

16 referencia1 = Cancion(nombre: "Lean on")

17 referencia2 = referencia1

18 referencia3 = referencia1

En el ejemplo anterior creamos una instancia de Cancion que es referenciada por la variablereferencia1. Esto implica que nuestra instia en memoria tiene una cuenta de referencia igual a1. Cuando apuntamos referencia2 y referencia3 a la misma instancia, la cuenta incrementa a3 de forma que para ser liberada de memoria, se tendría que desconectar estas 3 referencias de lainstancia:

1 referencia1 = nil

2 referencia2 = nil

3 referencia3 = nil

4 // Imprime "Lean on se va a deinicializar"

Ciclos de strong reference entre clases de instancia

En el ejemplo anterior es fácil liberar de memoria las 3 referencias simplemente haciendo sus valoresigual a nil. Sin embargo en algunos escenarios podemos escribir código con instancias que nuncason liberadas dememoria. Esto sucede cuando dos instanciasmantienen una referencia strongmutuade forma que una siempre referencia a la otra.

La forma de solucionar este problema en Swift es definiendo dichas relaciones comoweak o unowneden lugar de como strong references. Un ejemplo de strong reference podría ser el siguiente:

Page 57: Intro Duccion Swift

9 - Automatic Reference Counting (ARC) 52

1 class Album {

2 let nombre: String

3 var canciones: [Cancion] = []

4

5 init(nombre: String, canciones: [Cancion] = []) {

6 self.nombre = nombre

7 self.canciones = canciones

8 }

9

10 deinit {

11 print("Liberando album \(self.nombre) de memoria")

12 }

13 }

14

15 class Cancion {

16 let nombre: String

17 let album: Album

18

19 init(nombre: String, album: Album) {

20 self.nombre = nombre

21 self.album = album

22 self.album.canciones.append(self)

23 }

24 deinit {

25 print("Liberando cancion \(self.nombre) de memoria")

26 }

27 }

28

29 var album: Album?

30 var cancion: Cancion?

31

32 album = Album(nombre: "Head full of dreams")

33 cancion = Cancion(nombre: "Adventure of a lifetime", album: album!)

34 album = nil // No se libera album

35 cancion = nil // No se libera track

En el ejemplo anterior, cuando creamos la instancia de una canción pasando un album añadimos lacanción se añade a sí misma al album guardando la referencia de este. Por lo tanto el album estaráreferenciado por cada una de sus tracks, y el album referenciará a las tracks. Si quisiéramos liberarel album de memoria no podríamos ya que todas las tracks tienen una referencia a él.

Page 58: Intro Duccion Swift

9 - Automatic Reference Counting (ARC) 53

Resolviendo strong reference cycles entre instancias

La forma de resolver el problema de los ciclos de retain es usando referencias que no sean de tipostrong. Swift ofrece dos alternativas a estas, unowned y weak. Ambas permiten tener una referenciaa la otra instancia pero sin mantenerlas en memoria, es decir, si en cualquier punto la otra instanciaqueda liberada, nuestra referencia apuntará a nil.

La principal diferencia entre weak y unowned es que en el caso de weak la variable será un opcionalexplícito, MiTipo? de forma que nos fuerza a comprobar si tiene o no valor en el momento de uso.En el caso de unowned el opcional es de tipo implícito, y tiene sentido su uso cuando estamos segurosde que nuestra variable nunca será nil, y por lo tanto, podemos ahorrar las comprobaciones extrasde valor en opcionales.

1 class Casa {

2 let direccion: String

3 weak var duenyo: Persona?

4

5 init(direccion: String) {

6 self.direccion = direccion

7 }

8 deinit {

9 print("La casa \(self.direccion) está siendo deinicializada")

10 }

11 }

12 class Persona {

13 let nombre: String

14 var casa: Casa?

15 init(nombre: String) {

16 self.nombre = nombre

17 }

18 deinit {

19 print("\(nombre) está siendo deinicializado")

20 }

21 }

22

23 var pedro: Persona?

24 var casa: Casa?

25

26 pedro = Persona(nombre: "Pedro")

27 casa = Casa(direccion: "Gran Via")

28 pedro!.casa = casa

29 casa!.duenyo = pedro

Page 59: Intro Duccion Swift

9 - Automatic Reference Counting (ARC) 54

Referencias weak

Una referencia de tipo weak no mantiene la el elemento refereico en memoria, simplemente apuntaa él, no bloqueando a ARC de su liberación. Este tipo de referencias ayuda a evitar los ciclos dereferencia strong.

La forma de especificar que una referencia es de tipo weak es especificándolo al comienzo de ladefinición de la variable:

1 weak var miVariable: Coche?

Cuando se usan las referencias de tipo weak la variable tiene que forzadamente ser un opcionaldebido a que en cualquier momento dicha variable puede ser liberada y por lo tanto no terner valor.Si dicha variable siempre va a estar en memoria, podemos usar unowned en lugar de weak como seexplica en la siguiente sección.

1 class Persona {

2 let nombre: String

3 init(nombre: String) {

4 self.nombre = nombre

5 }

6 var casa: Casa?

7 deinit {

8 print("Liberando \(self.nombre) de memoria")

9 }

10 }

11

12 class Casa {

13 let direccion: String

14 init(direccion: String) {

15 self.direccion = direccion

16 }

17 weak var duenyo: Persona?

18 deinit {

19 print("Liberando la casa \(self.direccion) de memoria")

20 }

21 }

22

23 var pedro: Persona?

24 var casa: Casa?

25 pedro = Persona(nombre: "Pedro")

26 casa = Casa(direccion: "Gran Via")

27

Page 60: Intro Duccion Swift

9 - Automatic Reference Counting (ARC) 55

28 pedro!.casa = casa

29 casa!.duenyo = pedro

30

31 pedro = nil // Imprime Liberando Pedro de memoria

32 casa = nil // Imprime liberando la casa Gran Via de memoria

En el ejempo anterior Casa tiene una weak reference to Persona. De esa forma evitamos el ciclo deretain. Si por alguna razón el dueño de la casa es liberado de memoria, conceptualmente la casa sequedará sin dueño y no impedirá que la Persona que representa al dueño sea liberada d ememoria.

Referencias unowned

De forma parecida a las weak las referencias de tipo unowned no mantienen un areferencia fuertea os elementos que refierenn. A diferencia de weak, estas asumen que la variable siempre tendrávalor. Por esa razón, el tipo de las referencias unowned será siempre de tio no opcional. La forma deespecificar que una referencia es de tipo unowned es mediante el keyword unowned:

1 class MiClase {}

2 unowned var miVariable: MiClase = MiClase()

Debido a que las variable con referencia unowned son no opcionales, no necesitas realizarel unwrap para acceder a su valor. ARC no puede cambiar el valor de la referencia a nil.Esto implica que si en algún momento la variable referida deja desaparece de memoriae intentas accceder a ella, la ejecución lanzará un error de runtime. Usa referenciasde tipo unowned sólo cuando estees 100% seguro de que la variable no va a serliberada de memoria.

Ciclos strong reference en closures

De forma similar a como se crean los ciclos de referencia strong entre clases, estos también puedenser creados si la instancia de una clase retiene a un closure, y este closure a su vez está reteniendo ala propia clase. Cuando se define un closure, los elementos accedidos fuera del scope del closure soncapturados. Por ejemplo, si desde el closure accediéramos al elemento self.miPropiedad, estaríamoscapturando self en ese closure y por lo tanto creando un ciclo de referencia.

La razón por la que esto sucede es debido a que al igual que las clases, los closures también son tiposde referencia. Cuando asignas un closure a un atributo, en realidad estás asignando una refencia adicho closure. En esencia, el mismo problema que el anterior.

Este problema es resuelto de forma muy elegante en Swift comparado con la forma utilizada enObjective-C. En este caso se hace uso de “capture lists”.

Un ejemplo de un ciclo de referencia strong podría ser el siguiente:

Page 61: Intro Duccion Swift

9 - Automatic Reference Counting (ARC) 56

1 class Issue {

2

3 var nombre: String = ""

4

5 init(nombre: String) {

6 self.nombre = nombre

7 }

8

9 lazy var nombreCool: () -> String = { () -> String in

10 return "Cool \(self.nombre)"

11 }

12

13 deinit {

14 print("Liberando issue \(self.nombre)")

15 }

16 }

17

18 var issue: Issue?

19 issue = Issue(nombre: "Pepito")

20 print(issue?.nombreCool())

21 issue = nil // No se llama a deinit

Donde nombreCool es un closure e internamente está haciendo referencia a self y por lo tantocreando un ciclo de referencia strong. La definición del atributo del closure es de tipo lazy ya quese inicializa sólo cuando alguien lo llama. Es por ello que a menos que alguien llame a la función, elciclo de strong reference no será creado.

Resolviendo ciclos de strong reference en closures; Capture List

La forma de resolver ciclos de strong reference en closures es haciendo uso de “capture lists”. Estasson un conjunto de weak o unowned keywords seguidos de la instancia de clase (como por ejemploself) o una variable inicializada con algún valor. El formato de un capture list es el siguiente:

1 lazy var miClosure: (Int, String) -> String = { [unowned self, weak delegate = s\

2 elf.delegate!] (indice: Int, cadena: String) -> String in

3 // Cuerpo del closure

4 }

Nota como a la hora de definir el capture list podemos asignar un nuevo nombre a lavariable dentro del closure.

Page 62: Intro Duccion Swift

9 - Automatic Reference Counting (ARC) 57

• Unowned: Captura las variables fuera del contexto del closure usando unowned cuando tantocomo el closure como las variables referenciados sean liberados de memoria al mismo tiempo.Recuerda que usábamos unowned cuando estábamos seguro de que las variables unowned

siempre iban a tener valor. De lo contrario si intentamos acceder a ellas en tiempo de ejecución,nuestra aplicación lanzará un error.

• Weak: Si las variables referenciadas dentro del closure pueden pasar a tener valor nil encualquier momento, independientemente del ciclo de vida del propio closure, estas deben serreferenciadas con weak, lo que quiere decir que dentro del closure, dichas variables pasarán aser opcionales.

Resumen

• Podemos especificar el nivel de referencia de elementos de referencia con weak y unowned.• Si no lo especificamos por defecto las referencias serán de tipo strong.• Cuando tipos de referencia tienen referencias entre ellos se crean retain cycles que hay quecontrolar.

• Lomismo sucede entre clases y closures ya que estos últimos también son un tipo de referencia.

Page 63: Intro Duccion Swift

10 - Encadenado de opcionalesEl proceso de llamar a properties, subscripts y métodos en un opcional que pueda ser nil se definecomo encadenado de opcionales. Un encadenado de opcionales puede retornar dos valores:

• Si el opcional contiene un valor entonces las properties, métodos y subscripts encadenadasretornan valor.

• Si el opcional no contiene valor entonces las properties, métodos y subscripts retornan nil.

Ya que podemos encadenar varios métodos, properties y subscripts, el efecto del encadenado esválido para toda la cadena.

Alternativa del encadenado de opcionales al unwrap forzado

La forma de encadenar opcionales es usando el signo ? para llamar a una property. La otra opciónsería usar un unwrap forzado usando !, sin embargo del uso de este operador tiene el riesgo de quesi alguno de los valores es nil, la ejecución lanzará un error y nuestra aplicación se detendrá:

1 class Elecciones {

2 var candidato: Candidato?

3 }

4

5 class Candidato {

6 var nombre ="MP"

7 }

8 let elecciones = Elecciones()

9 let nombreCandidato = elecciones.candidato!.nombre

Si intentamos ejecutar el fragmento de código anterior en Playgrownd lanzará un error diciendoque ha sido imposible hacer el unwrap forzado ya que candidato es nil para nuestra instanciaelecciones creada.

Uso de el encadenado de opcionales

Con properties

La otra opción sería hacer uso de ’?’ comprobando en el momento de acceder al atributo en concretode la cadena si este tiene valor:

58

Page 64: Intro Duccion Swift

10 - Encadenado de opcionales 59

1 let elecciones = Elecciones()

2 if let nombre = elecciones.candidato?.nombre {

3 print("El nombre del candidato es \(nombre)")

4 }

5 else {

6 print("El candidato no se puede obtener")

7 }

Al ejecutar el código anterior obtendremos por consola la segunda sentencia del else ya que nuestrainstancia no tiene nombre.

Con métodos

De forma similar a como lo hacemos con properties, también podemos usar el encadenado deopcionales en métodos. Basta con usar ’?’ antes de llamar al método:

1 class Arbol {

2 var naranjas: [Naranja] = []

3 }

4

5 class Naranja {

6 func exprimir() {

7 print("Exprimiendo naranja")

8 }

9 }

10

11 let arbol: Arbol = Arbol()

12 arbol.naranjas.first?.exprimir()

En el ejemplo anterior, el método first retorna un opcional porque podemos no tener naranjas en elarbol. Al quere exprimiarla tenemos que usar el encadenado de opcionales para llamar al método.Al ser nil la ejecución no imprimirá nada por consola.

Con subscripts

El encadenado de opcionales también aplica a subscripts a la hora de acceder a un index determinadode un elemento que pueda ser nil:

1 let array: [String]? = ["uno", "dos", "tres"]

2 let valor = array?[2]

Al ser array un opcional, si queremos acceder a algún elemento en un índice determinado tenemosque hacerlo con ’?’.

Page 65: Intro Duccion Swift

10 - Encadenado de opcionales 60

Encadenando múltiples niveles

Lo explicado anteriormente aplica a múltiples niveles de opcionales, sean properties, métodos osubscripts. Cuando algún elemento opcional de la cadena tiene valor nil, este valor se propagará alvalor que estás asignando y la cadena se detendrá.

Recuerda, a la hora de usar encadenado de opcionales, el valor de salida será siempreun opcional. De ahí que sea muy útil el uso de este patrón con el patrón if let ....

else {}

1 if let calle = pedro.residencia?.direccion?.calle {

2 print("La calle donde Pedro vive es \(calle)")

3 }

4 else {

5 print("Imposible obtener la calle")

6 }

Resumen

• El acceso a opcionales a través de distintos niveles se conoce como encadenado de opcionales.• Al usarlo para atributos si alguno de los opcionales retorna nil, la cadena retornará nil paradicho atributo. Por lo tanto este debe ser un opcional.

• En el caso de un método, el método no se acabará llamando.• El encadenado de opcionales también es válido con subscripts.

Page 66: Intro Duccion Swift

11 - Gestión de erroresSwift implementa un mecanismo de gestión de errores similar al de otros lenguajes de programacióncon algunas sutiles diferencias. Cualquier método en Swift puede ser definido como un método quelanza errores y se especifica mediante la palabra throws a la hora de definir el método. Por ejemplo,en el ejemplo inferior el método puede lanzar un error.

1 func myMethod() throws {

2 // Implementación

3 }

Lanzando errores: ErrorType

La forma de lanzar errores dentro del método es usando throw y pasando el tipo de error que se estálanzando. El requerimiento de Swift respecto a este tipo es que tiene que conformar el protocoloErrorType independientemente del tipo que sea. El error puede ser una clase, un struct, o incluso unenum. Los ejemplos mostrados son ejemplos válidos de errores:

1 class MyClassError: ErrorType {}

2 class MyStructError: ErrorType {}

3 class MyEnumError: ErrorType {}

Generalmente se usan enums para definir errores puesto que puedes englobar en ellas distintossubtipos de errores. Por ejemplo en el aso de un error que represente un error de HTTP, el enum querepresenta el error podría tener el siguiente formato:

1 enum HTTPError: ErrorType {

2 case ClientError

3 case ServerError

4 // Otros HTTP Errors

5 }

En cualquier punto de nuestro método podemos lanzar el error:

61

Page 67: Intro Duccion Swift

11 - Gestión de errores 62

1 func myMethod() throws {

2 throw HTTPError.ClientError

3 }

Swift no permite especificar el tipo de error que se está lanzando. Desde el punto de vistadel “consumidor” de este método, no sabe que errores internamente el método puedelanzar y tendrá que hacer hacer cast a los distintos tipos de errores y proponer accioensen función del error lanzado por el m´todo

Llamando métodos con try

Siempre que se ejecute un método que pueda lanzar un error Swift te forzará a hacerlo con try. Delo contrario el compilador te alertará y tu aplicación no compilará. En función si queremos o noignorar el error retornado, o de si estamos seguros de que el método no va a lanzar error algunopodemos usar try en diferentes contextos.

1 try myMethodThatThrows()

Capturando errores con do/catch

Si intentáramos el ejemplo anterior, el compilador se quejaría ya que estamos intentando ejecutarun método que lanza errores pero no estamos especificando como esos errores serían capturados.La primera opción es ser explícitos usando la sentencia do {} catch {} donde podremos espeificarqué hacer en el caso de obtener un error:

1 do {

2 try myMethodThatThrows()

3 }

4 catch {

5 // Oh! algo pasó, qué hacemos entonces?

6 }

Si no se especifica el tipo de error, en el bloque de catch se capturarán todos los errores,independientemente de su tipo. Si quisiéramos especificar un bloque de catch para un subconjuntodeterminado de errores podemos especificarlo en un patrón:

Page 68: Intro Duccion Swift

11 - Gestión de errores 63

1 do {

2 try methodThatTrows()

3 }

4 catch MyErrors.WeirdError {

5 // Especificamos que hacer en ese caso

6 }

7 catch MyErrors.UnexpectedError {

8 // Especificamos que hacer en ese caso

9 }

En el ejemplo superior, llamamos al método methodThatThrows() y capturamos los errores .Weird-Error y .UnexpectedError que puedan ser lanzados.

No es necesario especificar un catch para todos los posibles tipos de errores existentes. En elcaso de que ninguno de los bloques catch capture el error, este será propagado hacia el scope que hallamado al método, siendo este el responsable de decidir que hacer con él.

Ignorando errores

Otra opción a la hora de tratar con métodos que lanzan errores es ignorarlos, la forma de ignorarlostiene una sintaxis muy similar a los opcionales pero con respecto al try:

1 try? myMethodThatThrows()

En el caso de que el método retorne un valor (aparte de también lanzar errores), try? aplicado en elmétodo retornará un valor nil para el valor esperado:

1 func myMethod() throws -> String {

2 throw CustomError.Default

3 }

4

5 let value: String? = try? myMethod()

Un uso común de try? es con sentencias guard else donde nos quedamos con el valor si el métodono ha retornado error, y en el caso contrario en el bloque de else, especificamos que hacer:

1 guard let value = try? myMethodThatThrows() else {

2 // Hacemos algo

3 }

Deshabilitando la propagación de erores

La última opción es ideal para situaciones en las cuales estamos seguros de que el método no vaa lanzar error. En ese caso podemos directamente usar try! y nos ahorraría tener añadir todo elconjunto do/catch y en una linea de código tendríamos la ejecución del método.

Page 69: Intro Duccion Swift

11 - Gestión de errores 64

1 let value = try! myMethodThatThrows()

El uso de este operador es arriesgado ya que supone que conoces muy bien como secomporta el método que estás utilizando. Úsalo, pero de forma responsable. Al igualque

Defer

Puede interesarnos en determinados escenarios ejecutar una porción de código cuando por algunarazón (por ejemplo debido a errores) el método tiene que retornar.

Swift dispone de sentencias defer que te permite definir fragmentos de código que serán ejecutadoscuando el método tiene que retornar por cualquier razón. Los defer pueden ser definidos en cualquierposición de la función donde son creados y el orden de ejecución es inverso al order de definición,es decir, el último defer será ejecutado primero.

1 func dameBanana() -> Banana? {

2 defer {

3 self.recogerEscaleras()

4 }

5 do {

6 self.colocarEscaleras()

7 let banana: Banana = cogerBanana()

8 return pelarBanana(banana)

9 }

10 catch {

11 print("No te puedo dar la banana. Algo ha fallado: \(error)")

12 return nil

13 }

14 }

Resumen

• Los errores deben conformar el protocolo ErrorType y pueden ser clases, estructuras y enums.• Las funciones que pueden lanzar error son especificadas con throws.• En cualquier punto de la función podemos lanzar un error con throw.• Los errores pueden ser capturados de tres formas:

– Usando un bloque do {} catch {} si estamos interesados en el error.– Usando try? si no nos interesa el error pero si el valor.– Usando try! si nos interesa el valor y además estamos seguros de que no va a fallar.

• Existen bloques de defer {} que pueden ser utilizados en funciones para ser llamados antesde que una función salga de su ejecución por cualquier razón, por ejemplo, por un error.

Page 70: Intro Duccion Swift

11 - Gestión de errores 65

Ejercicios

Analiza el ejemplo anterior y explica, sin usar Xcode, por qué el fragmento inferior nocompilará

1 func getTracks() -> [Track] {

2 do {

3 return try client.getTracks()

4 }

5 catch Client.Errors.HTTPError {

6 print("Hubo un error con la petición HTTP")

7 }

8 catch Client.Errors.Mapping {

9 print("Hubo un error mapeando la respuesta")

10 }

11 }

Analiza el ejemplo inferior e indica que se imprimirá por consola

1 func myMethod() {

2

3 let privateMethod: () -> Void = {

4 defer {

5 print "U"

6 }

7 }

8

9 defer {

10 print "O"

11 }

12

13 privateMethod()

14

15 defer {

16 print "A"

17 }

18 }

Page 71: Intro Duccion Swift

12 - Casting de tiposPara validar el tipo de una instancia Swift ofrece una serie de herramientas para comprobarlo. Seusa para comprobar si el tipo de una instancia corresponde a una determinada clase padre, o si estádefinida en su propia jerarquía.

Los dos operadores que Swift ofrece para estas comprobaciones son is para comprobar el tipo, y as

para hacer cast de un tipo a otro tipo.

El casting de tipos también sirve para comprobar si una instancia conforma un determinadoprotocolo.

Comprobación del tipo

La forma de comprobar un tipo es mediante el operador is. El operador is comprueba si unainstancia pertenece a una subclase determinada y retorna true en ese caso. De lo contrario retornaráfalse:

1 class Asignatura {

2 let nombre: String

3 init(nombre: String) {

4 self.nombre = nombre

5 }

6 }

7

8 class Mates: Asignatura {

9 init() {

10 super.init(nombre: "mates")

11 }

12 }

13

14 let cualquierAsignatura: Asignatura = Assignatura(nombre: "Historia")

15 let mates: Mates = Mates()

16

17 print("CualquierAsignatura es Mates: \(cualquierAsignatura is Mates)") // false

18 print("CualquierAsignatura es Asignatura: \(cualquierAsignatura is Asignatura)")\

19 // true

20 print("mates es Mates: \(cualquierAsignatura is Mates)") // true

21 print("mates es Asignatura: \(cualquierAsignatura is Asignatura)") // true

66

Page 72: Intro Duccion Swift

12 - Casting de tipos 67

En el ejemplo anterior definimos una clase y subclase, Asignatura y Mates respectivamente,creando instancias de estas: cualquierAsignatura y mates. A la hora de comprobar el tipo decualquierAsignatura este retorna false cuando comprobamos si se trata de Mates ya que estainstancia no lo es. Al comprobar los tipos de mates ambos son ciertos ya que por un lado es unainstancia del tipo Mates pero al ser este subclase de Asignatura también pertenece a este tipo.

Casting a un tipo determinado

El operador is sólo sirve para comprobación. Si quisiéramos hacer un “cast” de esta variable, es decir,tratarlo a nivel de compilación como otro tipo distinto, tenemos el operador as y sus derivados as?y as!:

• as es usado cuando el casting hacia otro tipo es siempre posible porque los tipos involucradosen el casting tienen relación.

• as? se utiliza cuando el casting puede fallar. En ese caso el resultado del operador as? será nil.• as! cuando el casting puede fallar pero estás seguro de que no lo hará puedes usar un castingforzado.

1 class Asignatura {

2 let nombre: String

3 init(nombre: String) {

4 self.nombre = nombre

5 }

6 }

7

8 class Mates: Asignatura {

9 var formulas: [String] = []

10 init() {

11 super.init(nombre: "mates")

12 }

13 }

14

15 class Lengua: Asignatura {

16 var libros: [String] = []

17 init() {

18 super.init(nombre: "lengua")

19 }

20 }

21

22 let asignaturas: [Asignatura] = [Mates(), Lengua()]

Page 73: Intro Duccion Swift

12 - Casting de tipos 68

23

24 for asignatura in asignaturas {

25 if let lengua = asignatura as? Lengua {

26 print("Soy lengua y estos son mis libros: \(lengua.libros)")

27 }

28 else if let mates = asignatura as? Mates {

29 print("Soy mates y estas son mis formulas: \(mates.formulas)")

30 }

31 }

El operador as? es típicamente usado en sentencias if/let como se muestra en elejemplo anterior. De esta forma realizamos una validación segura de los tipos y noforzado, algo que se puede traducir en crashes en nuestros proyectos cuando alguienintroduce un tipo no esperado en el subconjunto.

Casteado de tipos: Any y AnyObject

Para representar una instancia que pertenece a cualquier tipo (incluyendo funciones y value types)se puede usar el tipo Any:

1 var asignaturas: [Any] = []

2 asignaturas.append(Mates())

3 asignaturas.append(Lengua())

4 asignaturas.append("Desconocida")

De la misma forma que en el ejemplo anterior, podemos hacer casing de Any a cualquier tipo usandoas.

Si quisieramos restringir el subconjuto de tipos soportados a sólo valores de referencias, Objects,podemos usar en su lugar AnyObject

1 var asignaturas: [AnyObject] = []

2 asignaturas.append(Mates())

3 asignaturas.append(Lengua())

Si en el ejemplo anterior intentásemos añadir un enum, o una función el compiladornos alertaría de que no se trata de un valor de referencia y por lo tanto no puede serañadido al array.

Page 74: Intro Duccion Swift

12 - Casting de tipos 69

Resumen

• Swift permite comprobar el tipo o protocolo de una instancia con el operador is.• Podemos hacer casting de un tipo a otro, o a un protocolo determinado con el operador as ysus respectivas versiones as? y as!.

• Swift ofrece dos tipos genéricos:– Any para cualquier tipo, sea de referencia o de valor.– AnyObject para cualquier tipo de referencia únicamente.

Page 75: Intro Duccion Swift

13 - Tipos encadenadosTipos encadenados encadenados son tipos cuya definición está incluida dentro de otros tipos. Sonútiles para organizar el espacio de nombres de nuestra base de código.

Por ejemplo supón que tenemos un enum que representa el estado de un mensaje

1 enum EstadoMensaje {

2 case Enviado

3 case Recibido

4 case Leido

5 }

¿Como se podría usar el espacio de nombres con el ejemplo anterior? De forma muy sencilla,encadenando un tipo Estado dentro de la clase Mensaje

1 class Mensaje {

2

3 enum Estado {

4 case Enviado

5 case Recibido

6 case Leido

7 }

8

9 }

Sintáxis del punto y la inferencia de tipos

Al usar tipos encadenados, podemos acceder a estos tipos usando notación de punto accediendo através de los tipos hasta llegar al deseado. Siguiendo con el ejemplo anterior:

1 let estado: Mensaje.Estado = .Recibido

Al haber especificado el tipo de nuetra variable, no es necesario de nuevo encadenar todos los tipospara Recibido, es decir, Mensaje.Estado.Recibido, si no que simplemente especificamos el valordel enum.

70

Page 76: Intro Duccion Swift

13 - Tipos encadenados 71

Resumen

• Swift facilita tener un espacio de nombres permitiendo definir tipos dentro de otros tipos (tiposencadenados).

• Para acceder a través de la cadena de tipos se usa ”.”.

Page 77: Intro Duccion Swift

14 - ExtensionesLa funcionalidad de una estructura, enum, y clase existente puede ser extendida gracias a la ayoudade extensiones. Estas no permiten sin embargo reemplazar funcionalidad existente.

Con las extensiones podemos:

• Añadir nuevos atributos de instancia y de tipo computados.• Definir nuevos métodos de instancia y de tipo.• Añadir nuevos constructores.• Definir subscripts.• Definir y usar nuevos tipos encadenados.• Conformar un protocolo.

La forma de definir una extensión es la siguiente:

1 extension MiTipo {

2 // Nueva funcionalidad a añadir

3 }

Si la extension es para conformar un protocolo existente, la forma de especificarlo es la siguiente:

1 extension Tipo: Protocolo1, Protocolo2 {

2 // Implementa los requerimientos del protocolo aqui

3 }

Atributos computados

Las extensiones permiten añadir nuevos atributos de instancia y de tipo computados:

1 extension Int {

2 var double: Int { return 2*self } // atributo de instancia

3 var half: Int { return self/2 } // Atributo de instancia

4 static var zero: Int { return 0 } // Atributo de tipo

5 }

72

Page 78: Intro Duccion Swift

14 - Extensiones 73

Constructores

Swift ofrece la flexibilidad de añadir nuevos constructores a tipos existentes gracias al uso deextensiones. El usuario puede añadir sus propiso tipos para extender los tipos ya existentes y añadirnuevas opciones de inicialización.

Las extensiones soportan init() pero no deinit()

1 struct Rectangulo {

2 let size: Double

3 var area: Double { return size*size }

4

5 init(size: Double) {

6 self.size = size

7 }

8 }

9

10 extension Rectangulo {

11 init(area: Double) {

12 self.size = sqrt(area)

13 }

14 }

En el ejempo anterior, extendemos Rectangulo para inicializarlo a partir de otro valor, en este casoa partir del area.

Métodos

Nuevos métodos de tipo y de instancia pueden ser añadidos de forma muy sencilla:

1 extension Int {

2 func mas(numero: Int) -> Int {

3 return self + numero

4 }

5 func esPar() -> Bool {

6 return numero%2 == 0

7 }

8 static func cero() -> Int {

9 return 0

Page 79: Intro Duccion Swift

14 - Extensiones 74

10 }

11 }

12

13 let cinco = 3.mas(2)

14 let esPar = 4.esPar() // true

15 le cero = Int.cero()

Métodos de instancia mutables

Los métodos de instancia pueden también ser mutables cuando los declaramos en extensiones.

Los métodos de estructuras y enums puedenmodificar a self a sus propiedades. En ese caso el métododebe marcarse como mutating, de la misma forma en la que lo hacemos en la implementaciónoriginal:

1 extension Double {

2 mutating func saure() {

3 let pi = 3.1415

4 self = pi * self * self

5 }

6 }

Subscripts

Swift también permite utilizar extensiones para añadir nuevos subscripts

1 extension Int {

2 subscript(var indice: Int) -> Int {

3 var no1 = 1

4 while indice > 0 {

5 no1 *= 10

6 --indice

7 }

8 return (self / no1) % 10

9 }

10 }

Tipos encadenados

Los tipos encadenados para clases, estructuras y enums también pueden ser extendidas con la ayudade extensiones.

Page 80: Intro Duccion Swift

14 - Extensiones 75

1 extension Int {

2 enum Calc {

3 case Sumar

4 case Restar

5 case Multiplicar

6 case Dividir

7 case Otro

8 }

9 var print: Calc {

10 switch self {

11 case 0:

12 return .Sumar

13 case 1:

14 return .Restar

15 case 2:

16 return .Multiplicar

17 case 3:

18 return .Dividir

19 default:

20 return .Otro

21 }

22 }

23 }

Protocolos

En la reciente versión de Swift, se introdujo la posibilidad de extender protocolos para ofrecerimplementaciones base de métodos definidos en el protocolo:

1 protocolo Dinamico {

2 func mover()

3 }

4

5 struct Persona: Dinamico {

6 func caminar() {

7 print("Estoy caminando")

8 }

9 }

10

11 extension Dinamico where Self:Persona {

12 func mover() {

Page 81: Intro Duccion Swift

14 - Extensiones 76

13 self.caminar()

14 }

15 }

En el ejemplo anterior Dinamico es nuestro protocolo, y Persona conforma dicho protocolo. Sinembargo Persona no implementa los métodos requeridos por Dinamico. Lo que hacemos en su lugares crear una extensión del protocolo definiendo el comportamiento de mover() cuando la entidadque conforme el protocolo sea una Persona.

La forma de limitar el subconjunto de entidades que tendrán dicha implementación por defecto esmediante:

1 where Self:MiTipo

Aunque en el ejemplo hayamos especificado el subconjunto de entidades que tendría dichaimplementación por defecto, también podemos especificarlo para todas las que conformen dichoprotocolo:

1 extension Dinamico {

2 func mover() {

3 print("Me muevo")

4 }

5 }

Las extensiones de protocolos son muy útiles ya que permite heredar comportamientosya definidos en cualquiera de nuestros tipos existentes sin necesidad de tener querecurrir a subclases o derivados.

Resumen

• Las extensiones permiten añadir funcionalidades a elementos existentes.• Permiten añadir nuevos atributos computados (no almacenados).• Permiten definir nuevos constructores.• Permiten añadir nuevos métodos.• También permiten añadir nuevos subscripts y también encadenar tipos.• Los protocolos también pueden ser extendidos para ofrecer implementaciones por defectos enel mismo. Esto es conocido en Swift 2.0 como extensión de protocolos.

Page 82: Intro Duccion Swift

15 - ProtocolosLos protocolos sirven para definir abstracciones en las interfaces de nuestros estructuras, enums,clases. Sirven para definir el comportamiento de estos especificando que propiedades tienen, losmétodos que exponen así como los constructores.

Sintáxis

La forma de definir un protocolo es la siguiente

1 protocol MiProtocolo {

2 // Definición del protocolo

3 }

Clases, estructuras, y enums especifican la conformación de un protocolo de la siguiente forma:

1 struct MiStruct: Protocolo1, Protocolo2 {

2 // Definición del struct

3 }

Pudiendo conformar más de un protocolo simultáneamente.

Requerimientos de métodos y properties

A la hora dedefinir nuestro protocolo podemos especificar atributos ymétodos que cualquier entidadconformando el protocolo deberá implementar.

• La forma de especificar los atributos es mediante var miVariable: Tipo { get set}. Desdeel punto de vista del protocolo se desconoce si la variable es almacenada o computada,simplemente el tipo y si puede ser escriba, leida, o ambas.

• La forma de especificar los métodos sería de la misma forma en la que definimos una funciónsólo que en este caso no definimos el cuerpo del mismo: func miFuncion() -> Bool

77

Page 83: Intro Duccion Swift

15 - Protocolos 78

1 protocol MiProtocolo {

2 var nombre: String { get }

3 func eliminar() -> Bool

4 }

Requerimientos de mutabilidad en los métodos de unprotocolo

Si un método especificado en un protocolo puede modificar la instancia que lo conforma, es decir,“mutarlo” el método debe ser de tipo mutating

1 protocol Crece {

2 mutating func cumpleanyos()

3 }

4

5 public struct Persona: Crece {

6

7 var anyos: Int = 0

8

9 mutating func cumpleanyos() {

10 self.anyos = self.anyos + 1

11 }

12 }

En el ejemplo anterior el método cumpleanyos() se espera que mute a la entidad que lo conforma,por eso especificamos que dicho método es mutating.

Requerimientos en constructores

Los protocolos en Swift también permiten especificar requerimientos de determinados constructores:

Page 84: Intro Duccion Swift

15 - Protocolos 79

1 protocol MiProtocolo {

2 init(name: String)

3 }

4

5 class MiClase: MiProtocolo {

6

7 required init(name: String) {

8 // Inicializa MiClase

9 }

10 }

En este caso Swift forzará a definir dicho constructor como required para asegurar de formaexplicita que todas las subclases de dicha clase implementan este constructor.

Conformar protocolos mediante extensiones

Un tipo existente puede conformar un protocolo haciendo uso de extensiones. Los requerimientosdel protocolo pueden ser implementados en dicha extension. Por ejemplo:

1 protocol EdadClasificable {

2 var edad: Int { get }

3 func tipo() -> String

4 }

5

6 class Persona {

7 let nombre: String

8 let edad: Int

9 init(nombre: String, edad: Int) {

10 self.nombre = nombre

11 self.edad = edad

12 }

13 }

14

15 etension Persona: EdadClasificable {

16

17 func tipo() -> String {

18 switch edad {

19 case 0...2:

20 return "Bebe"

21 case 2...12:

22 return "Niño"

Page 85: Intro Duccion Swift

15 - Protocolos 80

23 case 13...19:

24 return "Joven"

25 case let x where x> 65:

26 return "Mayor"

27 default:

28 return "Normal"

29 }

30 }

31 }

A la hora de extender el protocolo no hemos tenido que especificar el atributo edad ya que este vieneya definido en la clase que está conformando el protocolo.

Swift no permite variables computadas en extensiones.

Composición de protocolos

Swift permite la composición de múltiples protocolos en un nuevo protocolo mediante:

1 protocol <ProtocoloA, ProtocoloB>

Por ejemplo:

1 protocolo TieneNombre {

2 var name: String { get }

3 }

4

5 protocolo Piensa {

6 func razona()

7 }

8

9 func print(humano: protocol<TieneNombre, Piensa>) {

10 humano.razona()

11 print("El humano: \(humano.name) razonó")

12 }

Comprobar la conformación de un protocolo

Gracias al casting de tipos (as & is) podemos comprobar si una entidad conforma un determinadoprotocolo.

Page 86: Intro Duccion Swift

15 - Protocolos 81

• El operador is retorna true si la entidad conforma el protocolo.• La versión as del operador de casting retorna un valor del tipo del protocolo. Si la entidad noconformara dicho protocolo, retornaría nil en su lugar.

1 protocol Figura {

2 var area: Float { get }

3 }

4

5 class Circunferencia: Figura {

6 let radio: Float

7 init(radio: Float) {

8 self.radio = radio

9 }

10 var area: Float {

11 return 3.141516*self.radio*self.radio

12 }

13 }

14

15 protocol Poligono: Figura {

16 var lados: Int { get }

17 }

18

19 class Cuadrado: Poligono {

20 let ancho: Float

21 var lados: Int {

22 return 4

23 }

24 var area: Float {

25 return self.ancho*self.ancho

26 }

27 init(ancho: Float) {

28 self.ancho = ancho

29 }

30 }

31

32 let figura: Figura = Circunferencia(radio: 25)

33 if let circunferencia = figura as? Circunferencia {

34 print("Poligono de radio \(circunferencia.radio)")

35 }

36 else if let poligono = figura as? Poligono {

37 print("Poligono de \(poligono.lados) lados")

38 }

Page 87: Intro Duccion Swift

15 - Protocolos 82

Resumen

• Gracias a los protocolos podemos definir comportamientos y abstraer concrecciones.• Los protocolos permiten definir los requerimientos a nivel de:

– Constructores.– Atributos y su accesibilidad.– Métodos.

• Los protocolos pueden ser conformados por elementos existentes usando extensiones.• Pueden ser compuestos de la siguiente forma protocol<Protocolo1, Protocolo2>.• Los operadores is and as para comprobación y casting de tipos también funcionan a nivel deprotocolos.

Page 88: Intro Duccion Swift

16 - GenéricosUna de las funcionalidades más potentes introducidas por Swift es genéricos. Genéricos permitenescribir funciones y tipos flexibles y reusables sin dejar de lado la seguridad de tipos de Swift.

Funciones genéricas: Parametros tipados

Las funciones genéricas pueden ser usadas para acceder a cualquier tipo de dato como por ejemploInt o String.

1 func cambiar<T>(inout a: T, inout b: T) {

2 let temp = a

3 a = b

4 b = temp

5 }

6 var num1 = 100

7 var num2 = 200

8 var str1 = "ola"

9 var str2 = "kase"

10 print("Antes de cambiarlos: \(num1) & \(num2)") // 100 & 200

11 cambiar(&num1, &num2)

12 print("Después de cambiarlos: \(num1) & \(num2)") // 200 & 100

13 print("Antes de cambiarlos: \(str1) \(str2)") // ola kase

14 cambiar(&str1, &str2)

15 print("Después de cambiarlos: \(str1) \(str2)") // kase ola

La función cambiar<T> es una función genérica con tipo de parámetro <T>. De esta forma podemosreutilizar la misma función para distintos tipos de como entrada de la función.

Al especificar un parámtro como inout estamos dando la funciónmodifica directamentela referencia a dicho parámetro

Tipos genéricos

De forma similar también podemos especificr un tipo como genérico

83

Page 89: Intro Duccion Swift

16 - Genéricos 84

1 struct Coleccion<T> {

2 var elementos: [T] = []

3

4 mutating func anyadir(elemento: T) {

5 elementos.append(elemento)

6 }

7 }

La estructura Collecion en el ejemplo mostrado es genérico de tipo T. Al ser un tipo genérico elvalor de T puede ser usado internamente, como por ejemplo para indicar un array de elementos dela colección o para especificar que tipo de elementos pueden ser añadidos a la colección.

Extendiendo un tipo genérico

A la hora de extender un tipo genérico también podemos hacer uso del valor genérico. En el casodel ejemplo anterior:

1 extension Coleccion {

2 var primer: T? {

3 return elementos.firstObject

4 }

5 }

Donde T es visible en la extensión y podemos utilizarlo para añadir nuevas funcionalidades.

Constraints para tipos

A la hora de usar generics Swift permite restringir estos tipos a un subconjunto determinado. Estose conoce como type constraints. La forma de especificarlo es la siguiente:

1 func imprimir<T: StringLiteralConvertible>(valor: T) {

2 print("Imprimiendo: \(valor)")

3 }

Donde T es el tipo genérico y StringLiteralConvertible el subconjunto de valores genéricos quepueden ser admitidos. Ese subconjunto puede ser especificado mediante una clase o un protocolo alcual deben conformar.

Tipos asociados (Protocolos Genéricos)

En el caso de querer definir protocolos genéricos, Swift ofrece el concepto de “tipos asociados”. A lahora de definir el carácter genérico de un protocolo se especifica mediante typealias en el protocolocuyo tipo es especificado cuando el protocolo es conformado.

Page 90: Intro Duccion Swift

16 - Genéricos 85

1 protocol Contenedor {

2 typealias TipoElemento

3 mutating func anyadir(elemento: TipoElemento)

4 var total: Int { get }

5 subscript(i: Int) -> TipoElemento { get }

6 }

7

8 struct Coleccion<T>: Contenedor {

9

10 var elementos: [T] = []

11

12 mutating func anyadir(elemento: T) {

13 elementos.append(elemento)

14 }

15

16 mutating func pop() -> T {

17 return elementos.removeLast()

18 }

19 var total: Int {

20 return elementos.count

21 }

22

23 subscript(i: Int) -> T {

24 return elementos[i]

25 }

26 }

En el ejemplo anterior estamos definientdo en Contenedor (protocolo) con un typealis TipoElemento.Este protocolo es conformado por Colleccion. Swift automáticamente tratará de inferior el valor deTipoElemento cuando el protocolo es conformado. En el caso de no poder inferir el tipo, el valor deTipoElemento deberá definirse el el enum/clase/estructura que conforme el protocolo mediante:

1 typealis TipoElemento = MiTipo

Sentencias Where

De la misma forma que Swift permite definir constraints para el tipo de los genéricos, también lopermite para los tipos asociados. La forma de hacerlo en Swift es mediante sentencias where que secoloca a continuación de la lista de tipos de parámetros:

Page 91: Intro Duccion Swift

16 - Genéricos 86

1 func matchElementos<

2 C1: Contenedor, C2: Contenedor

3 where C1.TipoElemento == C2.TipoElemento, C1.TipoElemento: Equatable>

4 (contenedor: C1, contenedor: C2) -> Bool {

5 return someContainer.count == anotherContainer.count

6 }

En el ejemplo mostrado estamos usando la sentencia where para comprobar que el el tipo delelemento de C1 y de C2 es el mismo y que además conforma el protocolo Equatable.

Resumen

• Los genéricos permiten definir funciones y tipos flexibles a cualquier tipo sin dejar de lado laseguridad de tipos de Swift.

• Podemos definir funciones con parámetros (de entrada y salida) genéricos.• Estructuras y clases también pueden ser genéricas.• El conjunto de tipos genéricos puede ser restringido mediante constraints.• Los protocolos también pueden ser genéricos y los tipos de estos son especificados mediantetypealias. Esto se conoce como tipos asociados.

• También podemos restringir el conjunto de tipos genéricos de tipos asociados mediantesentencias where.

Page 92: Intro Duccion Swift

17 - Control de accesoPara restringir el acceso a bloques de código, módulos y abstracciones podemos usar controlde acceso en Swift. Clases, estructuras y enusm puede ser accecdidos de acuerdo al control deacceso especificado para sus properties, métodos, constructores y subscripts. Constantes, variables yfunciones en un protocolo están restringidas y se permite el acceso de forma global y local medianteel control de acceso.

El control de acceso se basa en módulos y sus ficheros código fuente.

Se definemódulo como una unidad única de distribución de código que puede importarse medianteimport MiModulo. Un fichero de código fuente es cada uno de los ficheros que pertenecen almódulo.

Los tres niveles de acceso que ofrece Swift son public, internal y private.

• Public: Las entidades definidas como públicas en un módulo pueden ser accedidas desde elmismo módulo y también desde otros módulos que importen a este.

• Internal: Las entidades definidas cómo internal pueden ser utilizadas desde el propio módulopero no desde otros módulos, incluso si el módulo es importado.

• Private: En este caso las entidades definidas como private en un código fuente son sólo visiblesdesde este fichero. Si otros ficheros del mismo módulo intentaran usar dicha entidad, esta nosería visible.

Algunos ejemplos de control de acceso serían:

1 private var nombre: String = "Pedro"

2 private class Cancion {}

3 internal func caminar() {}

4 public static let url: String = "https://github.com"

Por defecto el tipo acceso de cualquier elemento definido es internal

Si una entidad contiene entidades en su interior, el nivel de acceso en la entidad padredetermina el nivel de restricción en las hijas. Por ejemplo, si una clase definiera su nivelde acceso como private e internamente tuviera un atributo como public el compiladoralertaría sobre el conflicto (sin llegar a bloquear la compilación).

Control de acceso en funciones

La forma de especificar el control de acceso en funciones es añadiendo el tipo de acceso al comienzode la definición de la función como se muestra en el ejemplo inferior:

87

Page 93: Intro Duccion Swift

17 - Control de acceso 88

1 public func suma(a: Int, b: Int) -> Int {

2 return a + b

3 }

Si los parámetros de la función tuvieran un nivel de acceso más restrictivo que el de la propia funciónel proceso de compilación lanzaría un error:

1 private class Tarjeta {}

2

3 public func guardar(tarjeta: Tarjeta) {

4 print("Guardando tarjeta: \(tarjeta)")

5 }

Control de acceso en Enums

A la hora de definir el control de acceso de un enum este se especifica en la declaración del propioenum. No es posible especificar un nivel de acceso para cada una de las sentencias del enum y todasellas heredan el nivel de acceso del enum:

1 public enum Error {

2 case HTTPError

3 case StoreError

4 case Unknown

5 }

Control de acceso en subclases

Swift permite heredar clases que son visibles en el contexto donde se está heredando. La subclasenunca puede tener un nivel de acceso menos restrictivo que la clase de la que heredan.

1 public class Cricket {

2 private func comenzar() {

3 print("Bienvenido a Cricket")

4 }

5 }

6

7 internal class Tennis: Cricket {

8 override internal func comenzar() {

9 print("Bienvenido a Tennis")

10 }

11 }

Page 94: Intro Duccion Swift

17 - Control de acceso 89

En el ejemplo anterior la subclase Tennis puede sobreescribir el valor la función comenzar() ya queambas definición están en el mismo fichero y por lo tanto son visibles. Si se intentara usar la claseCricket desde otro módulo el método comenzar() no sería visible, pero sin embargo, si que lo seríapara la clase Tennis.

Control de acceso en constantes, variables, propertiesy subscripts

Constantes, variables, o properties sólo pueden ser definidas con un nivel de acceso menos restrictivoque el de su tipo. Por ejemplo:

1 private class MiClasePrivada {}

2 public static let miClase: MiClasePrivada = MiClasePrivada()

No compilaría ya que estamos intentando hacer público un tipo que de por sí es privado.

Getters y Setters

Los getters y setters definidos para constantes, variables, properties y subscripts automáticmanetereciben el mismo nivel que el de la constante, variable, property o subscript al que pertenecen:

1 public class Ejemplo {

2 private var contador: Int = 0 {

3 willSet(nuevoValor) {

4 print("El nuevo valor es \(nuevoValor)")

5 }

6 didSet {

7 if contador > oldValue {

8 print("Se han añadido \(contador - oldValue)")

9 }

10 }

11 }

12 }

13 let ejemplo = Ejemplo()

14 ejemplo.contador = 100

15 ejemplo.contador = 400

16

17 // El nuevo valor es 100

18 // Se han añadido 100

19 // El nuevo valor es 400

20 // Se han añadido 300

Page 95: Intro Duccion Swift

17 - Control de acceso 90

Control de acceso en constructores y constructorespor defecto

A la hora de definir un constructor, este debe de tener un nivel de acceso igual o inferior al de laentidad que está inicializando. El único caso en el que el nivel de acceso tiene que ser el mismo queel de la clase es en el caso de required initializers.

Los tipos de los parámetros de los constructores tienen que tener un nivel igual o menos restrictivoque el de la clase que están inicializando:

Los constructores por defecto tienen el mismo nivel de acceso del tipo de acceso que el tipo queestán inicializando, amenos que el tipo que están inicializando sea public. En este caso el constructorpor defecto será internal. Si quisiéramos tener un constructor público para ser usado desde otromódulo, este debería definirse explicitamente como parte de la definición del tipo.

1 // Modulo A

2 public class ClassA {}

3

4 // Modulo B

5 import ModuloA

6 let miObjetoA: ClassA = ClassA()

El ejemplo anterior no compilaría porque desde el módulo B no es visible el constructor por defectode dicha clase. Tendríamos que expecificarlo de forma explícita:

1 public class ClassA {

2 public init() {}

3 }

Control de acceso en protocolos

Cuando definimos un nuevo protocolo para heredar funcionalidades de un protocolo existente,ambos tienen que estar definidos con el mismo nivel de acceso para heredar las propiedades del otro.El control de acceso de Swift no permite a los desarrolladores definir un prótocolo como public quehereda de un internal protocolo:

Page 96: Intro Duccion Swift

17 - Control de acceso 91

1 public protocol ProtocoloTCP {

2 init(no1: Int)

3 }

4

5 public class ClasePrincipal {

6 var no1: Int

7 init(no1: Int) {

8 self.no1 = no1

9 }

10 }

11

12 class Subclase: ClasePrincipal, ProtocoloTCP {

13

14 var no2: Int

15

16 init(no1: Int, no2: Int) {

17 self.no2 = no2

18 super.init(no1: no1)

19 }

20

21 required override convenience init(no1: Int) {

22 self.init(no1: no1, no2: 0)

23 }

24

25 }

26

27 let clase1 = ClasePrincipal(no1: 20)

28 let clase2 = Subclase(no1: 30, no2: 50)

• Definimos un protocolo: ProtocoloTCP• Subclase hereda de ClasePrincipal y además conforma el protocolo ProtocoloTCP

• Define un primer constructor init(no1: Int, no2: Int) que:– No sobreescribe ninguno de la clase padre, por lo tanto no es necesario especificarlo con

override

– Llama al designed initializer de la clase padre para finalizar la inicialización.• Define un segundo constructor que required override convenience init(no1: Int):

– Sobreescribe el constructor de la clase padre, por eso es necesario especificarlo conoverride.

– Es un convenience initializer ya que usa el otro constructor para el proceso de inicial-ización.

Page 97: Intro Duccion Swift

17 - Control de acceso 92

Control de acceso en extensiones

Swift no permite definir el nivel de acceso de una extensión cuando el usuario la usa para especificarque la entidad conforma un nuevo protocolo. El nivel de acceso para cada requerimiento delprotocolo viene determinado por el propio nivel de acceso del protocolo:

1 public protocol PuedoVolar {

2 func vuela()

3 }

4

5 private class Avion {}

6

7 extension Avion: PuedoVolar {

8 public func vuela() {

9 print("Estoy volando")

10 }

11 }

El ejemplo anterior lanzaría una alerta ya que estamos especificando un nivel de acceso público parala función vuela() en Avion cuando el tipo de Avion es privado. No es necesario.

Resumen

• Existen tres niveles de acceso en Swift: public, private e internal. Estos determinan lavisibilidad entre ficheros fuente y entre módulos.

• Los niveles de acceso pueden ser especificados en funciones, atributos y constructores.• También a nivel de tipos, clases, estructuras, y enums.• El nivel de acceso de un enum aplica a todos los cases.• en el caso de extensiones Swift no permite definir el nivel de acceso de una extensión.

Ejercicio

Analiza el siguiente ejemplo y aplica las correcciones que sean necesarias para que desdeModulo1 se pueda ejecutar un comando de API (Sin uar Xcode):

Page 98: Intro Duccion Swift

17 - Control de acceso 93

1 // Modulo 1

2 // Fichero A

3 internal class Comando {

4

5 private let nombre

6

7 public init(name: String) {

8 self.nombre = nombre

9 }

10

11 public func ejecutar() {

12 print("Ejecutando: \(nombre)")

13 }

14 }

15

16 // Modulo 1

17 // Fichero B

18 public class ComandoAPI: Comando {

19

20 }

21

22 // Modulo 2

23 import Modulo1

24

25 ComandoAPI().ejecutar()