Você está na página 1de 98

Introduccin a Swift

Pedro Piera 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 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

9 - Automatic Reference Counting (ARC) . . . . . . . .


Cmo funciona . . . . . . . . . . . . . . . . . . . . .
Ciclos de strong reference entre clases de instancia .
Resolviendo strong reference cycles entre instancias .

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

50
50
51
53

.
.
.
.
.

.
.
.
.
.

CONTENTS

Ciclos strong reference en closures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .


Resumen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

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

Extendiendo un tipo genrico . . . . .


Constraints para tipos . . . . . . . . .
Tipos asociados (Protocolos Genricos)
Sentencias Where . . . . . . . . . . .
Resumen . . . . . . . . . . . . . . . .

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

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"

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


1

var latitude=39.47913556, longitude=-0.35497427

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

var nombreEmpleado: String

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

var x,y,z: Double

Nombrando Constantes y Variables


Los nombres de constantes y variables pueden contener casi cualquier carcter excepto espacios en
blanco, simbolos matemticos, flechas, carcteres unicode invlidos o carcteres de dibujo de cajas
o lneas. Tampoco pueden empezar por un nmero, aunque s que pueden contener nmeros en
cualquier otra posicin.
Es posible cambiarle el valor a una variable en cualquier momento, siempre que se lo cambiemos
por otro tipo compatible:
1
2

var bienvenida="Hola Pepito!"


bienvenida="Hola Juanito!"

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

let bienvenida="Hola Pepito!"


bienvenida="Hola Juanito!"
//Error de compilacin: bienvenida no puede ser cambiado.

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

let coordenada=(39.47913556, -0.35497427)


//la coordenada podramos extraerla en constantes
let (latitud, longitud) = coordenada
print ("La latitud es \(latitud)")
print ("La longitud es \(longitud)")
//Si solo necesitaramos la longitud podramos usar el guin bajo (_) al descompo\
ner una tupla
let (_,soloLaLongitud) = coordenada
print ("La longitud es \(soloLaLongitud)")
//Tambin se puede acceder a los valores de las tuplas mediante su ndice
print ("La latitud es \(coordenada.0)")
//Adems, se pueden poner nombres a los valores de la tupla cuando se crean y ac\
ceder mediante estos
let coordenadaNombres=(latitud:39.47913556, longitud:-0.35497427)
print ("La latitud es \(coordenadaNombres.latitud)")
print ("La longitud es \(coordenadaNombres.longitud)")

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

let numeroPosible = "123"


let numero = Int(numeroPosible)
//En este caso numero se inferira como tipo "Int?" o "Int opcional"

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

var respuesta: String?

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

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


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

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

//El primero y ms claro es la asignacin, con el operador =


var a = 5
//Se puede usar con tuplas como ya hemos visto
let (x,y)=(1,2)
//No devuelve valor
if x=y {
//No sera valido, ya que = no devuelve un valor
}

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

//Los operadores aritmticos (+, - , / y *)


var two = 1+1
var one = 2-1
var three = 9/3
var four = 2*2
//La suma se puede usar en cadenas
var holaMundo = "hola " + "mundo"
//Operador resto %
var resto = 11 % 2 //igual a 1
//Operaciones de incremento y decremento
var contador = 0
contador++ //1
contador-- //0
var nuevoContador = contador++
//nuevoContador = 0 y contador = 1 porque nuevoContador se ha asignado antes del\
incremento
var ultimoContador = ++contador
//ultimoContador = 2 y contador = 2 porque ultimoContador se ha asignado despus\
del incremento

//Se puede realizar una asignacin compuesta con los operadores


var compuesto = 1
compuesto+=3 //resultado 4

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

let cadena1 = "hola "


let cadena2 = "mundo"
var bienvenida = cadena1+cadena2
let exclamacion: Character = "!"
bienvenida.append(exclamacion)

Otras operaciones que podemos hacer con cadenas seran:


Insertar un caracter en un indice determinado con insert(_:atIndex:) Insertar el contenido de otro
string en un ndice con insertContentsOf(_:at:) Borrar un carcter de un indice determinado con
removeAtIndex(_:) Borrar un substring de un rango determinado mediante removeRange(_:)
Trabajando con Variables y Constantes
Veamos un pequeo ejemplo de cmo funcionara el trabajo con las constantes y variables segn
hemos visto hasta ahora:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

//Definimos la variable de tipo String


var nombreEmpleado="Pepito"
//Definimos otra variable de tipo String, pero sin asignarle valor
var nombreJefe:String
//Como son variables podemos reasignar el contenido de ambas, vamos a darle un a\
umento a Pepito y a contratar a un nuevo empleado
nombreJefe=nombreEmpleado
nombreEmpleado="Juanito"
//Ahora veamos los sueldos que tienen para poder trabajar con numeros
//El sueldo del jefe es fijo, as que lo definiremos con una constante
let sueldoJefe = 10
//El sueldo del empleado puede cambiar, as que se define con una variable
var sueldoEmpleado = 6
//Si el jefe intenta subirse el sueldo le dar un error
sueldoJefe = 11 //Error de compilacin

