Manual Basico de Swift 3

Descargar como pdf o txt
Descargar como pdf o txt
Está en la página 1de 29

Manual básico de Swift 3

Autor: Rodrigo Paszniuk


Basado en la serie de artículos publicados en https://www.programacion.com.py/
Introducción a Swift 3
Swift es un lenguaje de programación multiparadigma creado por Apple enfocado en el
desarrollo de aplicaciones para iOS y Mac OS X. Fue presentado en WWDC 2014 y está
diseñado para integrarse con los Frameworks Cocoa y Cocoa Touch, puede usar cualquier
biblioteca programada en Objective-C y llamar a funciones de C. También es posible
desarrollar código en Swift compatible con Objective-C bajo ciertas condiciones. Swift
tiene la intención de ser un lenguaje seguro, de desarrollo rápido y conciso. Usa el
compilador LLVM incluido en Xcode. En el año 2015 pasó a ser de código abierto.

Swift es un lenguaje fuertemente tipado, aunque su declaración no siempre es necesaria


gracias a su capacidad de inferir tipos. Los tipos de datos se dividen principalmente en dos
grupos. Los Tipos de valor,y los Tipos por referencia, se diferencian principalmente en
como son asignados.

 Al asignar un Tipo de valor se guarda una copia de su contenido. Se recomienda


su uso cuando se requiere copiar su información o se vaya a trabajar en múltiples
hilos.

 Al asignar un Tipo por referencia se asigna una instancia compartida que es


mutable aún si son usadas en constantes, es decir modificar una instancia se verá
reflejado en todas las variables y constantes que la compartan. Se recomienda su
uso cuando se requiera compartir datos mutables.

¿Cómo programo en Swift?


Para usuarios con Mac: Utilizar Xcode.
Al abrir Xcode aparecerá una ventana de bienvenida. En ella habrá diversos botones, entre
ellos encontramos uno que nos permite empezar con un Playground de Swift de forma
rápida, es el botón con el título Get started with a playground. Una vez pulsado nos
pedirá que demos un nombre a nuestro fichero de Playground y seleccionar una de las
plataformas para nuestro Playground: iOS, OSX o TVOS. Tras esto se nos consultará
dónde guardar el fichero de nuestro Playground. Con esto Xcode ya puede crear el
entorno de desarrollo para que empecemos a jugar con nuestro Playground de Swift.
Xcode presenta en su interfaz un area de menú con una barra de herramientas con las
principales opciones de ejecución, depuración y visualización, y un area de trabajo.
Para usuarios con Linux o Windows: Utilizar IBM Swift Sanbox (utilizando tu
navegador web).

Hola Mundo con Swift


import UIKit

var str = "Hola Mundo"

print(str)
Tras ejecutar el código veremos que el texto Hola mundo aparece en el área de
depuración. ¡Felicidades, has creado tu primer programa en Swift!

Variables, constantes, inferencia


y colecciones en Swift 3

Variables y constantes
Una variable es un contenedor cuyo valor puede variar en el transcurso de la ejecución de
nuestro programa. Una constante, por el contrario, es un valor que permanecerá
inalterable (o inmutable) durante el mismo. Ambos tipos forman parte de lo que se
llaman propiedades.
Las variables se definen con la palabra clave var antecediendo al nombre de la misma y
las constantes con la palabra let. Ejemplo:

var saludo = "Hola"

var numero = 5

let cantidad = "20 unidades"

let pedido = 15

Podemos definir varias variables o constantes en una misma línea, separando cada
nombre y su valor por una coma.

var x = 0, y = 0.0, z = 0, ax = "Prueba", bx = "Otra prueba"

let a = 3.0, b = 2, c = 3, aa = "Testeo"

La notación correcta para definir valores es con un =. Los números se ponen tal cual y las
cadenas o valores alfanuméricos han de ir entre comillas dobles ” “. Si solo queremos
definir un carácter, podemos hacerlo poniendo el valor entre comillas simples ‘ ‘. El
separador de decimales siempre es el punto, por ejemplo, 3.1415.

Tipos de datos básicos


Las variables y constantes en Swift pueden ser de muchos tipos y complejidad en cuanto a
estructura, pero al final todos tendrán una base en los llamados tipos de datos
básicos, que nos permiten guardar los tipos de información más básicos que luego nos
permitirán crear otros más complejos.

 Int, número entero de 32 bits con un rango de entre -2.147.483.648 y


