Documente Academic
Documente Profesional
Documente Cultură
This is a Leanpub book. Leanpub empowers authors and publishers with the Lean Publishing
process. Lean Publishing is the act of publishing an in-progress ebook using lightweight tools and
many iterations to get reader feedback, pivot until you have the right book and build traction once
you do.
2016 Pedro Piera Buendia and David Ortega
Contents
1 - Variables y tipos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Ejercicio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1
8
2 - Colecciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3 - Control de flujo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
14
4 - Closures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
20
5 - Clases y estructuras . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Clases y estructuras . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Propiedades y mtodos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
24
24
26
6 - Subscripts . . . . . . . . . . . .
Definicin de subscripts . . . .
Opciones de Subscripts . . . .
Dnde son tiles los subscripts
Resumen . . . . . . . . . . . .
Ejercicio . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
31
31
32
33
33
33
7 - Herencia . . . . . . . . . .
Definiendo la clase base . .
Heredando de la clase base
Sobreescritura (Overriding)
Resumen . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
34
34
34
35
38
8 - Inicializacin y deinicializacin
Inicializacin . . . . . . . . . . .
Deinicializacin . . . . . . . . .
Resumen . . . . . . . . . . . . .
Ejercicio . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
39
39
47
48
48
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
50
50
51
53
.
.
.
.
.
.
.
.
.
.
CONTENTS
55
57
10 - Encadenado de opcionales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Encadenando mltiples niveles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Resumen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
58
60
60
11 - Gestin de errores . . . . .
Lanzando errores: ErrorType
Llamando mtodos con try .
Resumen . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
61
61
62
64
12 - Casting de tipos . . . . . . . . . . .
Comprobacin del tipo . . . . . . .
Casting a un tipo determinado . . .
Casteado de tipos: Any y AnyObject
Resumen . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
66
66
67
68
69
13 - Tipos encadenados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Sintxis del punto y la inferencia de tipos . . . . . . . . . . . . . . . . . . . . . . . . . .
Resumen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
70
70
71
14 - Extensiones . . . . . . . . . .
Atributos computados . . . . .
Constructores . . . . . . . . .
Mtodos . . . . . . . . . . . .
Mtodos de instancia mutables
Subscripts . . . . . . . . . . .
Tipos encadenados . . . . . . .
Protocolos . . . . . . . . . . .
Resumen . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
72
72
73
73
74
74
74
75
76
15 - Protocolos . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Sintxis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Requerimientos de mtodos y properties . . . . . . . . . . . . .
Requerimientos de mutabilidad en los mtodos de un protocolo
Requerimientos en constructores . . . . . . . . . . . . . . . . .
Conformar protocolos mediante extensiones . . . . . . . . . . .
Composicin de protocolos . . . . . . . . . . . . . . . . . . . .
Comprobar la conformacin de un protocolo . . . . . . . . . .
Resumen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
77
77
77
78
78
79
80
80
82
16 - Genricos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Funciones genricas: Parametros tipados . . . . . . . . . . . . . . . . . . . . . . . . . . .
Tipos genricos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
83
83
83
CONTENTS
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
84
84
84
85
86
17 - Control de acceso . . . . . . . . . . . . . . . . . . . . . . . . . .
Control de acceso en funciones . . . . . . . . . . . . . . . . . . .
Control de acceso en Enums . . . . . . . . . . . . . . . . . . . .
Control de acceso en subclases . . . . . . . . . . . . . . . . . . .
Control de acceso en constantes, variables, properties y subscripts
Getters y Setters . . . . . . . . . . . . . . . . . . . . . . . . . . .
Control de acceso en constructores y constructores por defecto . .
Control de acceso en protocolos . . . . . . . . . . . . . . . . . . .
Control de acceso en extensiones . . . . . . . . . . . . . . . . . .
Resumen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Ejercicio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
87
87
88
88
89
89
90
90
92
92
92
1 - Variables y tipos
Si has llegado hasta aqu es que te interesa aprender un poco ms sobre Swift Swift es un lenguaje
de programacin creado por Apple y enfocado en el desarrollo de aplicaciones para toda su familia
de productos (iOS, Mac OS X, watchOS y tvOS). Fue presentado en el WWDC 2014 y est diseado
de tal manera que puede usar cualquier biblioteca programada en Objective-C, as como llamar a
funciones de C. Swift usa el compilador LLVM, incluido en XCode a partir de la versin 6 y en 2015
Apple liber la especificacin del lenguaje, que pas a ser de cdigo abierto.
Swift provee de una versin propia de los tipos bsicos de C y Objective-C, incluyendo Int para
enteros, Doubley Float para valores en coma flotante, Bool para Booleanos y String para cadenas
de texto.
Swift es un lenguaje fuertemente tipado con inferencia de tipos, esto quiere decir que cuando
creamos una variable esa variable ser del mismo tipo durante toda su vida y que, adems, no
siempre es necesario poner el tipo de las variables ya que el compilador lo inferir por el contexto.
Adems de las variables, en Swift es posible usar constantes, que permiten realizar un cdigo mucho
ms seguro y claro.
En Swift tambin se introducen los tipos opcionales, que gestionan la ausencia de valor. Se usan de
forma similar a los nil en Objective-C, pero son mucho ms seguros que estos.
Las constantes y variables asocian un nombre como puede ser contadoro nombreEmpleadocon un
valor de un tipo en concreto (1y Juan, por ejemplo). El valor de una constante no se puede cambiar
en el ciclo de vida de sta, pero las variables s que pueden ser modificadas con un nuevo valor en
el futuro.
Declarando Constantes y variables
Las constantes y variables han de ser declaradas antes de poder ser usadas. Para declarar una
constante utilizamos la palabra reservada lety para declarar una variable usamos la palabra
reservada var.
1
2
let contador = 1
var nombreEmpleado = "Juan"
1 - Variables y tipos
Anotaciones de tipo
Como hemos dicho, Swift es capaz de inferir el tipo de las variables y constantes en casi todas las
ocasiones. En cualquier caso, siempre tenemos la posiblidad de especificar el tipo que debe ser la
variable.
1
Los dos puntos se podran traducir como de tipo, con lo que la linea anterior se leera ms o menos
as:
Declara la variable nombreEmpleado de tipo String
Igual que antes, podemos declarar multiples variables del mismo tipo en una sola linea:
1
En cambio, para las constantes, no es posible cambiar el valor una vez que se ha puesto. Si lo
intentaramos recibiramos un error de compilacin:
1
2
3
Tuplas
Adems de los tipos conocidos como Int, Float, Double, Bool o String en Swift se incluye otro tipo
que nos permitir trabajar con agrupaciones de estos tipos bsicos de una forma muy eficaz, son las
tuplas.
Por ejemplo, para definir una variable con los datos de una coordenada geogrfica podramos hacerlo
de la siguiente manera
1 - Variables y tipos
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Opcionales
En Swift nos encontramos la opcin de definir que una variable o constante tiene o no tiene valor. Es
un concepto parecido a nil en Objective-C, pero no es exactamente lo mismo, ya que nil significa
la ausencia de un valor vlido y los opcionales de Swift significan que no existe ningn tipo de
valor. Adems, los opcionales de Swift funcionan para cualquier tipo de valor, mientras que nil en
Objective-C slo funciona para objetos.
Aqu dejamos un ejemplo sencillo de cmo se llegara a tener una variable opcional
1
2
3
Esto pasara porque el inicializador de Int podra fallar si, por ejemplo, numeroPosible hubiera
tenido un valor Pepito.
Para poner una variable opcional como sin valor utilizamos la palabra reservada nil. >Hay que
tener en cuenta que nil solo se puede utilizar en variables opcionales. Es decir, cuando se defina
una variable que debera poder no tener valor en ciertas condiciones la deberemos inicializar como
opcional.
Para definir una variable opcional utilizamos el carcter ? despus del tipo
1 - Variables y tipos
Para poder trabajar con un opcional debemos de desenvolverlo primero para comprobar si tiene
valor. Una forma sera mediante una comparacin en una sentencia if.
Adems, para poder acceder al valor de ese entero debemos utilizar el caracter exclamacin ! para
extraerlo. swift if numero != nil { print("numero contiene un entero: \(numero!)") }
Una forma ms directa de hacer todo este engranaje sera utilizar el enlazado opcional, que puede
ser usado en sentencias if y while.
1
2
3
if let numeroNoOpcional=numeroOpcional {
print("numero contiene un entero: \(numeroNoOpcional)")
}
Se podra hacer una estructura mucho ms compleja en la cual hubieran diversos enlazados
opcionales seguidos de una sentencia where comprobar una condicion booleana:
1
2
3
Existe la opcin de definir opcionales implicitamente desenvueltos usando para definirlos el simbolo ! en lugar de ?. Estos opcionales se comportarn en todo caso igual que un
opcional definido con el interrogante, con la excepcin de que no necesitaremos utilizar
la exclamacin para extraerlos y usarlos.
Operadores
En Swift los operadores bsicos se utilizan de forma similar a la mayora de lenguajes, de manera
que no nos detendremos mucho en esto y simplemente mostraremos un pequeo cdigo de ejemplo:
1
2
3
4
5
6
7
8
9
10
1 - Variables y tipos
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
Existen unos operadores especiales a los que s que les vamos a dar un poco ms de visibilidad, estos
son los operadores de rango.
Bsicamente existen dos operadores de rango en Swift, el de rango cerrado () y el de rango semi
abierto (..<), veamos un ejemplo de uso.
1 - Variables y tipos
1
2
3
4
5
6
7
8
9
10
11
12
13
//El operador de rango cerrado es util cuando iteramos sobre el mismo rango
for i in 0...5 {
print ("\(i) por 5 \(i*5)")
}
//El operador de rango semi abierto es util cuando iteramos sobre elementos como\
arrays
let sueldos = [100, 200, 300, 400]
let count = sueldos.count
for j in 0..<count {
print ("Sueldo \(j+1) es \(sueldos[j])")
}
Cadenas y carcteres
En Swift las cadenas se representan con el tipo String. Los contenidos de este tipo pueden ser
accedidos de varias maneras, incluido como coleccin de valores de tipo Character.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//Para inicializar una cadena lo hacemos de la forma que ya hemos visto anterior\
mente
let unaCadena = "Esto es una cadena"
//Podemos inicializar una cadena vacia de diferentes maneras
var cadenaVacia = ""
var otraCadenaVacia = String()
//Las cadenas tienen la propiedad isEmpty para comprobar si es una cadena vaca
if (cadenaVacia.isEmpty) {
print ("No hay nada")
}
//Una cadena ser mutable o inmutable (se podr modificar) simplemente mediante \
la definicin como variable o constante
var cadenaMutable = "cadena mutable"
let cadenaInmutable = "cadena inmutable"
Las cadenas en Swift siempre se pasan por valor. Es decir, cuando enviamos una cadena a un mtodo,
el valor de sta se copia al nuevo mtodo para que pueda trabajar con ella sin modificar la variable
externa.
1 - Variables y tipos
En Swift podemos trabajar con los carcteres de una cadena mediante su propiedad characters en
un bucle for-in.
Para concatenar cadenas podemos usar el operador + o +=, pero si queremos concatenar un
Character a un String debemos usar el mtodo append().
1
2
3
4
5
6
1 - Variables y tipos
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
Ejercicio
Crea un nuevo playground en el cual inicialices dos variables con tuplas. Estas variables tendrn
un nombre y una edad. Realiza operaciones con las edades para obtener informacin sobre la edad
media y la suma de las edades.Adems, imprime un mensaje dando la bienvenida a ambos.
2 - Colecciones
En Swift podemos encontrar tres tipos de colecciones bsicas: 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 claves
son nicas pero los valores pueden repetirse.
Las colecciones nicamente pueden contener objetos del mismo tipo de manera que slo podamos
insertar y obtener objetos de ese tipo.
Los arrays, sets y diccionarios de swift estn implementados como colecciones genricas. Veremos ms sobre esto en el capitulo de genricos.
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 coleccin donde los datos no
vayan a cambiar, de esta manera el compilador podr optimizar su trabajo.
Arrays
Para crear un array vaco de un tipo determinado utilizaremos la forma corta que nos provee Swift
[Element]. Existe la posibilidad de la forma larga, que sera Array<Element>, pero usaremos la corta
por seguir una guia de estilo similar a la de Apple.
1
2
3
4
5
6
7
8
9
10
2 - Colecciones
11
12
13
14
15
16
17
18
19
20
21
22
23
10
Adems de las operaciones bsicas los Arrays disponen de mtodos para aadir, reemplazar y
eliminar elementos
Por ejemplo, para insertar un elemento en un indice determinado de un array podemos utilizar
insert(_:atIndex:) Para borrar un elemento podemos usar removeAtIndex(_:), aunque si lo que
queremos 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 adems slo
permiten que cada elemento entre una vez. Es decir, si necesitamos una coleccin en la cual los
elementos no se repitan y adems 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
2
3
4
5
6
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 podemos
ver que estamos definiendo una variable numeros de tipo Set<String>. Es importante notar que al
inicializar la variable directamente con los datos debemos de poner el tipo, ya que el compilador no
sabra distinguir si se trata de un Array o un Set. En cambio, el <String>si que sera opcional, ya
que eso lo puede inferir el compilador de los datos que le pasamos. Es decir, el ejemplo anterior sera
equivalente a:
2 - Colecciones
11
Podemos obtener el nmero de elementos de un Set con la propiedad count Asimismo podemos
tambin saber si un Set es vaco con la propiedas isEmpty
1
2
3
4
5
6
7
8
9
10
Para borrar un elemento de un set podemos usar el mtodo remove(_:), el cual borrar el elemento
y lo devolver si existiera, sino devolver nil. Para comprobar si un elemento existe en el set tenemos
el mtodo contains(_:). swift if let numero = numeros.remove(dos) { print ((numero) exista)
} else { print (No exista) }
if numeros.contains(cinco) { print (Contiene) } else { print (No contiene) }
Existen una serie de operaciones que los sets nos permiten realizar, estas seran:
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 estn en el
segundo set.
Adems, los sets tambin nos permiten realizar comparaciones entre ellos para comprobar si uno
contiene a otro, si son iguales, etc
Usaremos el operador == para comprobar si dos sets contienen todos los mismos valores.
El mtodo isSubsetOf(_:) nos permite comprobar que todos los valores del set estn
contenidos en el set especificado.
El mtodo isSuperSetOf(_:) realizara la operacin contraria a la anterior, es decir, el set de
referencia sera el que contiene al otro.
Los mtodos isStrictSubsetOf(_:) e isStrictSuperSetOf(_:) determinan si un set es
subset o superset de otro, pero no igual a ste.
Finalmente, el mtodo isDisjointWith(_:) determinar si dos sets no tienen valores en
comn.
2 - Colecciones
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
12
Diccionarios
Los diccionarios nos permiten almacenar asociaciones clave-valor. Las claves han de ser del mismo
tipo y los valores del mismo tipo entre ellos tambin. Los valores no tienen un orden especfico
dentro del diccionario, sino que haremos referencia a ellos a travs de sus claves, que funcionarn a
modo de identificador.
Para inicializar un diccionario utilizaremos bien la forma larga: Dictionary<Key, Value> o bien la
forma corta: [Key: Value].
1
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 tiene
datos sobre su tipo ya podemos re inicializarlo mediante [:].
2 - Colecciones
1
2
13
enteros[16] = "dieciseis"
enteros = [:]
Como podemos comprobar, no ha hecho falta definirle el tipo que tienen la clave y el valor, ya que
en este caso s que es posible inferirlo directamente al tener todas las claves el mismo tipo, as como
todos los valores.
Igual que en los Arrays y los Sets podemos comprobar el nmero de elementos de un diccionario,
as como si est vaco a travs de la propiedad count y el mtodo isEmpty
Podemos usar la sintaxis de subscript para aadir o acceder a los elementos de un diccionario Adems
de obtener el valor, con subscripts podemos tambin eliminar un valor concreto asignando nil a la
clave correspondiente.
1
2
3
4
Como se puede comprobar, en el print utilizamos la ! para mostrar el String del valor
del diccionario. Esto es porque los Diccionarios pueden devolver valores vacos, con lo
que todas las operaciones que nos devuelvan algn valor siempre lo van a encapsular
en 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?
3 - Control de flujo
Swift nos provee con diversos mecanismos para controlar el flujo de nuestra aplicacin. Disponemos
de elementos bsicos, 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 una
basandonos en ciertas condiciones.
Adems del bucle for tradicional, Swift tambin dispone del bucle for-in, que nos facilita el trabajo
de recorrer arrays, sets, diccionarios y otras secuencias.
Bucles For
For
No hay mucho que explicar de los buenos bucles for. Bsicamente disponen de una condicin y un
incrementador, que harn que la variable que creemos vaya modificandose segn el incrementador
mientras se cumpla la condicin.
1
2
3
Las constantes y variables creadas en la inicializacin del bucle for slo sern vlidas en el contexto
de este. Si quisieramos obtener el valor final de indice despus de acabar el bucle deberamos
inicializarlo antes.
1
2
3
4
5
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
3 - Control de flujo
1
2
3
15
El funcionamiento es sencillo, el bucle coger el valor de cada uno de los elementos en la secuencia
seleccionada 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 cmo
sera en un array y un set:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
//un array
var array = ["uno", "dos", "tres"]
for numero in array {
print ("El numero es: \(numero)")
}
//un set
var set: Set = ["uno", "dos", "tres"]
for numero in set {
print ("El numero es: \(numero)")
}
Como hemos podido comprobar, en ambos casos el uso sera el mismo, con la diferencia de que el
array nos devolvera 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)) }
Bsicamente aqu hemos de usar una tupla para poder obtener la clave y el valor. En este caso
concreto, que slo necesitabamos el valor podamos haber puesto la tupla como (_,valor).
Bucles While
Los bucles while tienen la base en la repeticin de las acciones definidas mientras se cumpla una
condicin concreta.
En este caso tenemos dos tipos de bucles while, el bucle while bsico, que evaluar la condicin antes
de ejecutar las acciones y el bucle repeat-while, que evaluar la condicin despus de ejecutar las
acciones.
Veamos el ejemplo ms sencillo de estos bucles y donde est la diferencia:
3 - Control de flujo
1
2
3
4
5
6
7
8
9
10
11
12
13
14
16
var contador = 0
while (contador < 10) {
++contador
print("Contador es: \(contador)")
}
contador = 0
repeat {
++contador
print("Contador es: \(contador)")
} while (contador < 10)
Como hemos comprobado, en ambos casos el resultado es el mismo pero Qu pasara que el valor
del contador empezara en 10?
Instrucciones condicionales
If-else
Una sentencia if nos permite controlar la entrada o no del flujo de la aplicacin a una determinada
zona de instrucciones. Para eso comprobaremos la validez de una sentencia booleana.
1
2
3
4
5
Se pueden encadenar multiples sentencias if mediante la instruccin else if y podemos cerrar con
una sentencia else.
3 - Control de flujo
1
2
3
4
5
6
7
8
9
17
var opcion="tres"
if opcion=="uno" {
print ("La opcin es uno")
} else if opcion=="dos" {
print ("La opcin es dos")
} else {
print ("La opcin no es ni uno ni dos")
}
Bsicamente se podra traducir if por si y else por sino. Con lo que nos encontraramos con un
flujo que dira algo as como Si la opcin es uno entonces imprime La opcin es uno, sino pasa
entonces, si la opcin es dos, imprime La opcin es dos, sino pasa entonces imprime La opcin no
es ni uno ni dos.
Switch
La sentencia switch en general es una forma abreviada de escribir una serie de sentencias if-else
sobre la misma variable. Para ver un caso sencillo, pongamos el ejemplo de las opciones anteriores:
1
2
3
4
5
6
7
8
9
10
var opcion="tres"
switch opcion {
case "uno":
print ("La opcin es uno")
case "dos":
print ("La opcin es dos")
default:
print ("La opcin no es ni uno ni dos")
}
Este es un ejemplo muy sencillo, pero ya empezamos a ver la potencia de la sentencia switch en
Swift, ya que la hemos usado directamente con cadenas en lugar de con enteros como permiten
otros lenguajes como PHP o Java.
Otra opcin muy potente que tienen las sentencias switch en Swift es que permiten poner diversas
opciones en el mismo bloque, por ejemplo
18
3 - Control de flujo
1
2
3
4
5
6
7
8
Hay que tener en cuenta que en Swift es obligatorio que todos los bloques en una sentencia switch
tengan datos, ya que no existe el concepto de fallthrough en el cual si un bloque no acaba en break
se salta al siguiente.
No solo podemos usar la sentencia switch con Strings o enteros, sino que podemos usar por ejemplo
tuplas o rangos:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
let contador = 10
switch contador {
case 1...5:
print ("De 1 a 5")
case 6...15:
print ("De 6 a 15")
default:
print ("Mayor de 15 o menor de 1")
}
3 - Control de flujo
19
4 - Closures
Los closures son bloques de funcionalidad autocontenidos que pueden ser pasados de un lado a otro
y usados en el cdigo. Son un concepto similar a los bloques de Objective-C o lambdas en otros
lenguajes de programaicn.
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 sintcticas de manera que
podemos escribir closures de una forma ms abreviada sin perder claridad o intencin. En las
siguientes lineas veremos un ejemplo de estas optimizaciones realizadas para ir refinando un ejemplo
del mtodo sort(_:).
La librera estandar de swift provee un metodo llamado sort(_:), que ordena un array de valores de
un tipo determinado basandose en la salida de un closure de ordenacin que se le provee. Pongamos
un ejemplo del array a ordenar:
1
El mtodo sort(_:) recibe un closure que admite dos argumentos del mismo tipo que el contenido
del array y devuelve un Boolpara decir si el primer valor debera aparecer antes o despus del
segundo valor.
En este ejemplo estamos ordenando un array de String, con lo que el closure debe de ser una funcin
del tipo (String, String)->Bool.
Una manera de proveer del closure es escribir una funcin del tipo correspondiente y pasarla como
argumento al mtodo sort(_:).
1
2
3
4
Esta es una forma relativamente larga para escribir una expresin tan sencilla como (a>b), de manera
que vamos a empezar escribiendo el closure en linea usando la sintaxis de las expresiones de closure.
20
4 - Closures
1
2
21
El closure que estamos utilizando se est pasando a un mtodo. Por esto, el compilador puede saber
perfectamente cuales son los parmetros y el tipo de retorno que necesitar tener el closure. Es decir,
gracias a la inferencia de tipos podemos ahorrarnos todo ese cdigo, dejando algo como lo que sigue:
1
Siempre que usemos un closure como parmetro en linea para una funcin podemos omitir la
definicin de tipos, ya que se inferir de los tipos que recibe la funcin en cuestin.
Retornos implicitos en closures de una sola expresin.
Si nuestra closure tiene una sola instruccin podemos omitir tambin la palabra return.
1
Swift nos provee de abreviaturas para los nombres de argumento de las closures inline, de manera
que podamos omitir tambin la definicin explcita de stos si fuera necesario. Los nombres que da
son $0, $1, $2, etc siguiendo el orden de la definicin.
En nuestro caso podemos poner:
1
Funciones de operador
No se dar siempre, pero en el caso concreto de nuestro ejemplo la funcin mayor-que (>) de String
est definida como una funcin que recibe dos String y devuelve un Bool, exactamente como el
closure que necesitamos para la funcin sort(_:). Es decir, en ltima instancia podramos quedarnos
con:
4 - Closures
22
Closures a posteriori
Swift permite escribir despus del parntesis los closures en linea siempre que ste sea el ltimo
parmetro que reciba una funcin. Por tanto, el ejemplo anterior se podra haber escrito como:
1
Captura de valores
Los closures permiten capturar constantes y variables del contexto en el que son definidos. Adems,
si asignamos un closure a una constante o una variable, este closure ser capaz de modificar los
datos de las variables internas que tenga. Veamos un ejemplo:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
4 - Closures
23
Hemos creado una funcin suma, que devuelve una funcin de tipo ()->Int. Dentro de esta funcin
suma hemos definido una variable total y una funcin anidada (la forma ms sencilla de closure)
sumador. La funcin anidada es capaz de acceder al valor de total ya que los closures pueden acceder
a 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 misma
funcin suma devuelven valores independientes. Esto ocurre porque cada una de las constantes tiene
su propio contexto y por tanto los valores avanzan de forma independiente.
Adems, hay que recordar que los closures son tipos por referencia, esto quiere decir que si
asignamos el mismo closure a otra variable o constante, este ser exactamente el mismo y tendr
acceso al mismo contexto.
1
2
3
5 - Clases y estructuras
Clases y estructuras
Las clases y estructuras son la base de las aplicaciones en Swift. Permiten definir propiedades y
mtodos para aadirles funcionalidad.
Swift no requiere interface e implementacin separados para clases y estructuras.
Herencia
Casting de tipos
Desinicializadores para liberar los recursos
El conteo de referencias permite ms de una referencia a las instancias de clase.
Definicin
Las clases y las estructuras se definen de forma similar. Las clases con la palabra reservada class y
las estructuras con struct.
24
5 - Clases y estructuras
1
2
3
4
5
6
7
8
9
10
11
12
13
14
25
struct Localizacion {
var latitud = 0
var longitud = 0
}
class Direccion {
var localizacion = Localizacion()
var calle: String?
var ciudad: String?
var numero: Int?
}
let localizacion = Localizacion()
let direccion = Direccion()
En el ejemplo anterior hemos creado una estructura Localizacion y una clase Direccion. Esta
ltima tiene una variable de tipo Localizacion. Adems, hemos inicializado una instancia de cada
una de ellas para poder usarlas.
Inicializador por defecto
Todas las estructuras tienen un inicializador por defecto con sus variables, de manera que se
puede instanciar de la siguiente manera swift let localizacion = Localizacion(latitud: 0
longitud: 0)
Esto quiere decir que cuando asignamos una instancia de una estructura a otra variable o la usamos
para llamar a un mtodo, 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
Bsicamente lo contrario que con las estructuras. Si pasamos una instancia de una clase a un mtodo,
esta clase no se copia, sino que se enva una refrencia a la instancia. De esta manera, si se cambia la
clase dentro del mtodo se cambiar tambin fuera.
Operadores de identidad
Como las clases son tipos por referencia es posible que multiples constantes y variables hagan
referencia a la misma instancia de una clase. En ocasiones puede ser util comprobar si dos constantes
5 - Clases y estructuras
26
o variables hacen referencia a la misma instancia de una clase, para eso usamos los operadores de
identidad:
Identico a (===)
No identico a (!==)
Propiedades y mtodos
Las propiedades y los mtodos son lo que proveen a las clases y estructuras de funcionalidad real.
Las propiedades nos ofrecen potencial para guardar valores y los mtodos nos dan la posibilidad de
realizar clculos y procesos.
Propiedades almacenadas
Las propiedades almacenadas son la forma ms sencilla de propiedades. Son las constantes o
variables 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 y
una constante que no podr ser modificada:
1
2
3
4
5
struct Fuente {
let nombre: String
var tamao: Int
var peso: Int
}
Propiedades calculadas
Las propiedades calculadas no guardan un valor, sino que lo calculan en base a otros datos y lo sirven
en el momento que se consultan. Proveen de un getter y un setter opcional para obtener y guardar
otras propiedades indirectamente.
5 - Clases y estructuras
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
27
struct Punto {
var x = 0,
var y = 0
}
struct Rectangulo {
var xMin = 0.0, yMin = 0.0, xMax = 0.0, yMax = 0.0
var centro : Punto {
get {
let centroX = xMin+(xMax-xMin)/2
let centroY = yMin+(yMax-yMin)/2
return Punto(centroX, centroY)
}
}
}
var cuadrado = Rectangulo()
cuadrado.xMin = 0
cuadrado.yMin = 0
cuadrado.xMax = 10
cuadrado.yMax = 10
let centro = cuadrado.centro
//Punto(5,5)
Si una propiedad calculada slo tiene getter es posible eliminar el bloque get, con lo que quedara
algo como lo que sigue:
1
2
3
4
5
6
7
8
9
struct Rectangulo {
var xMin = 0.0, yMin = 0.0, xMax = 0.0, yMax = 0.0
var centro : Punto {
let centroX = xMin+(xMax-xMin)/2
let centroY = yMin+(yMax-yMin)/2
return Punto(centroX, centroY)
}
}
Mtodos
Todos los mtodos tienen un nombre por el cual se les hace referencia. Adems del nombre se les
puede definir uno o ms valores con sus tipos, que los mtodos recogern como entrada. Tambin
5 - Clases y estructuras
28
es posible definir el tipo del valor de retorno que la funcin devolver al acabar.
Por ejemplo, un mtodo que recibira como parmetro dos String y devolvera otro String
1
2
3
Si no hubieran parmetros o tipo de retorno simplemente no se pondran (en el caso del tipo de
retorno no se pondra ni la ->).
En los parmetros se puede especificar el nombre externo de stos si se pone antes del nombre
interno, tambin se puede omitir si se pone _
1
2
3
4
5
6
7
8
9
10
func diHola(quien nombre1: String = "Pedro", aQuien nombre2: String) -> String {
return nombre1 + " dice hola a " + nombre2
}
diHola(aQuien: "Pepito")
diHola(quien: "Juanito")
En los mtodos es Swift tambin es posible especificar que un mtodo recibir uno o ms valores de
un determinado tipo, as como permitir la mutabilidad de los parmetros que enviamos mediante su
definicin como constantes o variables.
5 - Clases y estructuras
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
29
Si quisieramos que un mtodo pudiera cambiar una variable externa deberamos pasar esa variable
como inout, adems, tendremos que llamar a la funcin enviando el parmetro por referencia
mediante el operador &
1
2
3
4
5
6
7
Tipos
Las funciones tienen un tipo especfico que est formado por los parmetros que se le pasan y por
el tipo de retorno. Por ejemplo, la funcin del ejemplo anterior tendra un tipo (Int, Int) -> Int
Otro ejemplo sera la funcin: swift func holaMundo() { print ("hola mundo") } que tendra
un tipo ()->Void, es decir, una funcin sin parmetros y devuelve Void.
Los tipos de las funciones se pueden usar como parmetro para otras funciones, as como para definir
variables
1
Veamos un ejemplo de paso de funciones como parmetro usando la funcin contador que definimos
anteriormente
5 - Clases y estructuras
1
2
3
4
5
6
7
8
9
10
func bucleContador(starting: Int=0, step: Int=1, maximo: Int, funcion: (inout In\
t, Int)->Void)->Int {
var sumatorio = starting
while sumatorio<maximo {
funcion(&sumatorio, step)
}
return sumatorio
}
bucleContador(maximo: 10, funcion: contador)
30
6 - Subscripts
En Swift, clases, estructuras y enumeraciones pueden definir subscripts, que son accesos directos a
miembros dentro de colecciones.
Usamos subscripts por ejemplo para acceder a los elementos de un Array
1
2
Un tipo puede tener mltiples subscripts y adems estos no estn limitads a una nica dimensin.
Definicin de subscripts
La forma de definir un subscript es similar a como se definen los atributos, solo que en este caso
es necesario especificar el gettery setterde dicho subscript. En el ejemplo inferior definimos un
subscript que toma un parmetro Inty define como acceder al index del elemento.
1
2
3
4
5
6
7
8
31
6 - Subscripts
32
Opciones de Subscripts
Aunque en los ejemplos anteriores hemos visto como podemos usar subscripts con un nico
parmetro, se pueden pasar tambin mltiples parmetros. A la hora de usarlos es el propio
compilador el que inferir el tipo de subscript a usar. Swift define el uso de mltiples scripts como
subscript overloading.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
struct Matrix {
let filas: Int, columnas: Int
var print: [Double]
init(filas: Int, columnas: Int) {
self.filas = filas
self.columnas = columnas
print = Array(count: filas * columnas, repeatedValue: 0.0)
}
// Definicin de subscript
subscript(celda: Int, columna: Int) -> Double {
get {
return print[(celda * columnas) + columna]
}
set {
print[(celda * columnas) + columna] = newValue
}
}
}
// Inicializamos la matriz
var mat = Matrix(filas: 3, columnas: 3)
// Accedemos a los setters de los subscript
mat[0,0] = 1.0
mat[0,1] = 2.0
mat[1,0] = 3.0
mat[1,1] = 5.0
// Obtenemos los valores usando subscript
println("\(mat[0,0])")
println("\(mat[0,1])")
println("\(mat[1,0])")
println("\(mat[1,1])")
6 - Subscripts
33
Resumen
Podemos crear subscripts en clases, estructuras y enumeraciones que representen colecciones
en varias dimensiones.
Los subscripts son accedidos mediante corchetes: [2, 3]
Los subscripts pueden tener varias dimensiones.
Tambin puede ser de cualquier tipo, y no unicamente Int.
Ejercicio
Vamos a poner en prctica los subscripts y lo vamos a hacer con un ejemplo real con el cual
encontramos los desarrolladores de apps a diario, el acceso a APIs paginadas. Cuando las APIs
HTTP tienen que retornar grandes colecciones de datos, estas lo hacen devolviendo los resultados en
mltiples peticiones. El resultado desde el punto de vista del programador que consume esos datos
son varias colecciones que tenemos que presentar como una nica coleccin en la interfaz de nuestra
aplicacin. Cmo?, modelaremos el resultado de todas las pginas en un nuevo tipo.
Tendremos un nuevo tipo, PaginatedResponse con las siguientes condiciones:
Permitir aadir una nueva pgina
En cualquier momento podremos acceder a la ltima pgina
Mediante subscript podremos acceder a una pgina en concreto. Si no existe devolveremos un
Opcional
Tambin podremos acceder a la coleccin completa concatenando el resultado de cada pgina.
Implementa el tipo de acuerdo a las especificaciones anteriores.
7 - Herencia
Una clase puede heredar mtodos, propiedades y otras funcionalidades de una clase. En programacin orientada a objetos esto se conoce como herencia de clases.
Las subclases pueden acceder a los mtodos, propiedades y subscripts de la super clase siempre y
cucando estas sean internal o public . El compilador comprobar si a la hora de sobreescribir los
elementos de la clase padre estos se corresponden con los mismos tipos de su padre.
class Animal {
var nombre: String = ""
var patas: Int = 0
var feliz: Bool = true
func camina() {
print("Yo camino")
}
}
Recuerda, la forma de crear una instancia de esa clase es usando su constructor. En este caso el
constructor por defecto no toma ningn parmetro:
1
Una vez tenemos la instancia de animal creada podemos acceder a cualquier propiedad de dicho
animal:
1
7 - Herencia
35
Nuestra nueva clase ha heredado tanto los mtodos 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
2
Sobreescritura (Overriding)
Cualquier subclase puede sobreescribir una propiedad, mtodo o subscript de la clase padre de la
que hereda. Este concepto se conoce como overriding.
Para indicar que una variable, mtodo o subscript va a ser sobreescrito en lugar de utilizar la
definicin de la clase padre se utiliza el keyword override. En el caso de no usarlo, el compilador
de Swift alertar de que la clase padre ya tiene una definicin y se est intentando sobreescribir sin
hacerlo de forma explcita.
Swift es un lenguaje de programacin que fuerza al desarrollador a ser bastante explicito
a la hora de desarrollar. En este caso en lugar de permitir sobreescribir comportamientos
sin ser consciente de ello, prefiere avisar y forzarte a que lo especifiques. De lo contrario,
el cdigo no compilar.
7 - Herencia
36
Sobreescribiendo mtodos
Retomando los ejemplos anteriores, podemos sobreescribir el mtodo de caminar para Perro siendo
ms explcitos en la accin del mismo:
1
2
3
4
5
6
7
Si creas una instancia de dog, y llamas al mtodo camina() ejecutar el mtodo sobreescrito.
1
2
Sobreescribiendo atributos
Puedes sobreescribir una atributo de una clase para redefinir el getter/setter de la misma o incluso
aadir 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 es
almacenada o computada. El carcter computado o almacenado de la variable no es conocido por la
subclase 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 redefinicin en
la subclase. Lo que no puedes hacer, sin embargo, es hacer readonly un atributo que est definido
como read-write en la clase padre.
7 - Herencia
1
2
3
4
5
6
7
37
super.nombre
class Perro {
override var feliz: Bool {
didSet {
if !feliz {
self.jugar()
}
}
func jugar() {
print("PJugando con \(self.nombre)")
}
}
Prevenir overrides
Podemos prevenir cualquier tipo de override usando el keyword final aplicado sobre classes,
mtodos, variables, y subscripts:
7 - Herencia
1
2
3
4
final
final
final
final
38
class MiClase {}
func miMetodoFinal() {}
var miVariableFinal: Bool = false
subscript(index: Int) {}
Si intentara sobreescribir cualquiera de esos elementos marcados con final el compilador lanzara
un error y el cdigo no se compilara.
Resumen
Una clase base es aquella que no hereda de ninguna otra.
Cuando heredamos de una clase existente, heredamos de esta sus atributos, mtodos, constructores y subscripts.
A la hora de heredar, podemos sobreescribir elementos de la clase padre tales como mtodos,
subscripts, atributos, y observers de los atributos.
La sobreescritura puede ser evitada si lo especificamos con final.
8 - Inicializacin y deinicializacin
Inicializacin
El proceso de inicializacin consiste en preparar una instancia de una clase, estructura, o enum
especificando el valor de aquellas variables que lo necesiten. Tambin se puede aprovechar este
mtodo para especificar el estado de la instancia.
La forma de especifica la forma de inicializar la clase es mediante la funcin init() que toma tantos
argumentos como sean necesarios para mover la instancia a un estado inicial. Por ejemplo, en el caso
del struct inferior, mediante el init podemos pasar el nombre del perro cuando se crea una instancia
de perro:
1
2
3
4
5
6
7
8
9
10
11
struct Perro {
let nombre: String
init(nombre: String) {
self.nombre = nombre
}
}
let bobby: Perro = Perro(nombre: "Bobby")
39
8 - Inicializacin y deinicializacin
1
2
3
4
5
6
7
8
9
10
40
struct Hijo {
let edad: Int
init(edad: Int = 0) {
self.edad = edad
}
}
let nuevoHijo: Hijo = Hijo()
En el ejemplo anterior, al no pasar el valor de edad, por defecto nuevoHijo tendr la edad de 0.
Parmetros externos e internos
A la hora de definir el constructor, Swift permite definir un nombre, externo y otro interno para
los parmetros del constructor. La razn es debido a que a la hora de inicializar una instancia otro
nombre puede hacer el cdigo de inicializacin ms fcil de leer. Por ejemplo:
1
2
3
4
5
6
7
8
struct Ordenador {
let compania: String
init(de compania: String) {
self.compania = compania
}
}
let iMac: Ordenador = Ordenador(de: "Apple")
Es ms difcil saber si la compaa que estamos pasando es la que hizo el envo del ordenador a tu
casa, la compaa que se encarg de fabricar el hardware,
Swift promueve la escritura de cdigo bastante expresivo, de ah que existan funcionalidades como la de utilizar nombres externos e internos para los parmetros. Haz uso de
estas funcionalidades y evita ambigedades en cdigo.
8 - Inicializacin y deinicializacin
41
Puedes encontrar el caso en el que a la hora de crear una instancia no necesites pasar ningn nombre
a los parmetros ya que el nombre de la clase/estructura/enum es suficientemente explcito como
para saber a qu haces referencia con el parmetro. En esos casos podemos decir a Swift que ignore
los nombres externos y que en el constructor no necesites especificarlos. Por ejemplo:
1
2
3
4
5
6
7
8
9
class Altura {
let total: Float
init(_ total: Float) {
self.total = total
}
}
let miAltura: Altura = Altura(180)
Atributos opcionales
Cuando nuestra instancia a inicializar tiene atributos que son opcionales estos atributos pueden
no tener valor. Esto implica que a la hora de crear definir la estructura de nuestro constructor, no es
necesario inicializar esas variables ya que de no hacerlo, el compilador entendera que los valores
para esos atributos, inicialmente son nil:
1
2
3
4
5
6
struct Perfil {
var trabajo: Trabajo?
}
let pedro: Perfil = Perfil()
Atributos constantes
Nuetra clase/estructura puede tener atributos que son constantes. Swift te forzar a inicializarlos
en los constructores definidos. De lo contrario el cdigo no compilar. Una vez estos atributos son
inicializados, por el hecho de ser constantes, no podrn ser modificados ms adelante:
8 - Inicializacin y deinicializacin
1
2
3
4
5
6
7
8
9
10
11
12
13
42
struct Cancion {
let nombre: String
let artista: Artista
init(nombre: String, artista: Artista) {
self.nombre = nombre
self.artista = artista
}
init() {
// El compilador se quejar ya que no inicializamos nombre & artista
}
}
class Lista {
var canciones: [Cancion] = []
func anyadir(cancion cancion: Cancion) {
self.canciones.append(cancion)
}
}
let salsa: Lista = Lista()
En el caso de estructuras el propio Swift define un constructor por defecto tomando como parmetros
todas las variables de la estructura:
1
2
3
4
5
struct Cancion {
let nombre: String
let anyo: Int
}
let miCancionFavorita: Cancion = Cancion(nombre: "Bailando", anyo: 2015)
8 - Inicializacin y deinicializacin
43
struct Rectangulo {
let ancho: Float = 0.0
let alto: Float = 0.0
let color: UIColor
init(ancho: Float, alto: Float, color: UIColor) {
self.ancho = ancho
self.alto = alto
self.color = color
}
init(ancho: Float, alto: Float) {
self.init(ancho: ancho, alto: alto, color: UIColor.whiteColor())
}
}
Herencia e inicializacin
Todos los atributos de una clase incluyendo todas ellas que una clase hereda de la super clase, deben
ser inicializados durante el proceso de inicializacin. Swift ofrece dos tipos de constructors para
clases conocidos como:
Designed initializers
Convenience initizers.
8 - Inicializacin y deinicializacin
44
Designed Initializers Inicializa todos los atributos introducidos por una clase y llama al init de super
para continuar con la inicializacin. Conceptualmente estos inicializers propagan la inicializacin
hacia las clases padres, siendo responsables nicamente de sus atributos nicamente.
Convenience Initializers Estos initializers llaman a un designed initializer de la propia clase para
inicializarse. Suelen ser utilizados para crear instancias con una configuracin determinada (como
una factory de modelos hara). Podramos decir que se trata del equivalente a delegar la inicializacin
(para estructuras) pero en el caso de clases.
1
2
3
4
5
6
7
Herencia de constructores
En comparacin con Objective-C, las subclases de Swift no heredan los constructores de sus clases
padres. Si necesitas que una subclase provea de uno o ms constructores de la clase padre, puedes
ofrecer una implementacin personalizada de esos constructores en la subclase.
Cuando escribes un constructor que coincide con la sintxis del constructor de la padre se trata de un
designated initializer, o constructor designado. Debes indicarlo con el modificador override antes
de la definicin del constructor.
De la misma forma en la que sobreescribes atributos, mtodos y subscripts, la presencia del
modificador 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.
8 - Inicializacin y deinicializacin
45
Si en lugar de sobreescribir un constructor designated lo hacemos de uno de conveniencia no es necesario especificar que estamos sobreescribiendo un constructor con
override.
Constructores Failable
En determinadas ocasiones puede interesar que el constructor falle en la inicializacin. Esto puede
suceder por ejemplo, cuando los parmetros de inicializacin son invlidos, o alguna otra condicin
que impide que la inicializacin sea satisfactoria.
Para hacer frente a eso, Swift ofrece un tipo de constructores conocidos como failable. Estos pueden
ser 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 tipo
de parmetros y nombres.
Para notificar sobre una inicializacin fallida, simplemente retorna nil durante la inicializacin:
8 - Inicializacin y deinicializacin
1
2
3
4
5
6
7
46
struct Animal {
let especie: String
init?(especie: String) {
if especie.isEmpty { return nil }
self.especies = especies
}
}
Tambin puede utilizarse con enums, por ejemplo si quisiramos inicializar el enum con otro tipo
distinto al propio enum:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
enum UnidadTemperatura {
case Kelvin, Celsius, Fahrenheit
init?(simbolo: Character) {
switch simbolo {
case "K":
self = .Kelvin
case "C":
self = .Celsius
case "F":
self = .Farenheit
default:
return nil
}
}
}
Constructores requeridos
A la hora de definir una clase podemos especificar si un constructor es required. Cuando un
constructor es marcado como required cualquier subclase de estas clase debe implementar dicho
constructor.
8 - Inicializacin y deinicializacin
1
2
3
4
5
47
class MiClase {
required init() {
// Implementacin del constructor
}
}
1
2
3
4
5
class MiSubclase {
required init() {
// Implementacin de la subclase
}
}
Deinicializacin
Swift permite definir deinitializer que son llamados automticamente cuando la instancia de una
clase va a ser deallocada. La forma de escribir un deinitializer es mediante:
1
2
3
4
5
class MiClase {
deinit {
// Ejecutamos lo necesario
}
}
Cmo funciona la deinicializacin? Por defecto cuando un objeto se libera de memoria, Swift
libera todos los recursos asociados a este si no hay otro referencindolos. En algunas situaciones
podemos necesitar ejecutar una limpieza de recursos cuando nuestra instancia es liberada de
memoria, por ejemplo cancelar peticioens activas, o elimiar una entidad que se est encargaa de
abrir un fichero.
Los deinitializers son llamados automticamente, justo antes de que la instancia sea liberad de
memoria. No puedes llamarlos de forma manual. Los deinitializers de la clase padre son heredados
por las clases hijas y la llamada de estos comienza por el ltimo en la jerarqua finalizando por el
deinit de la clase padre.
Debido a que el deinitializer se llama antes de que la clase sea liberada de memoria, todas los
atributos son accesibles.
8 - Inicializacin y deinicializacin
48
Resumen
Inicializar una instancia de una clase consiste en especificar un estado inicial para dicha
instancia (dando valor a sus atributos).
Los constructores pueden tener tanto parmetros internos como externos.
Si la clase o la estructura contiene atributos opcionales estos no requieren valor a la hora de
ser 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 automticamente definir uno seteando los atributos a sus
valores por defecto. En el caso de no tener valor por defecto, forzar a definir un constructor.
Las estructuras permiten delegar la inicializacin
Las clases tienen dos tipos de constructores:
Designed initializers que delegan la inicializacin hacia las clases padres.
Convenience initializers que delegan la inicializacin a un designed initializer.
Los constructores de las clases padres son heredados slo si se cumplen dos condiciones:
La subclase no define un constructor designado.
Si implementa todos los designados de la subpadre, adems hereda los de conveniencia.
Existen constructores de tipo failable (init?) que pueden fallar en la inicializacin y retornan
nil.
Existe la versin implcita del constructor failable, init!.
Los constructores pueden ser requeridos en las subclases con el keyword required.
Swift notifica de la liberacin de memoria de las instancias de clase llamando a un bloque
deinit{} definido en la clase.
Ejercicio
Sin usar Xcode analiza donde encuentra el error en el ejemplo inferior:
1
2
3
4
5
6
7
8
9
10
11
12
class Coche {
let color: String
let marca: String
}
class Mercedes: Coche {
init(color: String) {
self.color = color
self.marca = "Mercedes"
}
8 - Inicializacin y deinicializacin
13
14
49
Cmo funciona
Cada vez que creamos una instancia de una clase, ARC reserva una porcin de memoria para dicha
informacin. Esta porcin mantiene toda la informacin de la instancia, junto con la informacin
del 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 usada
con otros fines. Esto asegura que las instancias de clase no ocupan espacio en memoria cuando ya
no son necesarias nunca ms.
Si ARC deallocara (as es como se define la liberacin de memoria) una instancia e intentramos
a acceder a sus atributos, o llamar a sus mtodos de instancia, nuestra aplicacin probablemente
abortara la ejecucin lanzando un error de runtime.
Para saber si una instancia est todava siendo usada, ARC lleva un control sobre cuantos atributos,
constantes, y variables estn referenciando a la instancia de la clase. ARC no deallocar una istancia
si esta tiene una referencia activa que todava sigue en memoria.
Para hacer esto posibe, siempre que asignes una clase de instancia a un atributo, constante, o
variable, ese atributo, constante o variable tiene una referencia strong a la instancia. La referencia
se denomina referencia strong porque mantiene una relacin fuerte con dicha instancia, y esto
impide que sea liberada.
50
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
51
class Cancion {
let nombre: String
init(nombre: String) {
self.nombre = nombre
print("\(self.nombre) se est inicializando")
}
deinit {
print("\(self.nombre) se va a deinicializar")
}
}
var referencia1: Cancion?
var referencia2: Cancion?
var referencia3: Cancion?
referencia1 = Cancion(nombre: "Lean on")
referencia2 = referencia1
referencia3 = referencia1
En el ejemplo anterior creamos una instancia de Cancion que es referenciada por la variable
referencia1. Esto implica que nuestra instia en memoria tiene una cuenta de referencia igual a
1. Cuando apuntamos referencia2 y referencia3 a la misma instancia, la cuenta incrementa a
3 de forma que para ser liberada de memoria, se tendra que desconectar estas 3 referencias de la
instancia:
1
2
3
4
referencia1 = nil
referencia2 = nil
referencia3 = nil
// Imprime "Lean on se va a deinicializar"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
52
class Album {
let nombre: String
var canciones: [Cancion] = []
init(nombre: String, canciones: [Cancion] = []) {
self.nombre = nombre
self.canciones = canciones
}
deinit {
print("Liberando album \(self.nombre) de memoria")
}
}
class Cancion {
let nombre: String
let album: Album
init(nombre: String, album: Album) {
self.nombre = nombre
self.album = album
self.album.canciones.append(self)
}
deinit {
print("Liberando cancion \(self.nombre) de memoria")
}
}
var album: Album?
var cancion: Cancion?
album =
cancion
album =
cancion
En el ejemplo anterior, cuando creamos la instancia de una cancin pasando un album aadimos la
cancin se aade 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 quisiramos liberar
el album de memoria no podramos ya que todas las tracks tienen una referencia a l.
53
class Casa {
let direccion: String
weak var duenyo: Persona?
init(direccion: String) {
self.direccion = direccion
}
deinit {
print("La casa \(self.direccion) est siendo deinicializada")
}
}
class Persona {
let nombre: String
var casa: Casa?
init(nombre: String) {
self.nombre = nombre
}
deinit {
print("\(nombre) est siendo deinicializado")
}
}
var pedro: Persona?
var casa: Casa?
pedro = Persona(nombre: "Pedro")
casa = Casa(direccion: "Gran Via")
pedro!.casa = casa
casa!.duenyo = pedro
54
Referencias weak
Una referencia de tipo weak no mantiene la el elemento refereico en memoria, simplemente apunta
a l, no bloqueando a ARC de su liberacin. Este tipo de referencias ayuda a evitar los ciclos de
referencia strong.
La forma de especificar que una referencia es de tipo weak es especificndolo al comienzo de la
definicin de la variable:
1
Cuando se usan las referencias de tipo weak la variable tiene que forzadamente ser un opcional
debido 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 se
explica en la siguiente seccin.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Persona {
let nombre: String
init(nombre: String) {
self.nombre = nombre
}
var casa: Casa?
deinit {
print("Liberando \(self.nombre) de memoria")
}
}
class Casa {
let direccion: String
init(direccion: String) {
self.direccion = direccion
}
weak var duenyo: Persona?
deinit {
print("Liberando la casa \(self.direccion) de memoria")
}
}
var pedro: Persona?
var casa: Casa?
pedro = Persona(nombre: "Pedro")
casa = Casa(direccion: "Gran Via")
28
29
30
31
32
55
pedro!.casa = casa
casa!.duenyo = pedro
pedro = nil // Imprime Liberando Pedro de memoria
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 de
retain. Si por alguna razn el dueo de la casa es liberado de memoria, conceptualmente la casa se
quedar sin dueo y no impedir que la Persona que representa al dueo sea liberada d ememoria.
Referencias unowned
De forma parecida a las weak las referencias de tipo unowned no mantienen un areferencia fuerte
a os elementos que refierenn. A diferencia de weak, estas asumen que la variable siempre tendr
valor. Por esa razn, el tipo de las referencias unowned ser siempre de tio no opcional. La forma de
especificar que una referencia es de tipo unowned es mediante el keyword unowned:
1
2
class MiClase {}
unowned var miVariable: MiClase = MiClase()
Debido a que las variable con referencia unowned son no opcionales, no necesitas realizar
el unwrap para acceder a su valor. ARC no puede cambiar el valor de la referencia a nil.
Esto implica que si en algn momento la variable referida deja desaparece de memoria
e intentas accceder a ella, la ejecucin lanzar un error de runtime. Usa referencias
de tipo unowned slo cuando estees 100% seguro de que la variable no va a ser
liberada de memoria.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
56
class Issue {
var nombre: String = ""
init(nombre: String) {
self.nombre = nombre
}
lazy var nombreCool: () -> String = { () -> String in
return "Cool \(self.nombre)"
}
deinit {
print("Liberando issue \(self.nombre)")
}
}
var issue: Issue?
issue = Issue(nombre: "Pepito")
print(issue?.nombreCool())
issue = nil // No se llama a deinit
Donde nombreCool es un closure e internamente est haciendo referencia a self y por lo tanto
creando un ciclo de referencia strong. La definicin del atributo del closure es de tipo lazy ya que
se inicializa slo cuando alguien lo llama. Es por ello que a menos que alguien llame a la funcin, el
ciclo de strong reference no ser creado.
lazy var miClosure: (Int, String) -> String = { [unowned self, weak delegate = s\
elf.delegate!] (indice: Int, cadena: String) -> String in
// Cuerpo del closure
}
Nota como a la hora de definir el capture list podemos asignar un nuevo nombre a la
variable dentro del closure.
57
Unowned: Captura las variables fuera del contexto del closure usando unowned cuando tanto
como el closure como las variables referenciados sean liberados de memoria al mismo tiempo.
Recuerda que usbamos unowned cuando estbamos seguro de que las variables unowned
siempre iban a tener valor. De lo contrario si intentamos acceder a ellas en tiempo de ejecucin,
nuestra aplicacin lanzar un error.
Weak: Si las variables referenciadas dentro del closure pueden pasar a tener valor nil en
cualquier momento, independientemente del ciclo de vida del propio closure, estas deben ser
referenciadas con weak, lo que quiere decir que dentro del closure, dichas variables pasarn a
ser opcionales.
Resumen
Podemos especificar el nivel de referencia de elementos de referencia con weak y unowned.
Si no lo especificamos por defecto las referencias sern de tipo strong.
Cuando tipos de referencia tienen referencias entre ellos se crean retain cycles que hay que
controlar.
Lo mismo sucede entre clases y closures ya que estos ltimos tambin son un tipo de referencia.
10 - Encadenado de opcionales
El proceso de llamar a properties, subscripts y mtodos en un opcional que pueda ser nil se define
como encadenado de opcionales. Un encadenado de opcionales puede retornar dos valores:
Si el opcional contiene un valor entonces las properties, mtodos y subscripts encadenadas
retornan valor.
Si el opcional no contiene valor entonces las properties, mtodos y subscripts retornan nil.
Ya que podemos encadenar varios mtodos, properties y subscripts, el efecto del encadenado es
vlido para toda la cadena.
class Elecciones {
var candidato: Candidato?
}
class Candidato {
var nombre ="MP"
}
let elecciones = Elecciones()
let nombreCandidato = elecciones.candidato!.nombre
10 - Encadenado de opcionales
1
2
3
4
5
6
7
59
Al ejecutar el cdigo anterior obtendremos por consola la segunda sentencia del else ya que nuestra
instancia no tiene nombre.
Con mtodos
De forma similar a como lo hacemos con properties, tambin podemos usar el encadenado de
opcionales en mtodos. Basta con usar ? antes de llamar al mtodo:
1
2
3
4
5
6
7
8
9
10
11
12
class Arbol {
var naranjas: [Naranja] = []
}
class Naranja {
func exprimir() {
print("Exprimiendo naranja")
}
}
let arbol: Arbol = Arbol()
arbol.naranjas.first?.exprimir()
En el ejemplo anterior, el mtodo first retorna un opcional porque podemos no tener naranjas en el
arbol. Al quere exprimiarla tenemos que usar el encadenado de opcionales para llamar al mtodo.
Al ser nil la ejecucin no imprimir nada por consola.
Con subscripts
El encadenado de opcionales tambin aplica a subscripts a la hora de acceder a un index determinado
de un elemento que pueda ser nil:
1
2
Al ser array un opcional, si queremos acceder a algn elemento en un ndice determinado tenemos
que hacerlo con ?.
10 - Encadenado de opcionales
60
1
2
3
4
5
6
Resumen
El acceso a opcionales a travs de distintos niveles se conoce como encadenado de opcionales.
Al usarlo para atributos si alguno de los opcionales retorna nil, la cadena retornar nil para
dicho atributo. Por lo tanto este debe ser un opcional.
En el caso de un mtodo, el mtodo no se acabar llamando.
El encadenado de opcionales tambin es vlido con subscripts.
11 - Gestin de errores
Swift implementa un mecanismo de gestin de errores similar al de otros lenguajes de programacin
con algunas sutiles diferencias. Cualquier mtodo en Swift puede ser definido como un mtodo que
lanza errores y se especifica mediante la palabra throws a la hora de definir el mtodo. Por ejemplo,
en el ejemplo inferior el mtodo puede lanzar un error.
1
2
3
Generalmente se usan enums para definir errores puesto que puedes englobar en ellas distintos
subtipos de errores. Por ejemplo en el aso de un error que represente un error de HTTP, el enum que
representa el error podra tener el siguiente formato:
1
2
3
4
5
61
11 - Gestin de errores
1
2
3
62
Swift no permite especificar el tipo de error que se est lanzando. Desde el punto de vista
del consumidor de este mtodo, no sabe que errores internamente el mtodo puede
lanzar y tendr que hacer hacer cast a los distintos tipos de errores y proponer accioens
en funcin del error lanzado por el mtodo
try myMethodThatThrows()
do {
try myMethodThatThrows()
}
catch {
// Oh! algo pas, qu hacemos entonces?
}
11 - Gestin de errores
1
2
3
4
5
6
7
8
9
do
63
{
try methodThatTrows()
}
catch MyErrors.WeirdError {
// Especificamos que hacer en ese caso
}
catch MyErrors.UnexpectedError {
// Especificamos que hacer en ese caso
}
En el ejemplo superior, llamamos al mtodo methodThatThrows() y capturamos los errores .WeirdError y .UnexpectedError que puedan ser lanzados.
No es necesario especificar un catch para todos los posibles tipos de errores existentes. En el
caso de que ninguno de los bloques catch capture el error, este ser propagado hacia el scope que ha
llamado al mtodo, siendo este el responsable de decidir que hacer con l.
Ignorando errores
Otra opcin a la hora de tratar con mtodos que lanzan errores es ignorarlos, la forma de ignorarlos
tiene una sintaxis muy similar a los opcionales pero con respecto al try:
1
try? myMethodThatThrows()
En el caso de que el mtodo retorne un valor (aparte de tambin lanzar errores), try? aplicado en el
mtodo retornar un valor nil para el valor esperado:
1
2
3
4
5
Un uso comn de try? es con sentencias guard else donde nos quedamos con el valor si el mtodo
no ha retornado error, y en el caso contrario en el bloque de else, especificamos que hacer:
1
2
3
11 - Gestin de errores
64
El uso de este operador es arriesgado ya que supone que conoces muy bien como se
comporta el mtodo que ests utilizando. salo, pero de forma responsable. Al igual
que
Defer
Puede interesarnos en determinados escenarios ejecutar una porcin de cdigo cuando por alguna
razn (por ejemplo debido a errores) el mtodo tiene que retornar.
Swift dispone de sentencias defer que te permite definir fragmentos de cdigo que sern ejecutados
cuando el mtodo tiene que retornar por cualquier razn. Los defer pueden ser definidos en cualquier
posicin de la funcin donde son creados y el orden de ejecucin es inverso al order de definicin,
es decir, el ltimo defer ser ejecutado primero.
1
2
3
4
5
6
7
8
9
10
11
12
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 funcin 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 adems estamos seguros de que no va a fallar.
Existen bloques de defer {} que pueden ser utilizados en funciones para ser llamados antes
de que una funcin salga de su ejecucin por cualquier razn, por ejemplo, por un error.
11 - Gestin de errores
65
Ejercicios
Analiza el ejemplo anterior y explica, sin usar Xcode, por qu el fragmento inferior no
compilar
1
2
3
4
5
6
7
8
9
10
11
func myMethod() {
let privateMethod: () -> Void = {
defer {
print "U"
}
}
defer {
print "O"
}
privateMethod()
defer {
print "A"
}
}
12 - Casting de tipos
Para validar el tipo de una instancia Swift ofrece una serie de herramientas para comprobarlo. Se
usa para comprobar si el tipo de una instancia corresponde a una determinada clase padre, o si est
definida en su propia jerarqua.
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 tambin sirve para comprobar si una instancia conforma un determinado
protocolo.
class Asignatura {
let nombre: String
init(nombre: String) {
self.nombre = nombre
}
}
class Mates: Asignatura {
init() {
super.init(nombre: "mates")
}
}
let cualquierAsignatura: Asignatura = Assignatura(nombre: "Historia")
let mates: Mates = Mates()
print("CualquierAsignatura es Mates: \(cualquierAsignatura is Mates)") // false
print("CualquierAsignatura es Asignatura: \(cualquierAsignatura is Asignatura)")\
// true
print("mates es Mates: \(cualquierAsignatura is Mates)") // true
print("mates es Asignatura: \(cualquierAsignatura is Asignatura)") // true
66
12 - Casting de tipos
67
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Asignatura {
let nombre: String
init(nombre: String) {
self.nombre = nombre
}
}
class Mates: Asignatura {
var formulas: [String] = []
init() {
super.init(nombre: "mates")
}
}
class Lengua: Asignatura {
var libros: [String] = []
init() {
super.init(nombre: "lengua")
}
}
let asignaturas: [Asignatura] = [Mates(), Lengua()]
12 - Casting de tipos
23
24
25
26
27
28
29
30
31
68
De la misma forma que en el ejemplo anterior, podemos hacer casing de Any a cualquier tipo usando
as.
Si quisieramos restringir el subconjuto de tipos soportados a slo valores de referencias, Objects,
podemos usar en su lugar AnyObject
1
2
3
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 y
sus respectivas versiones as? y as!.
Swift ofrece dos tipos genricos:
Any para cualquier tipo, sea de referencia o de valor.
AnyObject para cualquier tipo de referencia nicamente.
13 - Tipos encadenados
Tipos encadenados encadenados son tipos cuya definicin est incluida dentro de otros tipos. Son
tiles para organizar el espacio de nombres de nuestra base de cdigo.
Por ejemplo supn que tenemos un enum que representa el estado de un mensaje
1
2
3
4
5
enum EstadoMensaje {
case Enviado
case Recibido
case Leido
}
Como se podra usar el espacio de nombres con el ejemplo anterior? De forma muy sencilla,
encadenando un tipo Estado dentro de la clase Mensaje
1
2
3
4
5
6
7
8
9
class Mensaje {
enum Estado {
case Enviado
case Recibido
case Leido
}
}
Al haber especificado el tipo de nuetra variable, no es necesario de nuevo encadenar todos los tipos
para Recibido, es decir, Mensaje.Estado.Recibido, si no que simplemente especificamos el valor
del enum.
70
13 - Tipos encadenados
71
Resumen
Swift facilita tener un espacio de nombres permitiendo definir tipos dentro de otros tipos (tipos
encadenados).
Para acceder a travs de la cadena de tipos se usa ..
14 - Extensiones
La funcionalidad de una estructura, enum, y clase existente puede ser extendida gracias a la ayouda
de extensiones. Estas no permiten sin embargo reemplazar funcionalidad existente.
Con las extensiones podemos:
extension MiTipo {
// Nueva funcionalidad a aadir
}
Atributos computados
Las extensiones permiten aadir nuevos atributos de instancia y de tipo computados:
1
2
3
4
5
extension Int {
var double: Int { return 2*self } // atributo de instancia
var half: Int { return self/2 } // Atributo de instancia
static var zero: Int { return 0 } // Atributo de tipo
}
72
14 - Extensiones
73
Constructores
Swift ofrece la flexibilidad de aadir nuevos constructores a tipos existentes gracias al uso de
extensiones. El usuario puede aadir sus propiso tipos para extender los tipos ya existentes y aadir
nuevas opciones de inicializacin.
Las extensiones soportan init() pero no deinit()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
struct Rectangulo {
let size: Double
var area: Double { return size*size }
init(size: Double) {
self.size = size
}
}
extension Rectangulo {
init(area: Double) {
self.size = sqrt(area)
}
}
En el ejempo anterior, extendemos Rectangulo para inicializarlo a partir de otro valor, en este caso
a partir del area.
Mtodos
Nuevos mtodos de tipo y de instancia pueden ser aadidos de forma muy sencilla:
1
2
3
4
5
6
7
8
9
extension Int {
func mas(numero: Int) -> Int {
return self + numero
}
func esPar() -> Bool {
return numero%2 == 0
}
static func cero() -> Int {
return 0
14 - Extensiones
10
11
12
13
14
15
74
}
}
let cinco = 3.mas(2)
let esPar = 4.esPar() // true
le cero = Int.cero()
extension Double {
mutating func saure() {
let pi = 3.1415
self = pi * self * self
}
}
Subscripts
Swift tambin permite utilizar extensiones para aadir nuevos subscripts
1
2
3
4
5
6
7
8
9
10
extension Int {
subscript(var indice: Int) -> Int {
var no1 = 1
while indice > 0 {
no1 *= 10
--indice
}
return (self / no1) % 10
}
}
Tipos encadenados
Los tipos encadenados para clases, estructuras y enums tambin pueden ser extendidas con la ayuda
de extensiones.
14 - Extensiones
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
75
extension Int {
enum Calc {
case Sumar
case Restar
case Multiplicar
case Dividir
case Otro
}
var print: Calc {
switch self {
case 0:
return .Sumar
case 1:
return .Restar
case 2:
return .Multiplicar
case 3:
return .Dividir
default:
return .Otro
}
}
}
Protocolos
En la reciente versin de Swift, se introdujo la posibilidad de extender protocolos para ofrecer
implementaciones base de mtodos definidos en el protocolo:
1
2
3
4
5
6
7
8
9
10
11
12
protocolo Dinamico {
func mover()
}
struct Persona: Dinamico {
func caminar() {
print("Estoy caminando")
}
}
extension Dinamico where Self:Persona {
func mover() {
14 - Extensiones
13
14
15
76
self.caminar()
}
}
En el ejemplo anterior Dinamico es nuestro protocolo, y Persona conforma dicho protocolo. Sin
embargo Persona no implementa los mtodos requeridos por Dinamico. Lo que hacemos en su lugar
es crear una extensin del protocolo definiendo el comportamiento de mover() cuando la entidad
que conforme el protocolo sea una Persona.
La forma de limitar el subconjunto de entidades que tendrn dicha implementacin por defecto es
mediante:
1
where Self:MiTipo
extension Dinamico {
func mover() {
print("Me muevo")
}
}
Las extensiones de protocolos son muy tiles ya que permite heredar comportamientos
ya definidos en cualquiera de nuestros tipos existentes sin necesidad de tener que
recurrir a subclases o derivados.
Resumen
15 - Protocolos
Los protocolos sirven para definir abstracciones en las interfaces de nuestros estructuras, enums,
clases. Sirven para definir el comportamiento de estos especificando que propiedades tienen, los
mtodos que exponen as como los constructores.
Sintxis
La forma de definir un protocolo es la siguiente
1
2
3
protocol MiProtocolo {
// Definicin del protocolo
}
77
15 - Protocolos
1
2
3
4
78
protocol MiProtocolo {
var nombre: String { get }
func eliminar() -> Bool
}
protocol Crece {
mutating func cumpleanyos()
}
public struct Persona: Crece {
var anyos: Int = 0
mutating func cumpleanyos() {
self.anyos = self.anyos + 1
}
}
En el ejemplo anterior el mtodo cumpleanyos() se espera que mute a la entidad que lo conforma,
por eso especificamos que dicho mtodo es mutating.
Requerimientos en constructores
Los protocolos en Swift tambin permiten especificar requerimientos de determinados constructores:
15 - Protocolos
1
2
3
4
5
6
7
8
9
10
79
protocol MiProtocolo {
init(name: String)
}
class MiClase: MiProtocolo {
required init(name: String) {
// Inicializa MiClase
}
}
En este caso Swift forzar a definir dicho constructor como required para asegurar de forma
explicita que todas las subclases de dicha clase implementan este constructor.
protocol EdadClasificable {
var edad: Int { get }
func tipo() -> String
}
class Persona {
let nombre: String
let edad: Int
init(nombre: String, edad: Int) {
self.nombre = nombre
self.edad = edad
}
}
etension Persona: EdadClasificable {
func tipo() -> String {
switch edad {
case 0...2:
return "Bebe"
case 2...12:
return "Nio"
15 - Protocolos
23
24
25
26
27
28
29
30
31
80
case 13...19:
return "Joven"
case let x where x> 65:
return "Mayor"
default:
return "Normal"
}
}
}
A la hora de extender el protocolo no hemos tenido que especificar el atributo edad ya que este viene
ya definido en la clase que est conformando el protocolo.
Swift no permite variables computadas en extensiones.
Composicin de protocolos
Swift permite la composicin de mltiples protocolos en un nuevo protocolo mediante:
1
Por ejemplo:
1
2
3
4
5
6
7
8
9
10
11
12
protocolo TieneNombre {
var name: String { get }
}
protocolo Piensa {
func razona()
}
func print(humano: protocol<TieneNombre, Piensa>) {
humano.razona()
print("El humano: \(humano.name) razon")
}
15 - Protocolos
81
protocol Figura {
var area: Float { get }
}
class Circunferencia: Figura {
let radio: Float
init(radio: Float) {
self.radio = radio
}
var area: Float {
return 3.141516*self.radio*self.radio
}
}
protocol Poligono: Figura {
var lados: Int { get }
}
class Cuadrado: Poligono {
let ancho: Float
var lados: Int {
return 4
}
var area: Float {
return self.ancho*self.ancho
}
init(ancho: Float) {
self.ancho = ancho
}
}
let figura: Figura = Circunferencia(radio: 25)
if let circunferencia = figura as? Circunferencia {
print("Poligono de radio \(circunferencia.radio)")
}
else if let poligono = figura as? Poligono {
print("Poligono de \(poligono.lados) lados")
}
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.
Mtodos.
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 comprobacin y casting de tipos tambin funcionan a nivel de
protocolos.
16 - Genricos
Una de las funcionalidades ms potentes introducidas por Swift es genricos. Genricos permiten
escribir funciones y tipos flexibles y reusables sin dejar de lado la seguridad de tipos de Swift.
La funcin cambiar<T> es una funcin genrica con tipo de parmetro <T>. De esta forma podemos
reutilizar la misma funcin para distintos tipos de como entrada de la funcin.
Al especificar un parmtro como inout estamos dando la funcin modifica directamente
la referencia a dicho parmetro
Tipos genricos
De forma similar tambin podemos especificr un tipo como genrico
83
16 - Genricos
1
2
3
4
5
6
7
84
struct Coleccion<T> {
var elementos: [T] = []
mutating func anyadir(elemento: T) {
elementos.append(elemento)
}
}
extension Coleccion {
var primer: T? {
return elementos.firstObject
}
}
85
16 - Genricos
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
protocol Contenedor {
typealias TipoElemento
mutating func anyadir(elemento: TipoElemento)
var total: Int { get }
subscript(i: Int) -> TipoElemento { get }
}
struct Coleccion<T>: Contenedor {
var elementos: [T] = []
mutating func anyadir(elemento: T)
elementos.append(elemento)
}
Sentencias Where
De la misma forma que Swift permite definir constraints para el tipo de los genricos, tambin lo
permite para los tipos asociados. La forma de hacerlo en Swift es mediante sentencias where que se
coloca a continuacin de la lista de tipos de parmetros:
16 - Genricos
1
2
3
4
5
6
86
func matchElementos<
C1: Contenedor, C2: Contenedor
where C1.TipoElemento == C2.TipoElemento, C1.TipoElemento: Equatable>
(contenedor: C1, contenedor: C2) -> Bool {
return someContainer.count == anotherContainer.count
}
En el ejemplo mostrado estamos usando la sentencia where para comprobar que el el tipo del
elemento de C1 y de C2 es el mismo y que adems conforma el protocolo Equatable.
Resumen
Los genricos permiten definir funciones y tipos flexibles a cualquier tipo sin dejar de lado la
seguridad de tipos de Swift.
Podemos definir funciones con parmetros (de entrada y salida) genricos.
Estructuras y clases tambin pueden ser genricas.
El conjunto de tipos genricos puede ser restringido mediante constraints.
Los protocolos tambin pueden ser genricos y los tipos de estos son especificados mediante
typealias. Esto se conoce como tipos asociados.
Tambin podemos restringir el conjunto de tipos genricos de tipos asociados mediante
sentencias where.
17 - Control de acceso
Para restringir el acceso a bloques de cdigo, mdulos y abstracciones podemos usar control
de acceso en Swift. Clases, estructuras y enusm puede ser accecdidos de acuerdo al control de
acceso especificado para sus properties, mtodos, constructores y subscripts. Constantes, variables y
funciones en un protocolo estn restringidas y se permite el acceso de forma global y local mediante
el control de acceso.
El control de acceso se basa en mdulos y sus ficheros cdigo fuente.
Se define mdulo como una unidad nica de distribucin de cdigo que puede importarse mediante
import MiModulo. Un fichero de cdigo fuente es cada uno de los ficheros que pertenecen al
mdulo.
Los tres niveles de acceso que ofrece Swift son public, internal y private.
Public: Las entidades definidas como pblicas en un mdulo pueden ser accedidas desde el
mismo mdulo y tambin desde otros mdulos que importen a este.
Internal: Las entidades definidas cmo internal pueden ser utilizadas desde el propio mdulo
pero no desde otros mdulos, incluso si el mdulo es importado.
Private: En este caso las entidades definidas como private en un cdigo fuente son slo visibles
desde este fichero. Si otros ficheros del mismo mdulo intentaran usar dicha entidad, esta no
sera visible.
Algunos ejemplos de control de acceso seran:
1
2
3
4
17 - Control de acceso
1
2
3
88
Si los parmetros de la funcin tuvieran un nivel de acceso ms restrictivo que el de la propia funcin
el proceso de compilacin lanzara un error:
1
2
3
4
5
public
case
case
case
}
enum Error {
HTTPError
StoreError
Unknown
89
17 - Control de acceso
En el ejemplo anterior la subclase Tennis puede sobreescribir el valor la funcin comenzar() ya que
ambas definicin estn en el mismo fichero y por lo tanto son visibles. Si se intentara usar la clase
Cricket desde otro mdulo el mtodo comenzar() no sera visible, pero sin embargo, si que lo sera
para la clase Tennis.
No compilara ya que estamos intentando hacer pblico un tipo que de por s es privado.
Getters y Setters
Los getters y setters definidos para constantes, variables, properties y subscripts automticmanete
reciben el mismo nivel que el de la constante, variable, property o subscript al que pertenecen:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
El
Se
El
Se
nuevo valor
han aadido
nuevo valor
han aadido
es 100
100
es 400
300
17 - Control de acceso
90
// Modulo A
public class ClassA {}
// Modulo B
import ModuloA
let miObjetoA: ClassA = ClassA()
El ejemplo anterior no compilara porque desde el mdulo B no es visible el constructor por defecto
de dicha clase. Tendramos que expecificarlo de forma explcita:
1
2
3
17 - Control de acceso
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
91
17 - Control de acceso
92
El ejemplo anterior lanzara una alerta ya que estamos especificando un nivel de acceso pblico para
la funcin 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 la
visibilidad entre ficheros fuente y entre mdulos.
Los niveles de acceso pueden ser especificados en funciones, atributos y constructores.
Tambin 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 extensin.
Ejercicio
Analiza el siguiente ejemplo y aplica las correcciones que sean necesarias para que desde
Modulo1 se pueda ejecutar un comando de API (Sin uar Xcode):
17 - Control de acceso
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// Modulo 1
// Fichero A
internal class Comando {
private let nombre
public init(name: String) {
self.nombre = nombre
}
public func ejecutar() {
print("Ejecutando: \(nombre)")
}
}
// Modulo 1
// Fichero B
public class ComandoAPI: Comando {
}
// Modulo 2
import Modulo1
ComandoAPI().ejecutar()
93