1 - Variables y tipos

20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

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


sueldoEmpleado = 7
//Si queremos saber el coste total de los sueldos podemos utilizar el operador +
var sumaSueldos = sueldoJefe + sueldoEmpleado
//Y si queremos saber el sueldo medio podemos dividirlo por 2
var mediaSueldos = sumaSueldos/2
//Cuidado! mediaSueldos se ha inferido como entero y debera ser un decimal, as\
que le forzaremos a que lo sea
var mediaSueldosFloat: Float = Float(sumaSueldos)/2.0
//Si queremos subir un poco ms el sueldo del empleado podemos hacerlo mediante \
un operador unario (++)
++sueldoEmpleado

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

var enteros = [Int]()


print ("enteros es de tipo [Int] con \(enteros.count) elementos")
enteros.append(3)
//Cuando el compilador ya conoce el tipo podemos reinicializar el array mediante\
[]
enteros = []
//Podemos inicializar un array con un valor por defecto
var tresEnteros = [Int](count: 3, repeatedValue: 0)

2 - Colecciones

11
12
13
14
15
16
17
18
19
20
21
22
23

10

//Es posible enlazar dos arrays mediante el operador +


var tresEnterosMas = [Int](count: 3, repeatedValue:1)
var seisEnteros = tresEnteros+tresEnterosMas
//Si conocemos los valores del array, podemos inicializarlo directamente
var nombres = ["Rodrigo", "Lola"]
//No hemos puesto el tipo del array ya que este va a contener nicamente Strings\
y eso el compilador lo puede obtener por inferencia de tipos
//Con el operador += podemos aadir otro array
nombres += ["Sofia", "Anibal"]

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

var letras = Set<Character>()


letras.insert("a")
//Adems, igual que en los arrays, podemos volver a reinicializar el Set con la \
instruccin []
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 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

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

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

print("En el set haban \(numeros.count) elementos");


if numeros.isEmpty {
print ("Est vaco")
} else {
print ("No est vaco")
}
//Podemos aadir nuevos elementos con insert
numeros.insert("cuatro")

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

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


var numerosImpares : Set = ["uno", "tres", "cinco"]
var numerosPrimos : Set = ["uno", "dos", "tres", "cinco"]
var paresPrimos: Set = numerosPares.intersect(numerosPrimos)
//paresPrimos = ["dos"]
var imparesNoPrimos: Set = numerosImpares.subtract(numerosPrimos)
//imparesNoPrimos = []
var paresImpares: Set = numerosPares.union(numerosImpares)
//paresImpares = ["dos", "cuatro", "seis", "uno", "tres", "cinco"]
paresImpares.isSupersetOf(paresPrimos)
//true
paresPrimos.isSubsetOf(paresImpares)
//true
paresPrimos.isSupersetOf(numerosImpares)
//false
numerosPares.isDisjointWith(numerosImpares)
//true

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

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 tiene
datos sobre su tipo ya podemos re inicializarlo mediante [:].

2 - Colecciones

1
2

13

enteros[16] = "dieciseis"
enteros = [:]

La forma ms directa de crear un diccionario es a travs 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 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

aeropuertos["NYC"] = "New York"


aeropuertos["MAD"] = nil
print ("\(aeropuertos["NYC"]!)")

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

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


print("indice es \(indice)")
}

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

var indice: Int


for indice=0; indice<3; ++indice {
print("indice es \(indice)")
}
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

3 - Control de flujo

1
2
3

15

for indice in 1...5 {


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

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

var nombre = "David"


if (nombre == "David") {
print ("Hola David")
}

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

let caracter: Character = "e"


switch caracter {
case "d", "a", "v", "i":
print ("Contenido en david")
default:
print ("No contenido en david")
}

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")
}

let posicion = (2,2)


switch posicion {
case (0,0):
print
case (0,_):
print
case (_,0):
print
default:
print
}

("La posicin est en el origen")


("La posicin est en el eje X")
("La posicin est en el eje Y")
("La posicin est fuera de los ejes")

3 - Control de flujo

19

Instrucciones de transferencia de control


Existen algunas instrucciones en Swift que nos permitiran cambiar el flujo del control de la
aplicacin, estas son:
continue: bsicamente permite decirle a cualquier bucle que continue la ejecucin desde el
siguiente elemento, saltandose lo que le quede del actual.
break: permite romper la ejecucin de un bucle. Es util si estamos buscando un elemento
dentro de un array con un bucle y queremos cortar la ejecucin cuando ya lo tenemos.
fallthrough: sirve para permitir, en una sentencia switch, que el control caiga de un bloque
de ejecucin al siguiente.

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

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

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

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


return s1>s2
}
var ordenadosHaciaAtras = nombres.sort(haciaAtras)

Usando closure expressions

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

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


n s1 > s2 })