2,147,483,647.
 Double, número flotante de 64 bits con hasta 15 decimales de precisión.
 Float, número flotante de 32 bits con hasta 6 decimales de precisión.
 Bool, número booleano que puede ser 0 (false) o 1 (true).
 String, cadena de caracteres, tratada internamente con una matriz (array) de ellos.

Esto serían los tipos básicos que pueden ser usados a la hora de crear variables o
constantes. Podemos declararlos indicando los tipos, siempre y cuando queramos
tener claro qué contendrán o si queremos evitar que el compilador decida por
nosotros.

var mensaje: String = "Hola ¿qué tal?"

var pi: Double = 3.14159265

var verdad: Bool = true

let veces: Int = 320

Si nos fijamos en los ejemplos que dábamos al comienzo, estos no tenían puesto el tipo de
dato que íbamos a usar. Y esto es porque hemos usado la inferencia de datos: la
propiedad que tiene el compilador de determinar por nosotros cuál es el tipo de
dato a usar en una variable o constante en función de su contenido.
¿Cómo unir cadenas?

Podemos unir cadenas de estar forma:

var cadena = "hola"

var cadena2 = "que"

var cadena3 = cadena + " " + cadena2

//Tambien podemos interpolar

var numero = 10

var cadena4 = "Numero: \(numero)"

print(cadena4)

Array o matrices
Un array o matriz es una colección de elementos almacenados en una sucesión. Por
ejemplo, una serie de números, o una serie de cadenas. Tradicionalmente, cuando se
define un array de un tipo, este solo puede contener datos de ese tipo (números, cadenas,
objetos…), pero en Swift tenemos la posibilidad de crear estos con diferentes
tipos de datos.

var mCadenas = ["Uno","Dos","Tres","Cuatro"]

var mNumeros = [1,2,3,4]

let mCadenaTipificada:[String] = ["Cinco","Seis","Siete"]

Si queremos cambiar un valor de un array, solo hay que hacer una asignación teniendo
presente el índice.

mNumeros[3] = 7

A la hora de añadir valores a un array o incluso sustituir y añadir a la vez, tenemos varias
opciones interesantes. Para ello, vamos a usar el operador de agregación += que permite
añadir elementos a un array o sumar a una variable un valor u otra variable. En realidad,
este operador, suma el array que ya tenemos con uno nuevo, motivo por el que
aunque sea un solo valor, hemos de ponerlo entre corchetes.
Otras cosas útiles:

//Para conocer el número de elementos de un array

mCadenas.count

//Para conocer si el array esta vacio

mCadenas.isEmpty

Por último, podemos usar también rangos para acceder o sustituir valores. Por ejemplo,
imaginar que queremos cambiar los valores segundo y tercero a la vez (las posiciones 1 y
2). Podemos usar los rangos, un número de inicio y un número de fin separados por 2
puntos y un símbolo menor o 3 puntos. Estos rangos nos permiten indicar que
queremos acceder desde el primer valor del rango hasta el anterior al último o
hasta el último inclusive. El rango ..< indica desde el valor de inicio hasta el final sin
incluir este y … el rango completo. Veámoslo en ejemplos:

var m = ["Mad Men", "Better Call Saul", "Talking Dead", "Humans"]

m += ["Preacher"]
m += ["The Killing"]

// Sustituimos el rango que va del 1 al 2 con dos nuevos valores

m[1...2] = ["Prison Break", "Breaking Bad"]

// Añadimos dos elementos nuevos

m += ["Lost", "Game Of Thrones"]

// Y ahora cambiamos esos dos últimos elementos

m[4..<8] = ["TWD", "FTWD", m[7], m[6]]

print(m)

Es importante notar que cualquier variable queda modificada a la izquierda


del = siendo todo lo que hay a la derecha el valor antes de ser modificado. Por
eso, las posiciones 7 y 6 son las anteriores a querer cambiar o si hacemos a = a + 1,
siendo a igual 2, a la derecha del igual a sigue siendo 2 y a la izquierda, será 3.
Los rangos se usan también para la enumeración de arrays a través de bucles (recorrer
todos sus elementos o parte).
En contra de los tipos básicos, los arrays SÍ pueden definirse vacíos. Realmente, no están
vacíos pues se inicializan con la estructura, y por eso realmente no están vacíos y
podemos crearlos en este estado. La forma de definirlos es usando el doble paréntesis de
instancia vacía () y (muy importante) tipificando el dato, por
ejemplo, var arrayVacio = [String](). También podemos hacerlo indicando el tipo de dato
en formato de primitiva del lenguaje como var arrayVacio = Array<String>()