Bsicamente, la estructura es sencilla, comenzamos con una {, seguida de la declaracin de la


definicin de parmetros y tipo de retorno. Despus ponemos la palabra reservada in, que separar la
definicin de parmetros del cuerpo del closure. Finalmente ponemos el cuerpo del closure cerrando
todo con una llave de cierre }.
Como podemos ver, simplemente cambiando la funcin por un closure inline hemos acortado de
forma significativa la cantidad de cdigo escrito sin cambiar el resultado.
Infiriendo el tipo por el contexto

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

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

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

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 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

var ordenadosHaciaAtras = nombres.sort( { $0 > $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

var ordenadosHaciaAtras = nombres.sort(>)

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

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

E incluso, si el mtodo slo admite un parmetro, podemos no escribir los ().

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

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


var total = 0
func sumador() -> Int {
total += cantidad
return total
}
return sumador
}
let sumaDiez = suma(sumaBase: 10)
sumaDiez()
//10
sumaDiez()
//20
sumaDiez()
//30

let sumaTres = suma(sumaBase: 3)


sumaTres()
//3
sumaTres()
//6
sumaDiez()
//40

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

let sumaDiez2 = sumaDiez


sumaDiez2()
//50

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.

Comparando clases y estructuras


Las clases y estructuras en Swift tienen muchas cosas en comn, ambas pueden:

Definir propiedades para guardar valores


Definir mtodos para proveer funcionalidad
Definir subscripts para proveer acceso a sus valores
Definir inicializadores para definir su estado inicial
Ser extendidas
Conformar protocolos

Adems de esto, las clases permiten (y las estructuras no):

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)

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 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

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


return nombre1 + " dice hola a " + nombre2
}

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(diHola nombre1: String, aQuien nombre2: String) -> String {


return nombre1 + " dice hola a " + nombre2
}
diHola(quien: "Johnny", aQuien: "Pepito")
//Esta es otra opcin
func diHola(nombre1: String, _ nombre2: String) -> String {
return nombre1 + " dice hola a " + nombre2
}
diHola("David", "Pepito")

Es posible definir valores por defecto para los parmetros


1
2
3
4
5
6

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

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


sumatorio += step
return sumatorio
}
//En este caso podemos cambiar sumatorio (en el contexto del mtodo), pero no po\
driamos modificar step
contador(step: 1)
contador(step: 3)
contador(5, step: 3)
//Comprobamos que slo se cambia en el contexto del mtodo
var sumatorio = 10
contador(sumatorio, step: 4)
sumatorio //=10

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

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


sumatorio += step
}
var sumatorio = 10
contador(&sumatorio, step: 4)
sumatorio //=14

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

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

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

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


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

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

subscrit(index: Int) -> Int {


get {
// Defino como obtengo el elemento en el index pasado al subscript
}
set(newValue) {
// Defino como se setea el elemento en un index determinado.
}
}

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


caso anterior, tipo Int
En el caso de tener subscripts que son slo de lectura no es necesario definir las sentencias de get
y set y simplemente definir cmo se retorna el parmetro en ese index:
1
2
3

subscript(index: Int) -> Int {


// Retornar el elemento en ese index
}

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

Dnde 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 creas
tu propia coleccin, por ejemplo, para encapsular informacin extra aparte de la coleccin, entonces
pudes definir como sera el subscript de tu nuevo tipo.
Recuerda, si tu clase/estructura modela una coleccin, los subscripts son tiles para
acceder a los elementos de la coleccin de forma ms cmoda.

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.

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
2
3
4
5
6
7
8

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

let miMascota = Animal()

Una vez tenemos la instancia de animal creada podemos acceder a cualquier propiedad de dicho
animal:
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 nombre
de la nueva clase la clase de la que esta hereda con el siguiente formato
34

7 - Herencia

35

class MiSubclase: MiClasePadre

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


As pues, si quisiramos hacer la clase Animal ms concreta y representar un animal en concreto,
por ejemplo Perro podramos hacer una subclase de Animal:
1
2
3
4
5
6

class Perro: Animal {


var raza: String = ""
func guau() {
print("\(self.nombre): Guau Guau")
}
}

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

let miPerro = Perro()


miPerro.raza = "Yorkshire"

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

Acceder a la definicin de la clase padre


Cuando sobreescribimos el comportamiento de un subscript, mtodo o variable nos puede interesar
acceder a la definicin original en la clase padre. Esto se puede hacer de la siguiente forma:
Si hemos sobreescrito un mtodo en la definicin de nuestro propio mtodo podemos llamar
a super.miMetodo() y ejecutar la implementacin de dicho mtodo en la clase padre.
Si hemos sobreescrito un atributo en la nueva definicin de esta, podemos llamar a super.miPropiedad para obtener el valor de la clase padre.
En el caso de subscripts de forma similar podemos llamar a super[someIndex] para usar el
subscript de la clase padre.

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

class Perro: Animal {


override func camina() {
print("Soy un perro que camina con \(self.patas) patas")
}
}

Si creas una instancia de dog, y llamas al mtodo camina() ejecutar el mtodo sobreescrito.
1
2

let miPerro = Perro()


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 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

class Yorkshire: Perro {


override var nombre: String {
return "Yorkshire"
}
}

En el ejemplo anterior estamos sobreescribiendo la atributo nombre definida en la clase Animal. En


concreto estamos definiendo el getter de esta variable. Si quisiramos acceder a nombre de Animal
simplemente tendramos que hacer:
1

super.nombre

Sobreescribiendo los observers de los atributos


Tambin podemos sobreescribir los observers de variables willSet, didSet para escuchar cualquier
cambio que se pueda producir en las mismas.
No podemos sobreescribir los observers para tipo de variables constante, let, ya que
estas variables no pueden cambiar y por lo tanto no tiene sentido escuchar cambios en
las mismas.
Siguiendo con el ejemplo anterior, si quisieramos sobreescibir el observer de la variable happy,
podramos hacerlo de la siguiente forma:
1
2
3
4
5
6
7
8
9
10
11
12

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")

En el constructor de la clase (forma de definir a los inititializers) pasamos el parmetro nombre, y


en la definicin del init, damos valor al atributo nombre de la clase al valor de nombre (parmetro
pasado).
A la hora de pasar parmetros en el constructor, al igual que en los mtodos, podemos definir valores
por defectos. El usuario a la hora de crear una instancia de la clase puede no pasar esos parmetros
y el constructor tomar el valor por defecto:

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")

El en fragmento anterior a la hora de inicializar uns instancia de Ordenador se utiliza el nombre


externo del parmetro compania, de. De esa forma es fcil reconocer que el ordenador ha sido
fabricado por Apple. Si hubieramos utilizado el mismo nombre para la variable tanto de forma
interna como externa:
1

let ordenador: 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()

En el ejemplo anterior trabajo es opcional porque un perfil puede tener o no trabajo, y no es


necesario especificar un valor para ese atributo.

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
}
}

Constructor por defecto


En el caso de no definir ningn 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 compilador
retornar un error y te forzar a definir como estas son inicializadas.
1
2
3
4
5
6
7
8

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)

A estos constructors se les denomina Memberwise.

8 - Inicializacin y deinicializacin

43

Delegacin al inicializar (value types)


Las estructuras en Swift pueden tener multiples constructores definidos. Estos pueden delegar
responsabilidades de inicializacin entre ellos en un proceso conocido como tal. Para entenderlo
mejor analicemos el ejemplo inferior:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

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())
}
}

La estructura anterior tendra 3 constructores.


1. El constructor por defecto cuyo nico argumento es el color puesto que este no tiene valor por
defecto: Rectangulo(color: miColor)
2. El segundo constructor sera el primero que se aprecia en el ejemplo donde pasamos todas las
variables a inicializar: Rectangulo(ancho: 20, alto: 20, color: miColor)
3. El ltimo de ellos sera el constructor que internamente usa un color por defecto y slo espera
width y height: Rectangulo(ancho: 20, alto: 20)

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

init(parametro1: String, parametro2: String) {


// Inicializa la clase
}
convenience init() {
self.init(parametro1: "1", parametro2: "2")
}

Reglas de inicializacin para clases


Para simplificar la forma en la que interactuan los initializers en Swift, el lenguaje aplica las
siguientes 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 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.

Herencia automtica del constructor


Aunque en el punto anterior se mencione que los constructores no son automticamente heredados
de la clase padre, si se cumplen algunas condiciones, si que se pueden heredar. En prctica, esto
significa que no necesitas sobreescribiri construcores en algunos escenarios, y puedes heredarlos de
la super clase.
Las reglas que aplican si los constructores sern heredados son
Regla 1: Si la subclase no define ningn constructor designado, automticamente hereda todos
los constructores designados de la super clase.
Regla 2: Si la subclase ofrece una implementacin de todos los constructores designados
de la super clase ya sea por la regla 1, o mediante una implementacin como parte de su
definicin entonces automticamente hereda todos los constructores de conveniencia de la
super clase.
Las reglas tambin aplican incluso si hay ms constructores de conveniencia.

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
}
}
}

Constructor Failable init!


Los constructores de tipo failable, init?() retornan nil si no se ha podido inicializar. Si quisiramos
que en lugar de retornar un opcional unwrappeado de forma implicita podemos hacerlo con la
versin del constructor init!.

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
}
}

Cuando un init se define como required la subclase que lo sobreescriba no necesita


utilizar override.

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

9 - Automatic Reference Counting


(ARC)
Swift usa ARC para gestionar la memoria. En la mayora de casos, esta gestin de menoria funciona
de forma automtica de forma que no necesita de explicitamente indicar cmo y cuando liberar los
elementos en memoria. ARC automticamente libera de memoria aquellas instancias que ya no son
necesarias.
En algunos casos particulares, sin embargo, ARC requiere ms informacin para la relacin entre
las partes de tu cdigo para hacer dicha gestin.

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

9 - Automatic Reference Counting (ARC)

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"

Ciclos de strong reference entre clases de instancia