Diccionarios
Los diccionarios nos permita acceder a sus valores, con los diccionarios nosotros
asignamos el valor por el que podremos acceder a los mismos. El contenido de
cada posición del diccionario puede ser del tipo que queramos, bien datos básicos, bien
datos complejos (como un array u otro diccionario) e incluso datos mucho más complejos
como objetos, imágenes o bloques de código.
En Swift, además, podemos usar cualquier tipo de dato básico también para el dato de
índice. Pero estos índices, como es lógico, han de ser únicos para cada valor de un
diccionario. No pueden usarse índices duplicados.
Tanto en los diccionarios, como en los array, podremos guardar casi cualquier tipo de
datos básico, complejo o del tipo que queramos siempre que pueda asignarse a una
variable, incluso instanciaciones de una clase (cosa que veremos más adelante, vayamos
poco a poco). En cuanto al índice del diccionario, solo pueden usarse tipos
básicos, conocidos técnicamente como hashable.
La forma de definir una pareja de clave y dato para un diccionario (que iría en una
posición del mismo) es usando el separador : para tipificar índice y valor.
Imaginemos que queremos guardar un diccionario con el número de series de AMC. En el
caso de TWD estableceríamos su clave y su valor con “TWD”:1. Puestos en una sucesión
separados por comas, definimos todos los elementos que necesitemos y podemos
asignarlos.

var series = ["TWD": 1, "FTWD": 2]

Como hemos inferido el tipo (no lo hemos indicado), hemos de tener en cuenta que el
compilador habrá creado un diccionario con el tipo [String:Int]. Si quisiéramos tipificar el
diccionario, forzando los tipos de datos, lo que haremos será usar la estructura que hemos
indicado: Dentro de los corchetes, el primer valor es el tipo usado para el índice,
seguido de dos puntos y el tipo de dato que vamos a guardar. No hace falta
indicar nada más pues [Clave:Valor] será interpretado por el compilador
como Dictionary<Key,Value>.

var series:[String:Int] = ["TWD": 1, "FTWD": 2]

La forma de modificar o añadir valores a un diccionario es parecida a los array aunque


indicando el valor del índice. Simplemente hacemos referencia a la clave que sustituir o
añadir y le asignamos un valor. Si existe, será sustituida y si no existe se añadirá al
diccionario. En el caso que queramos enumerar o recorrer todos los valores de un
diccionario (que veremos más adelante) los valores aparecerán por orden de inserción,
como si fuera un array.

// Cambiamos el valor de TWD por 3

series["TWD"] = 3

// Como Breaking Bad no existía, se añade con un valor de 0

series["Breaking Bad"] = 0

Al igual que los array, los diccionarios también pueden inicializarse vacíos y también hay
que tipificar los datos siempre, no puede usarse la inferencia. Solo hemos de crear la
variable, tipificarla y dejarla vacía, como por ejemplo var dicVacio = [String:Int]().
También puede indicarse una capacidad mínima del mismo para reservar la memoria a la
hora de inicializar. Por ejemplo: var dicVacio = [String:Int](minimumCapacity: 3). Esto
indica que nuestro diccionario se asignará con una capacidad mínima de 3 valores.

Mutabilidad e inmutabilidad
Para terminar este artículo, vamos a repasar un concepto para que lo podamos tener bien
claro: el concepto de la mutabilidad (variable que puede ser modificada) y la inmutabilidad
(que no puede ser modificada). Como ya dijimos al comienzo, los tipos de propiedades
pueden ser variables o constantes. Si declaramos un diccionario como constante
(con let), este será inmutable e intentar modificar un valor del mismo o añadir valores
dará errores de compilación. Igual con un array, cuyos valores podremos modificar, borrar
elementos o añadirlos a nuestros antojo si es variable (definido con var), pero no
podremos hacer nada si es constante.
Como es obvio, si definimos un diccionario como inmutable, hemos de cargar sus
valores en el momento de definirlo pues ya no podremos hacerlo después. Como
poder, podríamos definirlo vacío si queremos, pero así se quedaría durante todo el
programa sin posibilidad de hacer nada más con él.
Aquí es importante que aprendamos un primer concepto sobre variables por
valor o por referencia. Las clases, que son un tipo de dato objeto que veremos más
adelante, son valores por referencia. De forma que cuando la asignamos a un let lo que
tiene este es simplemente la dirección de memoria donde está dicha clase u objeto.
Obviamente, ese valor no podrá cambiarse pero el objeto sí es mutable. No hemos de
entender que una clase puede modificarse, porque lo que va en el let es solo la dirección
donde está: por eso podemos modificarlo.
Del mismo modo, hemos dicho que todas las variables han de estar inicializadas con un
valor, pero en el caso de los let podemos definirlos después.