En el ejemplo anterior es fcil liberar de memoria las 3 referencias simplemente haciendo sus valores
igual a nil. Sin embargo en algunos escenarios podemos escribir cdigo con instancias que nunca
son liberadas de memoria. Esto sucede cuando dos instancias mantienen una referencia strong mutua
de forma que una siempre referencia a la otra.
La forma de solucionar este problema en Swift es definiendo dichas relaciones como weak o unowned
en lugar de como strong references. Un ejemplo de strong reference podra ser el siguiente:

9 - Automatic Reference Counting (ARC)

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

Album(nombre: "Head full of dreams")


= Cancion(nombre: "Adventure of a lifetime", album: album!)
nil // No se libera album
= nil // No se libera track

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.

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 tipo
strong. Swift ofrece dos alternativas a estas, unowned y weak. Ambas permiten tener una referencia
a la otra instancia pero sin mantenerlas en memoria, es decir, si en cualquier punto la otra instancia
queda liberada, nuestra referencia apuntar a nil.
La principal diferencia entre weak y unowned es que en el caso de weak la variable ser un opcional
explcito, 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 implcito, y tiene sentido su uso cuando estamos seguros
de que nuestra variable nunca ser nil, y por lo tanto, podemos ahorrar las comprobaciones extras
de valor en opcionales.
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

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

9 - Automatic Reference Counting (ARC)

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

weak var miVariable: Coche?

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")

9 - Automatic Reference Counting (ARC)

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.

Ciclos strong reference en closures


De forma similar a como se crean los ciclos de referencia strong entre clases, estos tambin pueden
ser creados si la instancia de una clase retiene a un closure, y este closure a su vez est reteniendo a
la propia clase. Cuando se define un closure, los elementos accedidos fuera del scope del closure son
capturados. Por ejemplo, si desde el closure accediramos al elemento self.miPropiedad, estaramos
capturando self en ese closure y por lo tanto creando un ciclo de referencia.
La razn por la que esto sucede es debido a que al igual que las clases, los closures tambin son tipos
de referencia. Cuando asignas un closure a un atributo, en realidad ests asignando una refencia a
dicho closure. En esencia, el mismo problema que el anterior.
Este problema es resuelto de forma muy elegante en Swift comparado con la forma utilizada en
Objective-C. En este caso se hace uso de capture lists.
Un ejemplo de un ciclo de referencia strong podra ser el siguiente:

9 - Automatic Reference Counting (ARC)

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.

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. Estas
son un conjunto de weak o unowned keywords seguidos de la instancia de clase (como por ejemplo
self) o una variable inicializada con algn valor. El formato de un capture list es el siguiente:
1
2
3
4

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.

9 - Automatic Reference Counting (ARC)

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.

Alternativa del encadenado de opcionales al unwrap forzado


La forma de encadenar opcionales es usando el signo ? para llamar a una property. La otra opcin
sera usar un unwrap forzado usando !, sin embargo del uso de este operador tiene el riesgo de que
si alguno de los valores es nil, la ejecucin lanzar un error y nuestra aplicacin se detendr:
1
2
3
4
5
6
7
8
9

class Elecciones {
var candidato: Candidato?
}
class Candidato {
var nombre ="MP"
}
let elecciones = Elecciones()
let nombreCandidato = elecciones.candidato!.nombre

Si intentamos ejecutar el fragmento de cdigo anterior en Playgrownd lanzar un error diciendo


que ha sido imposible hacer el unwrap forzado ya que candidato es nil para nuestra instancia
elecciones creada.

Uso de el encadenado de opcionales


Con properties
La otra opcin sera hacer uso de ? comprobando en el momento de acceder al atributo en concreto
de la cadena si este tiene valor:
58

10 - Encadenado de opcionales

1
2
3
4
5
6
7

59

let elecciones = Elecciones()


if let nombre = elecciones.candidato?.nombre {
print("El nombre del candidato es \(nombre)")
}
else {
print("El candidato no se puede obtener")
}

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

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


let valor = array?[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

Encadenando mltiples niveles


Lo explicado anteriormente aplica a mltiples niveles de opcionales, sean properties, mtodos o
subscripts. Cuando algn elemento opcional de la cadena tiene valor nil, este valor se propagar al
valor que ests asignando y la cadena se detendr.
Recuerda, a la hora de usar encadenado de opcionales, el valor de salida ser siempre
un opcional. De ah que sea muy til el uso de este patrn con el patrn if let ....
else {}

1
2
3
4
5
6

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


print("La calle donde Pedro vive es \(calle)")
}
else {
print("Imposible obtener la calle")
}

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

func myMethod() throws {


// Implementacin
}

Lanzando errores: ErrorType


La forma de lanzar errores dentro del mtodo 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 protocolo
ErrorType independientemente del tipo que sea. El error puede ser una clase, un struct, o incluso un
enum. Los ejemplos mostrados son ejemplos vlidos de errores:
1
2
3

class MyClassError: ErrorType {}


class MyStructError: ErrorType {}
class MyEnumError: ErrorType {}

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

enum HTTPError: ErrorType {


case ClientError
case ServerError
// Otros HTTP Errors
}

En cualquier punto de nuestro mtodo podemos lanzar el error:

61

11 - Gestin de errores

1
2
3

62

func myMethod() throws {


throw HTTPError.ClientError
}

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

Llamando mtodos con try


Siempre que se ejecute un mtodo que pueda lanzar un error Swift te forzar a hacerlo con try. De
lo contrario el compilador te alertar y tu aplicacin no compilar. En funcin si queremos o no
ignorar el error retornado, o de si estamos seguros de que el mtodo no va a lanzar error alguno
podemos usar try en diferentes contextos.
1

try myMethodThatThrows()

Capturando errores con do/catch


Si intentramos el ejemplo anterior, el compilador se quejara ya que estamos intentando ejecutar
un mtodo que lanza errores pero no estamos especificando como esos errores seran capturados.
La primera opcin es ser explcitos usando la sentencia do {} catch {} donde podremos espeificar
qu hacer en el caso de obtener un error:
1
2
3
4
5
6

do {
try myMethodThatThrows()
}
catch {
// Oh! algo pas, qu hacemos entonces?
}

Si no se especifica el tipo de error, en el bloque de catch se capturarn todos los errores,


independientemente de su tipo. Si quisiramos especificar un bloque de catch para un subconjunto
determinado de errores podemos especificarlo en un patrn:

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

func myMethod() throws -> String {


throw CustomError.Default
}
let value: String? = try? myMethod()

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

guard let value = try? myMethodThatThrows() else {


// Hacemos algo
}

Deshabilitando la propagacin de erores


La ltima opcin es ideal para situaciones en las cuales estamos seguros de que el mtodo no va
a lanzar error. En ese caso podemos directamente usar try! y nos ahorrara tener aadir todo el
conjunto do/catch y en una linea de cdigo tendramos la ejecucin del mtodo.

11 - Gestin de errores

64

let value = try! myMethodThatThrows()

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

func dameBanana() -> Banana? {


defer {
self.recogerEscaleras()
}
do {
self.colocarEscaleras()
let banana: Banana = cogerBanana()
return pelarBanana(banana)
}
catch {
print("No te puedo dar la banana. Algo ha fallado: \(error)")
return nil
}
}

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 getTracks() -> [Track] {


do {
return try client.getTracks()
}
catch Client.Errors.HTTPError {
print("Hubo un error con la peticin HTTP")
}
catch Client.Errors.Mapping {
print("Hubo un error mapeando la respuesta")
}
}

Analiza el ejemplo inferior e indica que se imprimir por consola


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

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.

Comprobacin del tipo


La forma de comprobar un tipo es mediante el operador is. El operador is comprueba si una
instancia pertenece a una subclase determinada y retorna true en ese caso. De lo contrario retornar
false:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

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

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 de
cualquierAsignatura este retorna false cuando comprobamos si se trata de Mates ya que esta
instancia no lo es. Al comprobar los tipos de mates ambos son ciertos ya que por un lado es una
instancia del tipo Mates pero al ser este subclase de Asignatura tambin pertenece a este tipo.

Casting a un tipo determinado


El operador is slo sirve para comprobacin. Si quisiramos hacer un cast de esta variable, es decir,
tratarlo a nivel de compilacin 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 involucrados
en el casting tienen relacin.
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 ests seguro de que no lo har puedes usar un casting
forzado.

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

for asignatura in asignaturas {


if let lengua = asignatura as? Lengua {
print("Soy lengua y estos son mis libros: \(lengua.libros)")
}
else if let mates = asignatura as? Mates {
print("Soy mates y estas son mis formulas: \(mates.formulas)")
}
}

El operador as? es tpicamente usado en sentencias if/let como se muestra en el


ejemplo anterior. De esta forma realizamos una validacin segura de los tipos y no
forzado, algo que se puede traducir en crashes en nuestros proyectos cuando alguien
introduce 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
2
3
4

var asignaturas: [Any] = []


asignaturas.append(Mates())
asignaturas.append(Lengua())
asignaturas.append("Desconocida")

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

var asignaturas: [AnyObject] = []


asignaturas.append(Mates())
asignaturas.append(Lengua())

Si en el ejemplo anterior intentsemos aadir un enum, o una funcin el compilador


nos alertara de que no se trata de un valor de referencia y por lo tanto no puede ser
aadido al array.

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
}
}

Sintxis del punto y la inferencia de tipos


Al usar tipos encadenados, podemos acceder a estos tipos usando notacin de punto accediendo a
travs 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 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:

Aadir nuevos atributos de instancia y de tipo computados.


Definir nuevos mtodos de instancia y de tipo.
Aadir nuevos constructores.
Definir subscripts.
Definir y usar nuevos tipos encadenados.
Conformar un protocolo.

La forma de definir una extensin es la siguiente:


1
2
3

extension MiTipo {
// Nueva funcionalidad a aadir
}

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