let test:Int

test = 3

Para esto hemos de tipificar el tipo de dato y tener presente que no podremos
usar o acceder en modo alguno la constante hasta que esta tenga un valor, pero
podemos diferir su inicialización si nos conviene en un momento determinado.

Controles de flujo en Swift 3


En este artículo veremos las instrucciones de flujo de control como bucles o
condicionales.
If y switch
La forma de condicionar un flujo de ejecución, para hacer que nuestro programa ejecute o
no determinadas partes de código, se hace con las instrucciones if y switch. El primero es
una simple condición (si x es igual a y, haz z) y el segundo es lo que se denomina una
sentencia case, o lo que es lo mismo, un listado de posibles, con un conjunto de
instrucciones en caso de cumplirse una de estas condiciones (dependiendo del valor de x,
si es 1 haz esto, si es 2 esto otro, si es 3 lo de más allá. Si no es ninguno de estos,
entonces nada). Ej:

var m = ["Mad Men", "Better Call Saul", "Talking Dead", "Humans"]

if m.count > 3{

print("Hay más de 3 series")

else{

print("Hay 3 series o menos")

Si nos fijamos, if solo acepta una condición o su contraria, pero ¿qué pasa si queremos
hacer diferentes cosas en función de diferentes posibles condiciones para una misma
variable? Para eso usamos switch.

let indice = Int(arc4random()) % m.count

let serie = m[indice]

switch serie {

case "Mad Men":

print ("Él Mad Men")


case "Better Call Saul":

print ("Es Better Call Saul")

default:

print ("No es ninguno de estos")

Obs: arc4random() es una función que devuelve un número aleatorio, el cual podemos
acotar con el operador %. Pero esta función (que pertenece a C) devuelve un
tipo UInt32 el cual hay que convertir a un Int de Swift, por eso va acotado
con Int(arc4random()) (lo que se llama una operación de casting o conversión de tipos).
Luego le añadimos el operador % y el número en que queremos acotarlo. De esta forma
obtendremos un número aleatorio entre 0 y el número que digamos.
También podemos utilizar Switch con rangos o bien utilizando patrones:

var numero = 5

switch numero {

case 1...3:

print ("1 al 3")

case 4...5:

print ("4 al 5")

default:

print ("No encontrado")


}

let nombre = "Rodrigo Alejandro"

switch nombre {

case "Rodrigo":

print ("Te llamas Rodrigo")

case let x where x.hasSuffix("Alejandro"):

print ("Encontramos a Rodrigo")

default:

print ("No encontrado")

For in, repeat while


Siguiente paso: bucles. Un bucle es un trozo de código que se repite n veces. Puede
servir para recorrer todos los valores de un array (lo que llamamos enumeración de
un array) o para ejecutar ininterrumpidamente un trozo de código hasta que dentro de
este se cumpla una condición determinada o porque queremos hacer algo repetitivo un
número determinado de veces. Las veces pueden conocerse de antemano o establecerse
según valores que solo se conocen en tiempo de ejecución del programa.

//Forma normal

for s in m {

print (s)
}

//Al revez

for s in m.reversed() {

print (s)

//Otro ejemplo

for num in 1...10 {

print("Número \(num)")

//Ejemplo con indice

for (index, data) in m.enumerated() {

print("Dato \(data) - Indice \(index)")

Lo que hace for in es simple: a la variable que sigue a la palabra clave for se le
asigna en cada recorrido del bucle un valor que comprende los rangos, valores
o recorridos que se indiquen tras la palabra in.
El caso de repeat while es muy diferente. Es un ciclo que se repite indefinidamente
hasta que, al final del mismo, se cumpla la condición que indiquemos, suponiendo el fin de
dicho ciclo y la salida de esas llaves.

var valor:Int?
let acierto = Int(arc4random()) % 10

print ("Buscando el número \(acierto)")

repeat {

valor = Int(arc4random()) % 20

if acierto != valor {

print ("No he acertado. Ha salido el número \(valor!)")

} while acierto != valor

print("Enhorabuena. Salió el \(valor!)")

//While Normal

var numero = 1

while numero < 5{


print(numero)

numero += 1

En este pequeño ejemplo, elegimos un valor aleatorio del 0 al 10 y luego hacemos un


bucle repeatwhile para repetir la elección de otro valor aleatorio de igual rango, que se
repite hasta que vuelve a salir el mismo número. Como vemos el repeat establece la
evaluación de diferencia != por lo que se repite lo que hay entre las llaves que
contienen repeat while, mientras acierto sea diferente de valor.
Obs: Swift no admite valores vacíos de inicialización, pero sí podemos inicializar una
variable sin saber qué valor tendrá en ese momento, si no que se establecerá
en el futuro. Eso se hace con un ? tras el tipo de variable.

Funciones en Swift 3
Una función (método o procedimiento, como prefiramos llamarlo) es un trozo de código
con entidad propia que recibe o puede recibir unos valores y que devolverá o
puede devolver un resultado. Su cometido es simple: permitir ejecutar una serie de
instrucciones en nuestro código, de manera repetida, sin tener que poner el mismo código
cada vez.
Las funciones se declaran siempre con la palabra clave func seguida del nombre que
queramos darle. Luego, imprescindible, paréntesis. Si la función no recibe parámetros de
entrada (valores con los que trabajará) ponemos unos de apertura y cierre seguidos () y si
no, separados por coma ponemos los parámetros y el tipo de los mismos. Las funciones
no tienen inferencia de tipos y siempre hemos de indicar cuál es el de cada
variable de entrada.
Tras los parámetros de entrada, si no vamos a tener parámetros de salida que
devolveremos con la función, directamente abrimos corchete y empezamos a escribir el
cuerpo de la misma. En caso de querer usar de salida, usamos los símbolos -> e
indicamos el tipo o tipos de variables que vamos a devolver. Podemos no usar nombre
en cuyo caso se podrá acceder a ellas por índice o ponerles nombre para que
sean más fáciles de identificar. Ejemplos:

// Sin parámetros de entrada ni salida

func saludo(){
print("Hola")

// Recibe dos parámetros a y b, ambos de tipo Int

func sumar(a:Int, b:Int) -> Int{

return a + b;

// Recibe los mismos dos y devuelve otros dos de tipo Int y String

func tipoDispositivo(width:Float, height:Float) -> (Int, String) {

return (3, "i")

// Recibe los mismos dos y devuelve

// otros dos de tipo Int y String con nombre

func tipoDispositivoLabels(width:Float, height:Float) -> (valor: Int, cadena: St


ring) {

return (3, "i")

saludo()
print(sumar(a: 1, b : 2))

print(tipoDispositivo(width: 1, height: 1))

print(tipoDispositivoLabels(width: 10.1, height: 15.1))

Podemos usar tantos parámetros de entrada como queramos. A la hora de invocarlos, no


obstante, hemos de recordar que (salvo cambio por nuestra parte) el nombre del
parámetro siempre se infiere (no hay que indicar, solo el valor) y a partir del
segundo siempre habrá que indicarlo. Esto se debe a que el lenguaje que el primer
parámetro tiene que tener su significado contextualizado en el nombre de la propia
función.
Es muy importante recordar que el compilador en tiempo real de Swift nos prevendrá de
usar funciones con parámetros de salida donde no usemos la palabra clave return seguido
de lo que vamos a devolver. Si el código de la función, en alguno de sus flujos, no
finaliza con un return el compilador nos dará error.

Polimorfismo
Vamos a probar el polimorfismo. Esto no es más que la capacidad de una función de
ser definida de diferentes formas y tener diferentes comportamientos en
función de cómo es invocada.

func sumar(c1:Int, c2:Int) -> Int {

return c1 + c2

Pero, ¿y si queremos poder usar también con más valores? Pues creamos otra
definición con el mismo nombre y otros parámetros.

func sumar(c1:Int, c2:Int, c3:Int, c4:Int) -> Int {

return c1 + c2 + c3 + c4;

print(sumar(c1 : 1, c2: 2));


print(sumar(c1 : 1, c2: 2, c3: 3, c4: 4));

De esta forma podemos usar uno u otro indistintamente y cuando escribamos el


asistente de código nos mostrará ambas definiciones para que elijamos aquella que nos
resulte más práctica en ese momento.

Valores por defecto


También podemos usar valores por defecto en las funciones para pre-asignar parámetros
en caso que no se indique un valor para estos en la llamada. De esta forma, podremos
indicar o no (según el caso) un parámetro en función de nuestro interés.
Vamos a suponer que queremos unir cadenas separadas por un espacio pero,
opcionalmente, puede que queramos usar otros caracteres como un guión o
un hashtag. De igual forma, puede que queramos que queramos unir dos cadenas, o
puede que queramos dos o cuatro o diez. Vamos a crear entonces una función que reciba
un array de cadenas y un valor opcional de separador:

func unirCadenas(separador:Character = " ", cadenas:[String]) -> String {

var union:String = ""

for cadena in cadenas {

union += cadena

union += String(separador)

return union

Con esta función, si indicamos el separador usará el de nuestra elección, pero si


no, usará un espacio. Ahora solo tenemos que llamar a nuestra función de una de estas
dos maneras:

let t1 = unirCadenas(separador: "-", cadenas: ["valor1", "valor2", "valor3"])


let t2 = unirCadenas(cadenas:["valor1", "valor2", "valor3"])

Claramente vemos que si no incluimos el valor para separador, no importa pues el


sistema cogerá el que hemos asignado por defecto. Eso sí, para evitar errores
tenemos que indicar que el parámetro que estamos enviando es cadenas y no separador,
que además, al ser el primer parámetro, va intrínseco.

Clases en Swift 3
Clases y herencia
Una clase es una plantilla para la creación de objetos de datos según un modelo
predefinido. Las clases son un pilar fundamental de la programación orientada a objetos.
Permiten abstraer los datos y sus operaciones asociadas al modo de una caja negra. Los
lenguajes de programación que soportan clases difieren sutilmente en su soporte para
diversas características relacionadas con clases. La mayoría soportan diversas formas de
herencia. Muchos lenguajes también soportan características para proporcionar
encapsulación, como especificadores de acceso.

class ClaseEjemplo {

var atributo: Int = 0

class ClaseEjemplo2 : ClaseEjemplo{

Hemos definido una clase llamada ClaseEjemplo y otra clase llamada ClaseEjemplo2 que
hereda de ClaseEjemplo.

Clases y structs
Una clase y un struct son muy similares, de hecho, en Swift son casi iguales. Su única
diferencia es que mientras una actúa en paso por referencia (la clase) la otra se
copia a sí misma en cada nueva asignación (el struct). Los structs, por otro lado,
permiten declarar una estructura de datos que no requiere inicializar ninguna de sus
variables.
Vamos a crear un struct que represente los valores de fuerza y vida como valores vitales y
lo sustituimos en nuestra clase Persona.

struct valoresVitales {

var fuerza: Int

var vida: Int

Ahora sustituimos en la clase los valores y, ahora sí, inicializamos la clase:

class Persona {

let nombre: String

let vital: valoresVitales

init(nombre: String, vital: valoresVitales) {

self.nombre = nombre

self.vital = vital

Debido a cómo Swift maneja las variables y las constantes en objetos, cuando definimos
una instancia de una clase como constante (en let) podremos cambiar las
propiedades de esta sin problema, aunque no podremos re-asignar la constante
a una nueva clase.
Sin embargo, un struct constante no permite alterar las propiedades de la misma ni
tampoco, como pasa con las clases, re-asignar dicha constante a un nuevo struct.
Como vemos, la inicialización de una clase se basa en la palabra clave init, donde
pedimos aquellos parámetros que consideremos necesarios para la
inicialización. Si tras crear nuestro inicializador, alguna propiedad de la clase no quedara
asignada, nos daría error, pues recordemos que en Swift todo ha de ser inicializado
con valor en el momento de su creación, incluso las opcionales que llevan un valor
vacío y wrapeado que se asigna en el momento de la creación del mismo opcional.

Inicializadores de conveniencia
Ya hemos creado nuestra clase Persona, y para inicializarla tenemos que pasarle el
nombre del mismo como parámetro, pero también tenemos pasarle la iniciación de
un struct que corresponda con sus valores vitales.

var vital = valoresVitales(fuerza: 30,vida: 50)

let Hombre = Persona (nombre: "Shagrat", vital: vital)

Esto puede hacerse más simplemente, usando un concepto llamado


inicializador de conveniencia. Podemos crear nuevos tipos de inicializadores que nos
permitan recibir otros parámetros diferentes que los que la clase requiere de por sí, y que
nos permitan construir más cómodamente la instancia.

convenience init (name:String, fuerza:Int, vida:Int) {


let vital = valoresVitales(fuerza: fuerza, vida: vida)
self.init (nombre: name, vital: vital)
}

El concepto es simple: creamos inicializadores alternativos que invoquen al init


principal, pero pudiendo pre-procesar los datos. En nuestro caso, creando
el struct desde los dos valores Int aislados. De esta forma, cuando queramos crear una
nueva clase Persona como la creada anteriormente, solo tendremos que hacer una única
llamada:

let Hombre = Persona (name: "XXX", fuerza: 30, vida: 50)

Herencia extendida
Ejemplo de cómo crear una nueva clase que extienda por herencia la funcionalidad de una
clase padre:
class Animal {

var patas: Int

init(patas: Int) {

self.patas = patas

class Perro: Animal {

let habilidad: String

init(patas: Int, habilidad: String) {

self.habilidad = habilidad

super.init(patas: patas)

let perro = Perro(patas: 4, habilidad: "Ladrar")

print(perro.patas)
Con eso, estaremos creando una clase Perro que hereda de Animal pero extiende sus
propiedades añadiendo un nuevo valor habilidad.

Tuplas y Enumeraciones en
Swift 3
Tuplas
Las tuplas es un tipo de dato compuesto que es una de las grandes novedades que
incorporó Swift, y cuyo mayor valor no es el tipo en sí, sino que está muy
integrado en el sistema y por lo tanto podemos sacarle un gran rendimiento.
Ejemplo: Imaginemos que queremos declarar varias variables a la vez y darles un
valor, como por ejemplo, un alto y un ancho. Normalmente haríamos:

var alto = 2

var ancho = 2

Con ayuda de las tuplas, podemos reducir el código así:

var (alto, ancho) = (2, 2)

print(alto)

print(ancho)

Podemos enumerar tanto arrays como diccionarios a través de tuplas, pudiendo acceder
a cada elemento de manera individual a través del índice.

Enumeraciones
Son un conjunto de datos de un mismo tipo que agrupa valores que se
relacionan entre sí. Normalmente se usan para acotar posibles características de una
propiedad, donde uno de los valores de la enumeración es asignado a una variable
tipificada en esta.
Por ejemplo, si tenemos una propiedad de la clase vehículo que es puertas, es fácil saber
qué valores vamos a poner: 1, 2, 3 o 4. Podemos usar un entero y poner los valores.
Pero, ¿y sí queremos que estos sean los únicos posibles valores de nuestro
campo? Usamos las enumeraciones.

enum puertas {
case una, dos, tres, cuatro
}

Las enumeraciones tienen dos tipos de valores: la descripción y el valor interno o


valor hash. Nosotros siempre trabajaremos con la descripción, pero
internamente el sistema les asigna un valor y es como reconoce cada
correspondencia.
Ejemplo de cómo utilizarlo:

puertas.una
puertas.una.hashValue

La primera instrucción nos devolverá la descripción y la llamada a hashValuenos devolverá


lo que es el índice interno que ha autoasignado nuestra enumeración.
También podemos asignarle un valor a cada opción de nuestro enum. Este solo puede
usarse si tipificamos el enum a un tipo de valor, como un String, un Float,
un Int o un Character. Estos son los únicos tipos soportados,:

enum puertas:Int {
case una = 1, dos = 2, tres = 3, cuatro = 4
}

puertas.dos.hashValue
puertas.dos.rawValue

A nivel de definición podemos separar por comas con una sola instrucción case los
diferentes valores o podemos poner uno en cada línea antecedido de case. Es
una opción a escoger según nuestras necesidades estéticas de código o cómo queramos
que se entienda.

enum puertas:Int {
case una = 1
case dos
case tres
case cuatro
}

La forma de usar estas enumeraciones es asignarlas como tipo de dato a una variable.

var numPuertas:puertas

numPuertas = .una

switch numPuertas {

case .una: print("Uno")

case .dos: print("Dos")

case .tres: print("Tres")

case .cuatro: print("Cuatro")

La enumeración permite controlar diferentes tipos de valores acotados y es muy práctico


cuando queremos controlar algún tipo de algo de una forma que sepamos, más
semánticamente, en qué se distinguen los diferentes tipos.

Estructuras y Protocolos en
Swift 3
Estructuras
Datos por valor o por referencia
Un struct es básicamente, una estructura de datos o al menos ese es su objetivo o
representación. Pero vista desde un punto de vista más amplio a nivel de Swift podemos
pensar que un struct es un realidad una clase sin herencia y cuyo principal objetivo es
crear una estructura de código que pueda ser representada a partir de un tipo
de dato por valor y no por referencia.
Mientras los tipos de dato por referencia suponen la base de la orientación a objetos,
donde una variable o constante no contiene el dato en sí, si no la dirección de memoria (o
referencia) donde esté el objeto almacenado, un struct es un dato por valor donde el
dato en sí está almacenado en la variable o constante directamente. Es
básicamente la diferencia entre apuntar el cajón donde guardamos las cosas en una lista
donde en cada línea enumeramos todos los cajones de un inventario y lo que contienen
(por referencia) o a ponerle una etiqueta directamente al cajón para saber qué hay dentro
(por valor).
A efectos de programación, asignar una variable con un dato por referencia a otra (un
objeto creado a partir de una clase), hace que ambas variables apunten y hablen del
mismo objeto. Pero en un struct esto no es así: al igual que un array o cualquier tipo de
colección de Swift, dato numérico o cadena, cuando se asigna una variable con
un struct a otra, en realidad estamos haciendo una copia idéntica que, a partir del
momento de la copia, se convierten en dos estructuras de datos completamente
independientes y sin relación alguna, salvo en su definición base.

Struct, inicializadores por defecto


Cuando creamos un struct no necesitamos crear un inicializador si no
simplemente decir qué datos queremos almacenar en el mismo como
propiedades o qué métodos queremos que le acompañen. Nada más. Imaginemos
una estructura para un auto.

struct Auto {

var ruedas: Int

var puertas: Int

var color: String

Cuando creamos una nueva variable y decimos que sea un tipo struct Auto, al escribir el
primer paréntesis el sistema ya nos permite elegir en su ayuda en línea la creación del
constructor por defecto donde vamos a darle valor a todas y cada una de las
propiedades no opcionales del mismo.

let auto1 = Auto(ruedas: 4, puertas: 4, color: "Rojo")


auto1.ruedas

Podemos copiar auto en una nueva variable auto2, y podremos modificar los valores
de las propiedades sin problema alguno.

var auto2 = auto1

auto2.ruedas = 2

auto2.ruedas

Y esto, en esencia, son los structs. Una estructura de datos similar a una clase, pero
que como hemos dicho sin herencia y en datos por valor. Los structs, igualmente,
permiten usar protocolos de forma que podemos construir especificaciones que queramos
que encajen con este (se conformen) y ahí es donde está la clave para la nueva
orientación a protocolos que incorpora Swift 2. A partir de implementaciones por
defecto y de la herencia que sí permiten los protocolos, nos permite construir un
nuevo tipo de abstracción basado en datos por valor que, al final, resultan más eficientes
a nivel de memoria para algunos casos en que trabajemos con Swift.

Protocolos
¿Qué es un protocolo? Es una plantilla a usar cuando queremos tener una especificación
común a una serie de clases, structs o enumeraciones. Un protocolo nos permite
indicar las cabeceras de funciones o propiedades que queramos se incluyan de
manera obligada para cumplir con una especificación que, posteriormente, tendrá
una implementación asociada (un código que le de funcionalidad).
Usamos la palabra clave protocol y luego podemos incluir funciones, indicando solo la
cabecera que corresponde, o propiedades donde no podemos usar almacenadas si no solo
propiedades calculadas que incluyan métodos get o set. En el caso expuesto solo
usamos get, pero si tuviéramos la ocasión de usar también el set, simplemente
ponemos { get set }. Ejemplo:
protocol Mortal {
var muerto: Bool { get }
func muerte()
}
class Pj {
var nombre: String
var vida: Int
init(nombre:String, vida:Int) {
self.nombre = nombre
self.vida = vida
}
}
class Villano: Pj, Mortal {
var muerto:Bool {
return vida <= 0
}

func muerte() {
print(“Muerteeeeeee”)
}
}
Cuando creamos la nueva clase, y especificamos que estamos creando una subclase de Pj,
seguido con coma ponemos el protocolo y eso nos obliga a incluir aquello que hemos
hecho que forme parte del protocolo. Con esto, conseguimos lo que se denomina
conformarse a un protocolo. Si quitáramos cualquiera de los dos componentes del
protocolo, veríamos que obtenemos un error inmediato porque los protocolos en Swift
son obligatorios.

También podría gustarte