1
2
3

extension Tipo: Protocolo1, Protocolo2 {


// Implementa los requerimientos del protocolo aqui
}

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()

Mtodos de instancia mutables


Los mtodos de instancia pueden tambin ser mutables cuando los declaramos en extensiones.
Los mtodos de estructuras y enums pueden modificar a self a sus propiedades. En ese caso el mtodo
debe marcarse como mutating, de la misma forma en la que lo hacemos en la implementacin
original:
1
2
3
4
5
6

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

Aunque en el ejemplo hayamos especificado el subconjunto de entidades que tendra dicha


implementacin por defecto, tambin podemos especificarlo para todas las que conformen dicho
protocolo:
1
2
3
4
5

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

Las extensiones permiten aadir funcionalidades a elementos existentes.


Permiten aadir nuevos atributos computados (no almacenados).
Permiten definir nuevos constructores.
Permiten aadir nuevos mtodos.
Tambin permiten aadir nuevos subscripts y tambin encadenar tipos.
Los protocolos tambin pueden ser extendidos para ofrecer implementaciones por defectos en
el mismo. Esto es conocido en Swift 2.0 como extensin de protocolos.

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
}

Clases, estructuras, y enums especifican la conformacin de un protocolo de la siguiente forma:


1
2
3

struct MiStruct: Protocolo1, Protocolo2 {


// Definicin del struct
}

Pudiendo conformar ms de un protocolo simultneamente.

Requerimientos de mtodos y properties


A la hora dedefinir nuestro protocolo podemos especificar atributos y mtodos que cualquier entidad
conformando el protocolo deber implementar.
La forma de especificar los atributos es mediante var miVariable: Tipo { get set}. Desde
el 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 mtodos sera de la misma forma en la que definimos una funcin
slo que en este caso no definimos el cuerpo del mismo: func miFuncion() -> Bool

77

15 - Protocolos

1
2
3
4

78

protocol MiProtocolo {
var nombre: String { get }
func eliminar() -> Bool
}

Requerimientos de mutabilidad en los mtodos de un


protocolo
Si un mtodo especificado en un protocolo puede modificar la instancia que lo conforma, es decir,
mutarlo el mtodo debe ser de tipo mutating
1
2
3
4
5
6
7
8
9
10
11
12

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.

Conformar protocolos mediante extensiones


Un tipo existente puede conformar un protocolo haciendo uso de extensiones. Los requerimientos
del protocolo pueden ser implementados en dicha extension. Por ejemplo:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

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

protocol <ProtocoloA, ProtocoloB>

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")
}

Comprobar la conformacin de un protocolo


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

15 - Protocolos

81

El operador is retorna true si la entidad conforma el protocolo.


La versin as del operador de casting retorna un valor del tipo del protocolo. Si la entidad no
conformara dicho protocolo, retornara nil en su lugar.
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
36
37
38

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.

Funciones genricas: Parametros tipados


Las funciones genricas pueden ser usadas para acceder a cualquier tipo de dato como por ejemplo
Int o String.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

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


let temp = a
a = b
b = temp
}
var num1 = 100
var num2 = 200
var str1 = "ola"
var str2 = "kase"
print("Antes de cambiarlos: \(num1) & \(num2)") // 100 & 200
cambiar(&num1, &num2)
print("Despus de cambiarlos: \(num1) & \(num2)") // 200 & 100
print("Antes de cambiarlos: \(str1) \(str2)") // ola kase
cambiar(&str1, &str2)
print("Despus de cambiarlos: \(str1) \(str2)") // kase ola

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)
}
}

La estructura Collecion en el ejemplo mostrado es genrico de tipo T. Al ser un tipo genrico el


valor de T puede ser usado internamente, como por ejemplo para indicar un array de elementos de
la coleccin o para especificar que tipo de elementos pueden ser aadidos a la coleccin.

Extendiendo un tipo genrico


A la hora de extender un tipo genrico tambin podemos hacer uso del valor genrico. En el caso
del ejemplo anterior:
1
2
3
4
5

extension Coleccion {
var primer: T? {
return elementos.firstObject
}
}

Donde T es visible en la extensin y podemos utilizarlo para aadir nuevas funcionalidades.

Constraints para tipos


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

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


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

Donde T es el tipo genrico y StringLiteralConvertible el subconjunto de valores genricos que


pueden ser admitidos. Ese subconjunto puede ser especificado mediante una clase o un protocolo al
cual deben conformar.

Tipos asociados (Protocolos Genricos)


En el caso de querer definir protocolos genricos, Swift ofrece el concepto de tipos asociados. A la
hora de definir el carcter genrico de un protocolo se especifica mediante typealias en el protocolo
cuyo tipo es especificado cuando el protocolo es conformado.

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)
}

mutating func pop() -> T {


return elementos.removeLast()
}
var total: Int {
return elementos.count
}
subscript(i: Int) -> T {
return elementos[i]
}
}

En el ejemplo anterior estamos definientdo en Contenedor (protocolo) con un typealis TipoElemento.


Este protocolo es conformado por Colleccion. Swift automticamente tratar de inferior el valor de
TipoElemento cuando el protocolo es conformado. En el caso de no poder inferir el tipo, el valor de
TipoElemento 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 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

private var nombre: String = "Pedro"


private class Cancion {}
internal func caminar() {}
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 padre
determina el nivel de restriccin en las hijas. Por ejemplo, si una clase definiera su nivel
de acceso como private e internamente tuviera un atributo como public el compilador
alertara sobre el conflicto (sin llegar a bloquear la compilacin).

Control de acceso en funciones


La forma de especificar el control de acceso en funciones es aadiendo el tipo de acceso al comienzo
de la definicin de la funcin como se muestra en el ejemplo inferior:
87

17 - Control de acceso

1
2
3

88

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


return a + b
}

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

private class Tarjeta {}


public func guardar(tarjeta: Tarjeta) {
print("Guardando tarjeta: \(tarjeta)")
}

Control de acceso en Enums


A la hora de definir el control de acceso de un enum este se especifica en la declaracin del propio
enum. No es posible especificar un nivel de acceso para cada una de las sentencias del enum y todas
ellas heredan el nivel de acceso del enum:
1
2
3
4
5

public
case
case
case
}

enum Error {
HTTPError
StoreError
Unknown

Control de acceso en subclases


Swift permite heredar clases que son visibles en el contexto donde se est heredando. La subclase
nunca puede tener un nivel de acceso menos restrictivo que la clase de la que heredan.
1
2
3
4
5
6
7
8
9
10
11

public class Cricket {


private func comenzar() {
print("Bienvenido a Cricket")
}
}
internal class Tennis: Cricket {
override internal func comenzar() {
print("Bienvenido a Tennis")
}
}

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.

Control de acceso en constantes, variables, properties


y subscripts
Constantes, variables, o properties slo pueden ser definidas con un nivel de acceso menos restrictivo
que el de su tipo. Por ejemplo:
1
2

private class MiClasePrivada {}


public static let miClase: MiClasePrivada = MiClasePrivada()

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

public class Ejemplo {


private var contador: Int = 0 {
willSet(nuevoValor) {
print("El nuevo valor es \(nuevoValor)")
}
didSet {
if contador > oldValue {
print("Se han aadido \(contador - oldValue)")
}
}
}
}
let ejemplo = Ejemplo()
ejemplo.contador = 100
ejemplo.contador = 400
//
//
//
//

El
Se
El
Se

nuevo valor
han aadido
nuevo valor
han aadido

es 100
100
es 400
300

17 - Control de acceso

90

Control de acceso en constructores y constructores


por defecto
A la hora de definir un constructor, este debe de tener un nivel de acceso igual o inferior al de la
entidad que est inicializando. El nico caso en el que el nivel de acceso tiene que ser el mismo que
el de la clase es en el caso de required initializers.
Los tipos de los parmetros de los constructores tienen que tener un nivel igual o menos restrictivo
que el de la clase que estn inicializando:
Los constructores por defecto tienen el mismo nivel de acceso del tipo de acceso que el tipo que
estn inicializando, a menos que el tipo que estn inicializando sea public. En este caso el constructor
por defecto ser internal. Si quisiramos tener un constructor pblico para ser usado desde otro
mdulo, este debera definirse explicitamente como parte de la definicin del tipo.
1
2
3
4
5
6

// 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

public class ClassA {


public init() {}
}

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 prtocolo como public que
hereda de un internal protocolo:

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

public protocol ProtocoloTCP {


init(no1: Int)
}
public class ClasePrincipal {
var no1: Int
init(no1: Int) {
self.no1 = no1
}
}
class Subclase: ClasePrincipal, ProtocoloTCP {
var no2: Int
init(no1: Int, no2: Int) {
self.no2 = no2
super.init(no1: no1)
}
required override convenience init(no1: Int) {
self.init(no1: no1, no2: 0)
}
}
let clase1 = ClasePrincipal(no1: 20)
let clase2 = Subclase(no1: 30, no2: 50)

Definimos un protocolo: ProtocoloTCP


Subclase hereda de ClasePrincipal y adems 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 inicializacin.


Define un segundo constructor que required override convenience init(no1: Int):
Sobreescribe el constructor de la clase padre, por eso es necesario especificarlo con
override.
Es un convenience initializer ya que usa el otro constructor para el proceso de inicializacin.

17 - Control de acceso

92

Control de acceso en extensiones


Swift no permite definir el nivel de acceso de una extensin cuando el usuario la usa para especificar
que la entidad conforma un nuevo protocolo. El nivel de acceso para cada requerimiento del
protocolo viene determinado por el propio nivel de acceso del protocolo:
1
2
3
4
5
6
7
8
9
10
11

public protocol PuedoVolar {


func vuela()
}
private class Avion {}
extension Avion: PuedoVolar {
public func vuela() {
print("Estoy volando")
}
}

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

Você também pode gostar